diff options
435 files changed, 9028 insertions, 7064 deletions
diff --git a/.editorconfig b/.editorconfig index a04cb9054c..258d41ab48 100644 --- a/.editorconfig +++ b/.editorconfig @@ -55,7 +55,7 @@ indent_size = 4 emacs_mode = perl # but user kernel "style" for imported scripts -[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +[scripts/{get_maintainer.pl,checkpatch.pl}] indent_style = tab indent_size = 8 emacs_mode = perl diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 45ed0c96fe..beac39e5bd 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -77,7 +77,7 @@ msys2-64bit: git grep make sed mingw-w64-x86_64-binutils mingw-w64-x86_64-ccache - mingw-w64-x86_64-curl + mingw-w64-x86_64-curl-winssl mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 mingw-w64-x86_64-libnfs diff --git a/MAINTAINERS b/MAINTAINERS index a07086ed76..0207946537 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -146,6 +146,8 @@ F: target/i386/*.[ch] F: target/i386/Kconfig F: target/i386/meson.build F: tools/i386/ +F: tests/functional/i386/ +F: tests/functional/x86_64/ Guest CPU cores (TCG) --------------------- @@ -189,6 +191,7 @@ M: Richard Henderson <richard.henderson@linaro.org> S: Maintained F: target/alpha/ F: tests/tcg/alpha/ +F: tests/functional/alpha/ F: disas/alpha.c ARM TCG CPUs @@ -212,7 +215,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/functional/test_aarch64_smmu.py +F: tests/functional/aarch64/test_smmu.py AVR TCG CPUs M: Michael Rolnik <mrolnik@gmail.com> @@ -220,7 +223,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/functional/test_avr_*.py +F: tests/functional/avr/ Hexagon TCG CPUs M: Brian Cain <brian.cain@oss.qualcomm.com> @@ -256,7 +259,7 @@ M: Song Gao <gaosong@loongson.cn> S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ -F: tests/functional/test_loongarch64_virt.py +F: tests/functional/loongarch64/test_virt.py M68K TCG CPUs M: Laurent Vivier <laurent@vivier.eu> @@ -308,7 +311,7 @@ F: configs/devices/ppc* F: docs/system/ppc/embedded.rst F: docs/system/target-ppc.rst F: tests/tcg/ppc*/* -F: tests/functional/test_ppc_74xx.py +F: tests/functional/ppc/test_74xx.py RISC-V TCG CPUs M: Palmer Dabbelt <palmer@dabbelt.com> @@ -330,7 +333,8 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ F: common-user/host/riscv* -F: tests/functional/test_riscv* +F: tests/functional/riscv32 +F: tests/functional/riscv64 F: tests/tcg/riscv64/ RISC-V XThead* extensions @@ -443,6 +447,7 @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Maintained F: target/arm/kvm.c +F: tests/functional/aarch64/test_kvm.py MIPS KVM CPUs M: Huacai Chen <chenhuacai@kernel.org> @@ -479,7 +484,7 @@ F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap -F: tests/functional/test_x86_64_hotplug_cpu.py +F: tests/functional/x86_64/test_hotplug_cpu.py Xen emulation on X86 KVM CPUs M: David Woodhouse <dwmw2@infradead.org> @@ -488,7 +493,7 @@ S: Supported F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/functional/test_x86_64_kvm_xen.py +F: tests/functional/x86_64/test_kvm_xen.py Guest CPU Cores (other accelerators) ------------------------------------ @@ -656,7 +661,7 @@ S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c F: tests/tcg/alpha/system/ -F: tests/functional/test_alpha_clipper.py +F: tests/functional/alpha/test_clipper.py ARM Machines ------------ @@ -672,7 +677,7 @@ F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst F: hw/misc/axp209.c -F: tests/functional/test_arm_cubieboard.py +F: tests/functional/arm/test_cubieboard.py Allwinner-h3 M: Niek Linnenbank <nieklinnenbank@gmail.com> @@ -682,7 +687,7 @@ F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* F: hw/arm/orangepi.c F: docs/system/arm/orangepi.rst -F: tests/functional/test_arm_orangepi.py +F: tests/functional/arm/test_orangepi.py ARM PrimeCell and CMSDK devices M: Peter Maydell <peter.maydell@linaro.org> @@ -752,7 +757,7 @@ F: docs/system/arm/bananapi_m2u.rst F: hw/*/allwinner-r40*.c F: hw/arm/bananapi_m2u.c F: include/hw/*/allwinner-r40*.h -F: tests/functional/test_arm_bpim2u.py +F: tests/functional/arm/test_bpim2u.py B-L475E-IOT01A IoT Node M: Samuel Tardieu <sam@rfc1149.net> @@ -770,7 +775,7 @@ S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* F: docs/system/arm/exynos.rst -F: tests/functional/test_arm_smdkc210.py +F: tests/functional/arm/test_smdkc210.py Calxeda Highbank M: Rob Herring <robh@kernel.org> @@ -789,7 +794,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/functional/test_arm_canona1100.py +F: tests/functional/arm/test_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -832,7 +837,7 @@ S: Odd Fixes F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/functional/test_arm_integratorcp.py +F: tests/functional/arm/test_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -874,7 +879,7 @@ 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 -F: tests/functional/test_aarch64_imx8mp_evk.py +F: tests/functional/aarch64/test_imx8mp_evk.py F: tests/qtest/rs5c372-test.c MPS2 / MPS3 @@ -938,7 +943,7 @@ F: pc-bios/npcm7xx_bootrom.bin F: pc-bios/npcm8xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst -F: tests/functional/test_arm_quanta_gsj.py +F: tests/functional/arm/test_quanta_gsj.py Raspberry Pi M: Peter Maydell <peter.maydell@linaro.org> @@ -951,9 +956,8 @@ F: hw/*/bcm283* F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst -F: tests/functional/test_arm_raspi2.py -F: tests/functional/test_aarch64_raspi3.py -F: tests/functional/test_aarch64_raspi4.py +F: tests/functional/arm/test_raspi2.py +F: tests/functional/aarch64/test_raspi*.py Real View M: Peter Maydell <peter.maydell@linaro.org> @@ -964,7 +968,7 @@ F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst -F: tests/functional/test_arm_realview.py +F: tests/functional/arm/test_realview.py SABRELITE / i.MX6 M: Peter Maydell <peter.maydell@linaro.org> @@ -993,7 +997,7 @@ F: hw/misc/sbsa_ec.c F: hw/watchdog/sbsa_gwdt.c F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst -F: tests/functional/test_aarch64_*sbsaref*.py +F: tests/functional/aarch64/test_*sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell <peter.maydell@linaro.org> @@ -1004,7 +1008,7 @@ F: hw/arm/strongarm* F: hw/gpio/zaurus.c F: include/hw/arm/sharpsl.h F: docs/system/arm/collie.rst -F: tests/functional/test_arm_collie.py +F: tests/functional/arm/test_collie.py Stellaris M: Peter Maydell <peter.maydell@linaro.org> @@ -1015,7 +1019,7 @@ F: hw/display/ssd03* F: include/hw/input/stellaris_gamepad.h F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst -F: tests/functional/test_arm_stellaris.py +F: tests/functional/arm/test_stellaris.py STM32L4x5 SoC Family M: Samuel Tardieu <sam@rfc1149.net> @@ -1044,7 +1048,7 @@ S: Odd Fixes F: hw/arm/vexpress.c F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst -F: tests/functional/test_arm_vexpress.py +F: tests/functional/arm/test_vexpress.py Versatile PB M: Peter Maydell <peter.maydell@linaro.org> @@ -1063,10 +1067,10 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/functional/test_aarch64_*virt*.py -F: tests/functional/test_aarch64_tuxrun.py -F: tests/functional/test_arm_tuxrun.py -F: tests/functional/test_arm_virt.py +F: tests/functional/aarch64/test_*virt*.py +F: tests/functional/aarch64/test_tuxrun.py +F: tests/functional/arm/test_tuxrun.py +F: tests/functional/arm/test_virt.py Xilinx Zynq M: Edgar E. Iglesias <edgar.iglesias@gmail.com> @@ -1096,7 +1100,7 @@ F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst F: docs/system/arm/xlnx-zcu102.rst -F: tests/functional/test_aarch64_xlnx_versal.py +F: tests/functional/aarch64/test_xlnx_versal.py Xilinx Versal OSPI M: Francisco Iglesias <francisco.iglesias@amd.com> @@ -1187,7 +1191,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c F: docs/system/arm/emcraft-sf2.rst -F: tests/functional/test_arm_emcraft_sf2.py +F: tests/functional/arm/test_emcraft_sf2.py ASPEED BMCs M: Cédric Le Goater <clg@kaod.org> @@ -1205,6 +1209,7 @@ F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst F: docs/system/arm/fby35.rst +F: tests/functional/*/*aspeed* F: tests/*/*aspeed* F: tests/*/*ast2700* F: hw/arm/fby35.c @@ -1220,7 +1225,7 @@ F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c -F: tests/functional/test_arm_microbit.py +F: tests/functional/arm/test_microbit.py F: docs/system/arm/nrf.rst ARM PL011 Rust device @@ -1247,7 +1252,7 @@ Arduino M: Philippe Mathieu-Daudé <philmd@linaro.org> S: Maintained F: hw/avr/arduino.c -F: tests/functional/test_avr_uno.py +F: tests/functional/avr/test_uno.py HP-PARISC Machines ------------------ @@ -1271,7 +1276,7 @@ F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img F: roms/seabios-hppa/ -F: tests/functional/test_hppa_seabios.py +F: tests/functional/hppa/test_seabios.py LoongArch Machines ------------------ @@ -1309,7 +1314,7 @@ F: hw/m68k/mcf_intc.c F: hw/char/mcf_uart.c F: hw/net/mcf_fec.c F: include/hw/m68k/mcf*.h -F: tests/functional/test_m68k_mcf5208evb.py +F: tests/functional/m68k/test_mcf5208evb.py NeXTcube M: Thomas Huth <huth@tuxfamily.org> @@ -1317,7 +1322,7 @@ S: Odd Fixes F: hw/m68k/next-*.c F: hw/display/next-fb.c F: include/hw/m68k/next-cube.h -F: tests/functional/test_m68k_nextcube.py +F: tests/functional/m68k/test_nextcube.py q800 M: Laurent Vivier <laurent@vivier.eu> @@ -1343,7 +1348,7 @@ F: include/hw/m68k/q800-glue.h F: include/hw/misc/djmemc.h F: include/hw/misc/iosb.h F: include/hw/audio/asc.h -F: tests/functional/test_m68k_q800.py +F: tests/functional/m68k/test_q800.py virt M: Laurent Vivier <laurent@vivier.eu> @@ -1358,7 +1363,7 @@ F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h F: docs/specs/virt-ctlr.rst -F: tests/functional/test_m68k_tuxrun.py +F: tests/functional/m68k/test_tuxrun.py MicroBlaze Machines ------------------- @@ -1367,7 +1372,7 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com> S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/functional/test_microblaze*.py +F: tests/functional/microblaze*/test_s3adsp1800.py petalogix_ml605 M: Edgar E. Iglesias <edgar.iglesias@gmail.com> @@ -1403,8 +1408,8 @@ F: hw/acpi/piix4.c F: hw/mips/malta.c F: hw/pci-host/gt64120.c F: include/hw/southbridge/piix.h -F: tests/functional/test_mips*_malta.py -F: tests/functional/test_mips*_tuxrun.py +F: tests/functional/mips*/test_malta.py +F: tests/functional/mips*/test_tuxrun.py Mipssim R: Aleksandar Rikalo <arikalo@gmail.com> @@ -1420,7 +1425,7 @@ S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/pci-host/bonito.c F: include/hw/pci-host/bonito.h -F: tests/functional/test_mips64el_fuloong2e.py +F: tests/functional/mips64el/test_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen <chenhuacai@kernel.org> @@ -1435,7 +1440,7 @@ F: hw/mips/loongson3_virt.c F: include/hw/intc/loongson_ipi_common.h F: include/hw/intc/loongson_ipi.h F: include/hw/intc/loongson_liointc.h -F: tests/functional/test_mips64el_loongson3v.py +F: tests/functional/mips64el/test_loongson3v.py Boston M: Paul Burton <paulburton@kernel.org> @@ -1454,7 +1459,7 @@ S: Maintained F: docs/system/openrisc/or1k-sim.rst F: hw/intc/ompic.c F: hw/openrisc/openrisc_sim.c -F: tests/functional/test_or1k_sim.py +F: tests/functional/or1k/test_sim.py PowerPC Machines ---------------- @@ -1463,7 +1468,7 @@ L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c F: hw/pci-host/ppc4xx_pci.c -F: tests/functional/test_ppc_bamboo.py +F: tests/functional/ppc/test_bamboo.py e500 M: Bernhard Beschow <shentey@gmail.com> @@ -1481,8 +1486,8 @@ F: pc-bios/u-boot.e500 F: hw/intc/openpic_kvm.c F: include/hw/ppc/openpic_kvm.h F: docs/system/ppc/ppce500.rst -F: tests/functional/test_ppc64_e500.py -F: tests/functional/test_ppc_tuxrun.py +F: tests/functional/ppc64/test_e500.py +F: tests/functional/ppc/test_tuxrun.py mpc8544ds M: Bernhard Beschow <shentey@gmail.com> @@ -1490,7 +1495,7 @@ L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/functional/test_ppc_mpc8544ds.py +F: tests/functional/ppc/test_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> @@ -1512,8 +1517,8 @@ F: include/hw/ppc/mac_dbdma.h F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py -F: tests/functional/test_ppc64_mac99.py +F: tests/functional/ppc/test_mac.py +F: tests/functional/ppc64/test_mac99.py Old World (g3beige) M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> @@ -1529,7 +1534,7 @@ F: include/hw/intc/heathrow_pic.h F: include/hw/input/adb* F: include/hw/pci-host/grackle.h F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py +F: tests/functional/ppc/test_mac.py PReP M: Hervé Poussineau <hpoussin@reactos.org> @@ -1546,7 +1551,7 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: tests/functional/test_ppc_40p.py +F: tests/functional/ppc/test_40p.py sPAPR (pseries) M: Nicholas Piggin <npiggin@gmail.com> @@ -1569,9 +1574,9 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/functional/test_ppc64_pseries.py -F: tests/functional/test_ppc64_hv.py -F: tests/functional/test_ppc64_tuxrun.py +F: tests/functional/ppc64/test_pseries.py +F: tests/functional/ppc64/test_hv.py +F: tests/functional/ppc64/test_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin <npiggin@gmail.com> @@ -1590,7 +1595,7 @@ F: include/hw/ssi/pnv_spi* F: pc-bios/skiboot.lid F: pc-bios/pnv-pnor.bin F: tests/qtest/pnv* -F: tests/functional/test_ppc64_powernv.py +F: tests/functional/ppc64/test_powernv.py pca955x M: Glenn Miles <milesg@linux.ibm.com> @@ -1605,7 +1610,7 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com> L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/functional/test_ppc_virtex_ml507.py +F: tests/functional/ppc/test_virtex_ml507.py sam460ex M: BALATON Zoltan <balaton@eik.bme.hu> @@ -1621,7 +1626,7 @@ F: pc-bios/dtb/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex F: docs/system/ppc/amigang.rst -F: tests/functional/test_ppc_sam460ex.py +F: tests/functional/ppc/test_sam460ex.py pegasos2 M: BALATON Zoltan <balaton@eik.bme.hu> @@ -1639,7 +1644,7 @@ S: Maintained F: hw/ppc/amigaone.c F: hw/pci-host/articia.c F: include/hw/pci-host/articia.h -F: tests/functional/test_ppc_amiga.py +F: tests/functional/ppc/test_amiga.py Virtual Open Firmware (VOF) M: Alexey Kardashevskiy <aik@ozlabs.ru> @@ -1717,7 +1722,7 @@ R: Yoshinori Sato <yoshinori.sato@nifty.com> S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/functional/test_rx_gdbsim.py +F: tests/functional/rx/test_gdbsim.py SH4 Machines ------------ @@ -1732,8 +1737,8 @@ F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h F: include/hw/timer/tmu012.h -F: tests/functional/test_sh4*_r2d.py -F: tests/functional/test_sh4_tuxrun.py +F: tests/functional/sh4*/test_r2d.py +F: tests/functional/sh4/test_tuxrun.py SPARC Machines -------------- @@ -1751,7 +1756,7 @@ F: include/hw/nvram/sun_nvram.h F: include/hw/sparc/sparc32_dma.h F: include/hw/sparc/sun4m_iommu.h F: pc-bios/openbios-sparc32 -F: tests/functional/test_sparc_sun4m.py +F: tests/functional/sparc/test_sun4m.py Sun4u M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> @@ -1764,8 +1769,8 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 -F: tests/functional/test_sparc64_sun4u.py -F: tests/functional/test_sparc64_tuxrun.py +F: tests/functional/sparc64/test_sun4u.py +F: tests/functional/sparc64/test_tuxrun.py Sun4v M: Artyom Tarasenko <atar4qemu@gmail.com> @@ -1793,7 +1798,7 @@ S: Supported F: hw/s390x/ F: include/hw/s390x/ F: configs/devices/s390x-softmmu/default.mak -F: tests/functional/test_s390x_* +F: tests/functional/s390x T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1807,7 +1812,7 @@ F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: docs/devel/s390-dasd-ipl.rst -F: tests/functional/test_s390x_pxelinux.py +F: tests/functional/s390x/test_pxelinux.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1861,7 +1866,7 @@ F: hw/s390x/cpu-topology.c F: target/s390x/kvm/stsi-topology.c F: docs/devel/s390-cpu-topology.rst F: docs/system/s390x/cpu-topology.rst -F: tests/functional/test_s390x_topology.py +F: tests/functional/s390x/test_topology.py X86 Machines ------------ @@ -1889,12 +1894,12 @@ F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c -F: tests/functional/test_i386_tuxrun.py -F: tests/functional/test_linux_initrd.py -F: tests/functional/test_mem_addr_space.py -F: tests/functional/test_pc_cpu_hotplug_props.py -F: tests/functional/test_x86_64_tuxrun.py -F: tests/functional/test_x86_cpu_model_versions.py +F: tests/functional/i386/test_tuxrun.py +F: tests/functional/x86_64/test_linux_initrd.py +F: tests/functional/x86_64/test_mem_addr_space.py +F: tests/functional/x86_64/test_pc_cpu_hotplug_props.py +F: tests/functional/x86_64/test_tuxrun.py +F: tests/functional/x86_64/test_cpu_model_versions.py PC Chipset M: Michael S. Tsirkin <mst@redhat.com> @@ -1970,8 +1975,8 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/system/numa.h -F: tests/functional/test_cpu_queries.py -F: tests/functional/test_empty_cpu_model.py +F: tests/functional/x86_64/test_cpu_queries.py +F: tests/functional/generic/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2000,7 +2005,7 @@ S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c F: include/hw/xtensa/mx_pic.h -F: tests/functional/test_xtensa_lx60.py +F: tests/functional/xtensa/test_lx60.py Devices ------- @@ -2077,7 +2082,7 @@ S: Odd Fixes F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst -F: tests/functional/test_arm_sx1.py +F: tests/functional/arm/test_sx1.py IPack M: Alberto Garcia <berto@igalia.com> @@ -2109,7 +2114,7 @@ ARM PCI Hotplug M: Gustavo Romero <gustavo.romero@linaro.org> L: qemu-arm@nongnu.org S: Supported -F: tests/functional/test_aarch64_hotplug_pci.py +F: tests/functional/aarch64/test_hotplug_pci.py ACPI/SMBIOS M: Michael S. Tsirkin <mst@redhat.com> @@ -2155,7 +2160,7 @@ M: Ani Sinha <anisinha@redhat.com> M: Michael S. Tsirkin <mst@redhat.com> S: Supported F: tests/functional/acpi-bits/* -F: tests/functional/test_acpi_bits.py +F: tests/functional/x86_64/test_acpi_bits.py F: docs/devel/testing/acpi-bits.rst ACPI/HEST/GHES @@ -2192,7 +2197,7 @@ S: Odd Fixes F: hw/net/ F: include/hw/net/ F: tests/qtest/virtio-net-test.c -F: tests/functional/test_info_usernet.py +F: tests/functional/generic/test_info_usernet.py F: docs/system/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -2263,6 +2268,7 @@ F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json +F: tests/functional/aarch64/test_device_passthrough.py vfio-igd M: Alex Williamson <alex.williamson@redhat.com> @@ -2340,7 +2346,7 @@ F: net/vhost-user.c F: include/hw/virtio/ F: docs/devel/virtio* F: docs/devel/migration/virtio.rst -F: tests/functional/test_virtio_version.py +F: tests/functional/x86_64/test_virtio_version.py virtio-balloon M: Michael S. Tsirkin <mst@redhat.com> @@ -2352,7 +2358,7 @@ F: include/hw/virtio/virtio-balloon.h F: system/balloon.c F: include/system/balloon.h F: tests/qtest/virtio-balloon-test.c -F: tests/functional/test_virtio_balloon.py +F: tests/functional/x86_64/test_virtio_balloon.py virtio-9p M: Christian Schoenebeck <qemu_oss@crudebyte.com> @@ -2375,7 +2381,7 @@ F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c -F: tests/functional/test_x86_64_hotplug_blk.py +F: tests/functional/x86_64/test_hotplug_blk.py T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -2599,7 +2605,7 @@ R: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* -F: tests/functional/test_netdev_ethtool.py +F: tests/functional/x86_64/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c @@ -2638,7 +2644,7 @@ M: Alex Bennée <alex.bennee@linaro.org> S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/functional/test_aarch64_xen.py +F: tests/functional/aarch64/test_xen.py Intel Hexadecimal Object File Loader M: Su Hang <suhang16@mails.ucas.ac.cn> @@ -2707,7 +2713,8 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst -F: tests/functional/test_aarch64_virt_gpu.py +F: tests/functional/aarch64/test_virt_gpu.py +F: tests/functional/x86_64/test_virtio_gpu.py vhost-user-blk M: Raphael Norwitz <raphael@enfabrica.net> @@ -3127,7 +3134,7 @@ S: Supported F: include/qemu/option.h F: tests/unit/test-keyval.c F: tests/unit/test-qemu-opts.c -F: tests/functional/test_version.py +F: tests/functional/generic/test_version.py F: util/keyval.c F: util/qemu-option.c @@ -3245,7 +3252,7 @@ F: include/ui/ F: qapi/ui.json F: util/drm.c F: docs/devel/ui.rst -F: tests/functional/test_vnc.py +F: tests/functional/generic/test_vnc.py Cocoa graphics M: Peter Maydell <peter.maydell@linaro.org> @@ -3447,6 +3454,7 @@ F: qom/ F: tests/unit/check-qom-interface.c F: tests/unit/check-qom-proplist.c F: tests/unit/test-qdev-global-props.c +F: tests/qtest/qom-test.c QOM boilerplate conversion script M: Eduardo Habkost <eduardo@habkost.net> @@ -3597,7 +3605,8 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py -F: tests/functional/test_migration.py +F: tests/functional/migration.py +F: tests/functional/*/*migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* @@ -3766,8 +3775,10 @@ F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c -F: tests/functional/*reverse_debug*.py -F: tests/functional/*replay*.py +F: tests/functional/replay_kernel.py +F: tests/functional/reverse_debugging.py +F: tests/functional/*/*replay*.py +F: tests/functional/*/*reverse_debug*.py F: qapi/replay.json IOVA Tree @@ -3851,7 +3862,7 @@ S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h -F: tests/functional/test_intel_iommu.py +F: tests/functional/x86_64/test_intel_iommu.py F: tests/qtest/intel-iommu-test.c AMD-Vi Emulation @@ -3913,7 +3924,7 @@ F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh -F: tests/functional/test_arm_bflt.py +F: tests/functional/arm/test_bflt.py Tiny Code Generator (TCG) ------------------------- @@ -3933,7 +3944,7 @@ S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ F: tests/tcg/plugins/ -F: tests/functional/test_aarch64_tcg_plugins.py +F: tests/functional/aarch64/test_tcg_plugins.py F: contrib/plugins/ F: scripts/qemu-plugin-symbols.py @@ -4286,7 +4297,8 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h -F: tests/functional/test_multiprocess.py +F: tests/functional/multiprocess.py +F: tests/functional/*/*multiprocess.py VFIO-USER: M: John Levon <john.levon@nutanix.com> @@ -4325,7 +4337,7 @@ F: scripts/ci/ F: tests/docker/ F: tests/vm/ F: tests/lcitool/ -F: tests/functional/test_*_tuxrun.py +F: tests/functional/*/test_tuxrun.py F: scripts/archive-source.sh F: docs/devel/testing/ci* F: docs/devel/testing/main.rst @@ -4392,7 +4404,6 @@ R: Philippe Mathieu-Daudé <philmd@linaro.org> S: Maintained F: meson.build F: meson_options.txt -F: scripts/meson-buildoptions.* F: scripts/check_sparse.py F: scripts/symlink-install-tree.py @@ -4403,6 +4414,9 @@ R: Thomas Huth <thuth@redhat.com> S: Maintained F: Makefile F: configure +F: pythondeps.toml +F: scripts/git-submodule.sh +F: scripts/meson-buildoptions.* F: scripts/mtest2make.py F: tests/Makefile.include @@ -4428,6 +4442,7 @@ F: po/*.po Sphinx documentation configuration and build machinery M: John Snow <jsnow@redhat.com> M: Peter Maydell <peter.maydell@linaro.org> +M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> S: Maintained F: docs/conf.py F: docs/*/conf.py @@ -4436,6 +4451,8 @@ F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst F: docs/devel/qapi-domain.rst +F: scripts/kernel-doc +F: scripts/lib/kdoc/ Rust build system integration M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 890d5ea9f8..f36dfe3349 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -414,7 +414,7 @@ err: return ret; } -void kvm_park_vcpu(CPUState *cpu) +static void kvm_park_vcpu(CPUState *cpu) { struct KVMParkedVcpu *vcpu; @@ -426,7 +426,7 @@ void kvm_park_vcpu(CPUState *cpu) QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); } -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) +static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; int kvm_fd = -ENOENT; diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6056598c23..bca93a0ac4 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch) GEN_ATOMIC_HELPERS(xchg) +#if HAVE_CMPXCHG128 +ATOMIC_HELPER(xchgo_be, Int128) +ATOMIC_HELPER(xchgo_le, Int128) +ATOMIC_HELPER(fetch_ando_be, Int128) +ATOMIC_HELPER(fetch_ando_le, Int128) +ATOMIC_HELPER(fetch_oro_be, Int128) +ATOMIC_HELPER(fetch_oro_le, Int128) +#endif + #undef ATOMIC_HELPER #undef GEN_ATOMIC_HELPERS diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 08a475c10c..ae5203b439 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return ret; } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -108,7 +107,28 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); DATA_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, val); +#else ret = qatomic_xchg__nocheck(haddr, val); +#endif + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} + +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, val); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -119,6 +139,22 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return ret; } +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, val); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA SIZE < 16 */ +#endif /* DATA SIZE == 16 */ #undef END @@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return BSWAP(ret); } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -233,7 +268,28 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); ABI_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, BSWAP(val)); +#else ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); +#endif + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} + +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -244,6 +300,22 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return BSWAP(ret); } +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val)); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #undef ADD #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA_SIZE < 16 */ +#endif /* DATA_SIZE == 16 */ #undef END #endif /* DATA_SIZE > 1 */ diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 713bdb2056..8491e5badd 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -778,24 +778,20 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, */ qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); - if (unlikely(qatomic_read(&cpu->interrupt_request))) { - int interrupt_request; +#ifdef CONFIG_USER_ONLY + assert(!cpu_test_interrupt(cpu, ~0)); +#else + if (unlikely(cpu_test_interrupt(cpu, ~0))) { bql_lock(); - interrupt_request = cpu->interrupt_request; - if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { - /* Mask out external interrupts for this step. */ - interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; - } - if (interrupt_request & CPU_INTERRUPT_DEBUG) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; bql_unlock(); return true; } -#if !defined(CONFIG_USER_ONLY) if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ - } else if (interrupt_request & CPU_INTERRUPT_HALT) { + } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) { replay_interrupt(); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; @@ -804,14 +800,20 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, return true; } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + int interrupt_request = cpu->interrupt_request; - if (interrupt_request & CPU_INTERRUPT_RESET) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) { replay_interrupt(); tcg_ops->cpu_exec_reset(cpu); bql_unlock(); return true; } + if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { + /* Mask out external interrupts for this step. */ + interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; + } + /* * The target hook has 3 exit conditions: * False when the interrupt isn't processed, @@ -836,12 +838,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu->exception_index = -1; *last_tb = NULL; } - /* The target hook may have updated the 'cpu->interrupt_request'; - * reload the 'interrupt_request' value */ - interrupt_request = cpu->interrupt_request; } -#endif /* !CONFIG_USER_ONLY */ - if (interrupt_request & CPU_INTERRUPT_EXITTB) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as the program flow was changed */ @@ -851,6 +849,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ bql_unlock(); } +#endif /* !CONFIG_USER_ONLY */ /* Finally, check if we need to exit to the main loop. */ if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 3b0d7d298e..9c37266c1e 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -97,7 +97,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu) /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); /* * If called from iothread context, wake the target cpu in diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index c23b5e66c4..8436599b9f 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) #endif DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG, diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index f25d80e2dc..748bfab04a 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -48,9 +48,7 @@ __thread uintptr_t helper_retaddr; void cpu_interrupt(CPUState *cpu, int mask) { - g_assert(bql_locked()); - cpu->interrupt_request |= mask; - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); + g_assert_not_reached(); } /* diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h index cc87f475b3..cec254f88b 100644 --- a/bsd-user/aarch64/target_arch_elf.h +++ b/bsd-user/aarch64/target_arch_elf.h @@ -114,7 +114,7 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); diff --git a/bsd-user/main.c b/bsd-user/main.c index 7e5d4bbce0..9ba69642f5 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -367,14 +367,6 @@ int main(int argc, char **argv) } } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; - } else if (!strcmp(r, "p")) { - unsigned size, want = qemu_real_host_page_size(); - - r = argv[optind++]; - if (qemu_strtoui(r, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } } else if (!strcmp(r, "g")) { gdbstub = g_strdup(argv[optind++]); } else if (!strcmp(r, "r")) { diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d50645a071..5d1579dcf8 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -81,16 +81,6 @@ kernel since 2001. None of the board types QEMU supports need ``param_struct`` support, so this option has been deprecated and will be removed in a future QEMU version. -User-mode emulator command line arguments ------------------------------------------ - -``-p`` (since 9.0) -'''''''''''''''''' - -The ``-p`` option pretends to control the host page size. However, -it is not possible to change the host page size, and using the -option only causes failures. - QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index d7c2113fc3..25a904032c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -571,6 +571,14 @@ The ``-singlestep`` option has been given a name that better reflects what it actually does. For both linux-user and bsd-user, use the ``-one-insn-per-tb`` option instead. +``-p`` (removed in 10.2) +'''''''''''''''''''''''' + +The ``-p`` option pretends to control the host page size. However, +it is not possible to change the host page size; we stopped trying +to do anything with the option except print a warning from 9.0, +and now the option is removed entirely. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/conf.py b/docs/conf.py index f892a6e1da..e09769e5f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -341,7 +341,9 @@ man_make_section_directory = False # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. -kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')] +# Since kernel-doc is now a Python script, we should run it with whatever +# Python this sphinx is using (rather than letting it find one via env) +kerneldoc_bin = [sys.executable, os.path.join(qemu_docdir, '../scripts/kernel-doc.py')] kerneldoc_srctree = os.path.join(qemu_docdir, '..') hxtool_srctree = os.path.join(qemu_docdir, '..') qapidoc_srctree = os.path.join(qemu_docdir, '..') diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 3aa972f2e8..9721072e47 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -63,11 +63,6 @@ class KernelDocDirective(Directive): env = self.state.document.settings.env cmd = env.config.kerneldoc_bin + ['-rst', '-enable-lineno'] - # Pass the version string to kernel-doc, as it needs to use a different - # dialect, depending what the C domain supports for each specific - # Sphinx versions - cmd += ['-sphinx-version', sphinx.__version__] - # Pass through the warnings-as-errors flag if env.config.kerneldoc_werror: cmd += ['-Werror'] @@ -127,7 +122,7 @@ class KernelDocDirective(Directive): result = ViewList() lineoffset = 0; - line_regex = re.compile("^#define LINENO ([0-9]+)$") + line_regex = re.compile(r"^(?:\.\.|#define) LINENO ([0-9]+)$") for line in lines: match = line_regex.search(line) if match: diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 890dc6fee2..4e8aca8b5d 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -30,6 +30,7 @@ the following architecture extensions: - FEAT_CMOW (Control for cache maintenance permission) - FEAT_CRC32 (CRC32 instructions) - FEAT_Crypto (Cryptographic Extension) +- FEAT_CSSC (Common Short Sequence Compression instructions) - FEAT_CSV2 (Cache speculation variant 2) - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) @@ -88,6 +89,7 @@ the following architecture extensions: - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) +- FEAT_LSE128 (128-bit Atomics) - FEAT_LVA (Large Virtual Address space) - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) @@ -121,6 +123,7 @@ the following architecture extensions: - FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) +- FEAT_SCTLR2 (Extension to SCTLR_ELx) - FEAT_SEL2 (Secure EL2) - FEAT_SHA1 (SHA1 instructions) - FEAT_SHA256 (SHA256 instructions) @@ -148,6 +151,7 @@ the following architecture extensions: - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) - FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2) +- FEAT_TCR2 (Support for TCR2_ELx) - FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1) - FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1) - FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1) diff --git a/docs/user/main.rst b/docs/user/main.rst index 347bdfabf8..a8ddf91424 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -262,9 +262,6 @@ Debug options: Activate logging of the specified items (use '-d help' for a list of log items) -``-p pagesize`` - Act as if the host page size was 'pagesize' bytes - ``-one-insn-per-tb`` Run the emulation with one guest instruction per translation block. This slows down emulation a lot, but can be useful in some situations, diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h deleted file mode 100644 index 991da4ef54..0000000000 --- a/host/include/aarch64/host/atomic128-cas.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-2.0-or-later - * Compare-and-swap for 128-bit atomic operations, AArch64 version. - * - * Copyright (C) 2018, 2023 Linaro, Ltd. - * - * See docs/devel/atomics.rst for discussion about the guarantees each - * atomic primitive is meant to provide. - */ - -#ifndef AARCH64_ATOMIC128_CAS_H -#define AARCH64_ATOMIC128_CAS_H - -/* Through gcc 10, aarch64 has no support for 128-bit atomics. */ -#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128) -#include "host/include/generic/host/atomic128-cas.h.inc" -#else -static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) -{ - uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); - uint64_t newl = int128_getlo(new), newh = int128_gethi(new); - uint64_t oldl, oldh; - uint32_t tmp; - - asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" - "cmp %[oldl], %[cmpl]\n\t" - "ccmp %[oldh], %[cmph], #0, eq\n\t" - "b.ne 1f\n\t" - "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" - "cbnz %w[tmp], 0b\n" - "1:" - : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), - [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) - : [cmpl] "r"(cmpl), [cmph] "r"(cmph), - [newl] "r"(newl), [newh] "r"(newh) - : "memory", "cc"); - - return int128_make128(oldl, oldh); -} - -# define CONFIG_CMPXCHG128 1 -# define HAVE_CMPXCHG128 1 -#endif - -#endif /* AARCH64_ATOMIC128_CAS_H */ diff --git a/host/include/aarch64/host/atomic128-cas.h.inc b/host/include/aarch64/host/atomic128-cas.h.inc new file mode 100644 index 0000000000..aec27df182 --- /dev/null +++ b/host/include/aarch64/host/atomic128-cas.h.inc @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Compare-and-swap for 128-bit atomic operations, AArch64 version. + * + * Copyright (C) 2018, 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef AARCH64_ATOMIC128_CAS_H +#define AARCH64_ATOMIC128_CAS_H + +/* Through gcc 10, aarch64 has no support for 128-bit atomics. */ +#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128) +#include "host/include/generic/host/atomic128-cas.h.inc" +#else +static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "cmp %[oldl], %[cmpl]\n\t" + "ccmp %[oldh], %[cmph], #0, eq\n\t" + "b.ne 1f\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b\n" + "1:" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [cmpl] "r"(cmpl), [cmph] "r"(cmph), + [newl] "r"(newl), [newh] "r"(newh) + : "memory", "cc"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_xchg(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_and(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "and %[tmpl], %[oldl], %[newl]\n\t" + "and %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_or(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "orr %[tmpl], %[oldl], %[newl]\n\t" + "orr %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + +# define CONFIG_CMPXCHG128 1 +# define HAVE_CMPXCHG128 1 +#endif + +#endif /* AARCH64_ATOMIC128_CAS_H */ diff --git a/host/include/generic/host/atomic128-cas.h.inc b/host/include/generic/host/atomic128-cas.h.inc index 6b40cc2271..990162c56f 100644 --- a/host/include/generic/host/atomic128-cas.h.inc +++ b/host/include/generic/host/atomic128-cas.h.inc @@ -23,6 +23,51 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i); return r.s; } + +/* + * Since we're looping anyway, use weak compare and swap. + * If the host supports weak, this will eliminate a second loop hidden + * within the atomic operation itself; otherwise the weak parameter is + * ignored. + */ +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, new, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old & val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old | val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} # define HAVE_CMPXCHG128 1 #elif defined(CONFIG_CMPXCHG128) static inline Int128 ATTRIBUTE_ATOMIC128_OPT @@ -36,6 +81,57 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i); return r.s; } + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, n; + + n.s = new; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, n.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i & v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i | v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} # define HAVE_CMPXCHG128 1 #else /* Fallback definition that must be optimized away, or error. */ diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 58f8964e13..ff16582803 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -547,6 +547,7 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar); memory_region_init_io(&ar->tmr.io, memory_region_owner(parent), &acpi_pm_tmr_ops, ar, "acpi-tmr", 4); + memory_region_enable_lockless_io(&ar->tmr.io); memory_region_add_subregion(parent, 8, &ar->tmr.io); } diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d391cd01bb..1e57c4ab9e 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -25,6 +25,7 @@ #include "hw/boards.h" #include "system/reset.h" #include "hw/loader.h" +#include "hw/mem/memory-device.h" #include "elf.h" #include "system/device_tree.h" #include "qemu/config-file.h" @@ -515,6 +516,29 @@ static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } +static int fdt_add_pmem_node(void *fdt, uint32_t acells, uint32_t scells, + int64_t mem_base, int64_t size, int64_t node) +{ + int ret; + + g_autofree char *nodename = g_strdup_printf("/pmem@%" PRIx64, mem_base); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "pmem-region"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, + mem_base, scells, size); + if (ret) { + return ret; + } + + if (node >= 0) { + return qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", + node); + } + + return 0; +} + int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, hwaddr addr_limit, AddressSpace *as, MachineState *ms, ARMCPU *cpu) @@ -525,6 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, unsigned int i; hwaddr mem_base, mem_len; char **node_path; + g_autofree MemoryDeviceInfoList *md_list = NULL; Error *err = NULL; if (binfo->dtb_filename) { @@ -628,6 +653,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } } + md_list = qmp_memory_device_list(); + for (MemoryDeviceInfoList *m = md_list; m != NULL; m = m->next) { + MemoryDeviceInfo *mi = m->value; + + if (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM) { + PCDIMMDeviceInfo *di = mi->u.nvdimm.data; + + rc = fdt_add_pmem_node(fdt, acells, scells, + di->addr, di->size, di->node); + if (rc < 0) { + fprintf(stderr, "couldn't add NVDIMM /pmem@%"PRIx64" node\n", + di->addr); + goto fail; + } + } + } + rc = fdt_path_offset(fdt, "/chosen"); if (rc < 0) { qemu_fdt_add_subnode(fdt, "/chosen"); diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 229af7fb10..e3c7203c6e 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -66,7 +66,7 @@ static void stm32f205_soc_initfn(Object *obj) TYPE_STM32F2XX_TIMER); } - s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_initialize_child(obj, "adc-irq-orgate", &s->adc_irqs, TYPE_OR_IRQ); for (i = 0; i < STM_NUM_ADCS; i++) { object_initialize_child(obj, "adc[*]", &s->adc[i], TYPE_STM32F2XX_ADC); @@ -171,12 +171,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } /* ADC 1 to 3 */ - object_property_set_int(OBJECT(s->adc_irqs), "num-lines", STM_NUM_ADCS, + object_property_set_int(OBJECT(&s->adc_irqs), "num-lines", STM_NUM_ADCS, &error_abort); - if (!qdev_realize(DEVICE(s->adc_irqs), NULL, errp)) { + if (!qdev_realize(DEVICE(&s->adc_irqs), NULL, errp)) { return; } - qdev_connect_gpio_out(DEVICE(s->adc_irqs), 0, + qdev_connect_gpio_out(DEVICE(&s->adc_irqs), 0, qdev_get_gpio_in(armv7m, ADC_IRQ)); for (i = 0; i < STM_NUM_ADCS; i++) { @@ -187,7 +187,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, adc_addr[i]); sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->adc_irqs), i)); + qdev_get_gpio_in(DEVICE(&s->adc_irqs), i)); } /* SPI 1 and 2 */ diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ef6be3660f..1e63f40fbe 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2917,7 +2917,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - if (!vms->acpi_dev) { + if (!vms->acpi_dev && !(is_nvdimm && !dev->hotplugged)) { error_setg(errp, "memory hotplug is not enabled: missing acpi-ged device"); return; @@ -2949,8 +2949,10 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, nvdimm_plug(ms->nvdimms_state); } - hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), - dev, &error_abort); + if (vms->acpi_dev) { + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), + dev, &error_abort); + } } static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, @@ -3455,10 +3457,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_10_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + static void virt_machine_10_1_options(MachineClass *mc) { + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 39e674aca2..259cf2a3c3 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -119,11 +119,6 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cpu->cc->reset_dump_flags); - } - cpu->interrupt_request = 0; cpu->halted = cpu->start_powered_off; cpu->mem_io_pc = 0; @@ -137,6 +132,21 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) cpu_exec_reset_hold(cpu); } +static void cpu_common_reset_exit(Object *obj, ResetType type) +{ + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + FILE *f = qemu_log_trylock(); + + if (f) { + CPUState *cpu = CPU(obj); + + fprintf(f, "CPU Reset (CPU %d)\n", cpu->cpu_index); + cpu_dump_state(cpu, f, cpu->cc->reset_dump_flags); + qemu_log_unlock(f); + } + } +} + ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { ObjectClass *oc; @@ -380,6 +390,7 @@ static void cpu_common_class_init(ObjectClass *klass, const void *data) dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; + rc->phases.exit = cpu_common_reset_exit; cpu_class_init_props(dc); /* * Reason: CPUs still need special care by board code: wiring up diff --git a/hw/core/loader.c b/hw/core/loader.c index e7056ba4bd..524af6f14a 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -295,10 +295,6 @@ static void *load_at(int fd, off_t offset, size_t size) return ptr; } -#ifdef ELF_CLASS -#undef ELF_CLASS -#endif - #define ELF_CLASS ELFCLASS32 #include "elf.h" diff --git a/hw/core/machine.c b/hw/core/machine.c index bd47527479..38c949c4f2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,6 +37,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_10_1[] = {}; +const size_t hw_compat_10_1_len = G_N_ELEMENTS(hw_compat_10_1); + GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, { "vfio-pci", "x-migration-load-config-after-iter", "off" }, diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 3a0e2b8ebb..6a0ab54bea 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -96,9 +96,6 @@ config ISAPC select ISA_BUS select PC select IDE_ISA - # FIXME: it is in the same file as i440fx, and does not compile - # if separated - depends on I440FX config Q35 bool diff --git a/hw/i386/isapc.c b/hw/i386/isapc.c new file mode 100644 index 0000000000..44f4a44672 --- /dev/null +++ b/hw/i386/isapc.c @@ -0,0 +1,189 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" + +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/char/parallel-isa.h" +#include "hw/dma/i8257.h" +#include "hw/i386/pc.h" +#include "hw/ide/isa.h" +#include "hw/ide/ide-bus.h" +#include "system/kvm.h" +#include "hw/i386/kvm/clock.h" +#include "hw/xen/xen-x86.h" +#include "system/xen.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/cpu.h" + +static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; +static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; +static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; + + +static void pc_init_isa(MachineState *machine) +{ + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_io = get_system_io(); + ISABus *isa_bus; + uint32_t irq; + GSIState *gsi_state; + MemoryRegion *ram_memory; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; + + /* + * There is a small chance that someone unintentionally passes "-cpu max" + * for the isapc machine, which will provide a much more modern 32-bit + * CPU than would be expected for an ISA-era PC. If the "max" cpu type has + * been specified, choose the "best" 32-bit cpu possible which we consider + * be the pentium3 (deliberately choosing an Intel CPU given that the + * default 486 CPU for the isapc machine is also an Intel CPU). + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu max is invalid for isapc machine, using pentium3"); + } + + /* + * Similarly if someone unintentionally passes "-cpu host" for the isapc + * machine then display a warning and also switch to the "best" 32-bit + * cpu possible which we consider to be the pentium3. This is because any + * host CPU will already be modern than this, but it also ensures any + * newer CPU flags/features are filtered out for older guests. + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu host is invalid for isapc machine, using pentium3"); + } + + if (machine->ram_size > 3.5 * GiB) { + error_report("Too much memory for this machine: %" PRId64 " MiB, " + "maximum 3584 MiB", machine->ram_size / MiB); + exit(1); + } + + /* + * There is no RAM split for the isapc machine + */ + if (xen_enabled()) { + xen_hvm_init_pc(pcms, &ram_memory); + } else { + ram_memory = machine->ram; + + pcms->max_ram_below_4g = 3.5 * GiB; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + + x86_cpus_init(x86ms, pcmc->default_cpu_version); + + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); + } + + /* allocate ram and load rom/bios */ + if (!xen_enabled()) { + pc_memory_init(pcms, system_memory, system_memory, 0); + } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + + if (machine->kernel_filename != NULL) { + /* For xen HVM direct kernel boot, load linux here */ + xen_load_linux(pcms); + } + } + + gsi_state = pc_gsi_create(&x86ms->gsi, false); + + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; + + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { + pc_i8259_create(isa_bus, gsi_state->i8259_irq); + } + + if (tcg_enabled()) { + x86_register_ferr_irq(x86ms->gsi[13]); + } + + pc_vga_init(isa_bus, NULL); + + /* init basic PC hardware */ + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); + + pc_nic_init(pcmc, isa_bus, NULL); + + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + } +} + +static void isapc_machine_options(MachineClass *m) +{ + static const char * const valid_cpu_types[] = { + X86_CPU_TYPE_NAME("486"), + X86_CPU_TYPE_NAME("athlon"), + X86_CPU_TYPE_NAME("kvm32"), + X86_CPU_TYPE_NAME("pentium"), + X86_CPU_TYPE_NAME("pentium2"), + X86_CPU_TYPE_NAME("pentium3"), + X86_CPU_TYPE_NAME("qemu32"), + X86_CPU_TYPE_NAME("max"), + X86_CPU_TYPE_NAME("host"), + NULL + }; + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + m->desc = "ISA-only PC"; + m->max_cpus = 1; + m->option_rom_has_mr = true; + m->rom_file_has_mr = false; + pcmc->pci_enabled = false; + pcmc->has_acpi_build = false; + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; + m->default_nic = "ne2k_isa"; + m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->valid_cpu_types = valid_cpu_types; + m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); +} + +DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, + isapc_machine_options); diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 7896f348cf..436b3ce52d 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -14,6 +14,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) +i386_ss.add(when: 'CONFIG_ISAPC', if_true: files('isapc.c')) i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2f58e73d33..bc048a6d13 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,6 +81,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_10_1[] = {}; +const size_t pc_compat_10_1_len = G_N_ELEMENTS(pc_compat_10_1); + GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c03324281b..7e78b6daa6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -27,20 +27,16 @@ #include "qemu/units.h" #include "hw/char/parallel-isa.h" -#include "hw/dma/i8257.h" -#include "hw/loader.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/pci-host/i440fx.h" -#include "hw/rtc/mc146818rtc.h" #include "hw/southbridge/piix.h" #include "hw/display/ramfb.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/irq.h" #include "system/kvm.h" @@ -72,12 +68,6 @@ #define XEN_IOAPIC_NUM_PIRQS 128ULL -#ifdef CONFIG_IDE_ISA -static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; -static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; -static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -#endif - static GlobalProperty pc_piix_compat_defaults[] = { { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, @@ -123,9 +113,13 @@ static void pc_init1(MachineState *machine, const char *pci_type) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *pci_memory = NULL; - MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; + + assert(pcmc->pci_enabled); /* * Calculate ram split, for memory below and above 4G. It's a bit @@ -196,42 +190,39 @@ static void pc_init1(MachineState *machine, const char *pci_type) kvmclock_create(pcmc->kvmclock_create_always); } - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - - phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); - object_property_add_child(OBJECT(machine), "i440fx", phb); - object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, - OBJECT(pci_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, - OBJECT(system_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, - OBJECT(system_io), &error_fatal); - object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, - x86ms->below_4g_mem_size, &error_fatal); - object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, - x86ms->above_4g_mem_size, &error_fatal); - object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, - &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - - pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pcms->pcibus, - xen_enabled() ? xen_pci_slot_get_pirq - : pc_pci_slot_get_pirq); - - hole64_size = object_property_get_uint(phb, - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); + + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + pc_memory_init(pcms, system_memory, pci_memory, hole64_size); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); @@ -243,72 +234,51 @@ static void pc_init1(MachineState *machine, const char *pci_type) } } - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - - if (pcmc->pci_enabled) { - PCIDevice *pci_dev; - DeviceState *dev; - size_t i; - - pci_dev = pci_new_multifunction(-1, pcms->south_bridge); - object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-acpi", - x86_machine_is_acpi_enabled(x86ms), - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pic", false, - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pit", false, - &error_abort); - qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); - object_property_set_bool(OBJECT(pci_dev), "smm-enabled", - x86_machine_is_smm_enabled(x86ms), - &error_abort); - dev = DEVICE(pci_dev); - for (i = 0; i < ISA_NUM_IRQS; i++) { - qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); - } - pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - - if (xen_enabled()) { - pci_device_set_intx_routing_notifier( - pci_dev, piix_intx_routing_notifier_xen); - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, - XEN_IOAPIC_NUM_PIRQS); - } - - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); - piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); - dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); - pci_ide_create_devs(PCI_DEVICE(dev)); - pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); - pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - } else { - uint32_t irq; + gsi_state = pc_gsi_create(&x86ms->gsi, true); + + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); + } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - } if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); @@ -322,7 +292,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + pc_vga_init(isa_bus, pcms->pcibus); /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, @@ -330,28 +300,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_nic_init(pcmc, isa_bus, pcms->pcibus); -#ifdef CONFIG_IDE_ISA - if (!pcmc->pci_enabled) { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } - } -#endif - if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); @@ -433,22 +381,7 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; } -#ifdef CONFIG_ISAPC -static void pc_init_isa(MachineState *machine) -{ - pc_init1(machine, NULL); -} -#endif - #ifdef CONFIG_XEN -static void pc_xen_hvm_init_pci(MachineState *machine) -{ - const char *pci_type = xen_igd_gfx_pt_enabled() ? - TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; - - pc_init1(machine, pci_type); -} - static void pc_xen_hvm_init(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); @@ -458,7 +391,10 @@ static void pc_xen_hvm_init(MachineState *machine) exit(1); } - pc_xen_hvm_init_pci(machine); + pc_init1(machine, xen_igd_gfx_pt_enabled() + ? TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE + : TYPE_I440FX_PCI_DEVICE); + xen_igd_reserve_slot(pcms->pcibus); pci_create_simple(pcms->pcibus, -1, "xen-platform"); } @@ -504,12 +440,21 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_10_1_options(MachineClass *m) +static void pc_i440fx_machine_10_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(10, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 2); + +static void pc_i440fx_machine_10_1_options(MachineClass *m) +{ + pc_i440fx_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_I440FX_MACHINE(10, 1); static void pc_i440fx_machine_10_0_options(MachineClass *m) { @@ -803,30 +748,6 @@ static void pc_i440fx_machine_2_6_options(MachineClass *m) DEFINE_I440FX_MACHINE(2, 6); -#ifdef CONFIG_ISAPC -static void isapc_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - m->desc = "ISA-only PC"; - m->max_cpus = 1; - m->option_rom_has_mr = true; - m->rom_file_has_mr = false; - pcmc->pci_enabled = false; - pcmc->has_acpi_build = false; - pcmc->smbios_defaults = false; - pcmc->gigabyte_align = false; - pcmc->smbios_legacy_mode = true; - pcmc->has_reserved_memory = false; - m->default_nic = "ne2k_isa"; - m->default_cpu_type = X86_CPU_TYPE_NAME("486"); - m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); - m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); -} - -DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, - isapc_machine_options); -#endif - #ifdef CONFIG_XEN static void xenfv_machine_4_2_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b309b2b378..e89951285e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -374,12 +374,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_10_1_options(MachineClass *m) +static void pc_q35_machine_10_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(10, 1); +DEFINE_Q35_MACHINE_AS_LATEST(10, 2); + +static void pc_q35_machine_10_1_options(MachineClass *m) +{ + pc_q35_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_Q35_MACHINE(10, 1); static void pc_q35_machine_10_0_options(MachineClass *m) { diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index b1b5f11e73..7512be64d6 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -952,7 +952,7 @@ void x86_load_linux(X86MachineState *x86ms, * kernel on the other side of the fw_cfg interface matches the hash of the * file the user passed in. */ - if (!sev_enabled() && protocol > 0) { + if (!MACHINE(x86ms)->cgs && protocol > 0) { memcpy(setup, header, MIN(sizeof(header), setup_size)); } diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 6166283cd1..0cd14d78a7 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -31,6 +31,7 @@ #include "gicv3_internal.h" #include "vgic_common.h" #include "migration/blocker.h" +#include "migration/misc.h" #include "qom/object.h" #include "target/arm/cpregs.h" @@ -776,6 +777,17 @@ static void vm_change_state_handler(void *opaque, bool running, } } +static int kvm_arm_gicv3_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + if (e->type == MIG_EVENT_PRECOPY_DONE) { + GICv3State *s = container_of(notifier, GICv3State, cpr_notifier); + return kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES, + NULL, true, errp); + } + return 0; +} static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { @@ -917,6 +929,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) { qemu_add_vm_change_state_handler(vm_change_state_handler, s); + migration_add_notifier_mode(&s->cpr_notifier, + kvm_arm_gicv3_notifier, + MIG_MODE_CPR_TRANSFER); } } diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index c4b242dbf4..32f01aabf0 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -110,10 +110,10 @@ static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) val = s->int_polarity; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); + val = ldq_le_p(&s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); + val = ldq_le_p(&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -129,7 +129,8 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, { LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint32_t offset; - uint64_t old, mask, data, *ptemp; + uint64_t old, mask, data; + void *ptemp; offset = addr & 7; addr -= offset; @@ -168,12 +169,12 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, s->int_polarity = (s->int_polarity & ~mask) | data; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); - *ptemp = (*ptemp & ~mask) | data; + ptemp = &s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); - *ptemp = (*ptemp & ~mask) | data; + ptemp = (uint64_t *)&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; default: qemu_log_mask(LOG_GUEST_ERROR, diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 8f4c9fd52e..1eed5125d1 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -190,7 +190,7 @@ static void qemu_s390_flic_notify(uint32_t type) CPU_FOREACH(cs) { S390CPU *cpu = S390_CPU(cs); - cs->interrupt_request |= CPU_INTERRUPT_HARD; + cpu_set_interrupt(cs, CPU_INTERRUPT_HARD); /* ignore CPUs that are not sleeping */ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING && diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index b15ada2078..31215b7785 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -46,6 +46,7 @@ #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" +#include "kvm/kvm_loongarch.h" static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 875fd00ef8..98cfe43c73 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -367,10 +367,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) +static void virt_machine_10_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + static void virt_machine_10_1_options(MachineClass *mc) { + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 6331997d56..51da226fcd 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -105,7 +105,7 @@ static void openrisc_timer_cb(void *opaque) CPUState *cs = CPU(cpu); cpu->env.ttmr |= TTMR_IP; - cs->interrupt_request |= CPU_INTERRUPT_TIMER; + cpu_set_interrupt(cs, CPU_INTERRUPT_TIMER); } switch (cpu->env.ttmr & TTMR_M) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 1855a3cd8d..eb22333404 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4762,14 +4762,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* + * pseries-10.2 + */ +static void spapr_machine_10_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 2); + +/* * pseries-10.1 */ static void spapr_machine_10_1_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_SPAPR_MACHINE_AS_LATEST(10, 1); +DEFINE_SPAPR_MACHINE(10, 1); /* * pseries-10.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a79bd13275..d0c6e80cb0 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -911,14 +911,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_10_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 2); + static void ccw_machine_10_1_instance_options(MachineState *machine) { + ccw_machine_10_2_instance_options(machine); } static void ccw_machine_10_1_class_options(MachineClass *mc) { + ccw_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(10, 1); +DEFINE_CCW_MACHINE(10, 1); static void ccw_machine_10_0_instance_options(MachineState *machine) { diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index cb48cc151f..789a31d0a0 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -38,6 +38,8 @@ #include "hw/timer/i8254.h" #include "system/address-spaces.h" #include "qom/object.h" +#include "qemu/lockable.h" +#include "qemu/seqlock.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -69,9 +71,11 @@ struct HPETState { SysBusDevice parent_obj; /*< public >*/ + QemuMutex lock; MemoryRegion iomem; uint64_t hpet_offset; bool hpet_offset_saved; + QemuSeqLock state_version; qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; uint8_t rtc_irq_level; @@ -428,6 +432,25 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read(addr); addr &= ~4; + if (addr == HPET_COUNTER) { + unsigned version; + + /* + * Write update is rare, so busywait here is unlikely to happen + */ + do { + version = seqlock_read_begin(&s->state_version); + if (unlikely(!hpet_enabled(s))) { + cur_tick = s->hpet_counter; + } else { + cur_tick = hpet_get_ticks(s); + } + } while (seqlock_read_retry(&s->state_version, version)); + trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); + return cur_tick >> shift; + } + + QEMU_LOCK_GUARD(&s->lock); /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -435,14 +458,6 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, return s->capability >> shift; case HPET_CFG: return s->config >> shift; - case HPET_COUNTER: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); - return cur_tick >> shift; case HPET_STATUS: return s->isr >> shift; default: @@ -482,6 +497,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, int len = MIN(size * 8, 64 - shift); uint64_t old_val, new_val, cleared; + QEMU_LOCK_GUARD(&s->lock); trace_hpet_ram_write(addr, value); addr &= ~4; @@ -494,6 +510,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, old_val = s->config; new_val = deposit64(old_val, shift, len, value); new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + seqlock_write_begin(&s->state_version); s->config = new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ @@ -512,6 +529,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, hpet_del_timer(&s->timer[i]); } } + seqlock_write_end(&s->state_version); + /* i8254 and RTC output pins are disabled * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { @@ -679,8 +698,11 @@ static void hpet_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); HPETState *s = HPET(obj); + qemu_mutex_init(&s->lock); + seqlock_init(&s->state_version); /* HPET Area */ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); + memory_region_enable_lockless_io(&s->iomem); sysbus_init_mmio(sbd, &s->iomem); } diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 2275d3fe39..2c03d49f97 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -885,7 +885,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) sioc = qio_channel_socket_new(); ioc = QIO_CHANNEL(sioc); - if (qio_channel_socket_connect_sync(sioc, addr, errp)) { + if (qio_channel_socket_connect_sync(sioc, addr, errp) < 0) { object_unref(OBJECT(ioc)); return NULL; } diff --git a/include/accel/tcg/cpu-ldst-common.h b/include/accel/tcg/cpu-ldst-common.h index 8bf17c2fab..17a3250ded 100644 --- a/include/accel/tcg/cpu-ldst-common.h +++ b/include/accel/tcg/cpu-ldst-common.h @@ -100,9 +100,6 @@ GEN_ATOMIC_HELPER_ALL(umax_fetch) GEN_ATOMIC_HELPER_ALL(xchg) -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); @@ -110,6 +107,16 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); +GEN_ATOMIC_HELPER(xchg, Int128, o_le) +GEN_ATOMIC_HELPER(xchg, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_be) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h index 4f4c8bbebc..46eda3403a 100644 --- a/include/hw/arm/stm32f205_soc.h +++ b/include/hw/arm/stm32f205_soc.h @@ -59,7 +59,7 @@ struct STM32F205State { STM32F2XXADCState adc[STM_NUM_ADCS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; - OrIRQState *adc_irqs; + OrIRQState adc_irqs; MemoryRegion sram; MemoryRegion flash; diff --git a/include/hw/boards.h b/include/hw/boards.h index f94713e6e2..665b620121 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -779,6 +779,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_10_1[]; +extern const size_t hw_compat_10_1_len; + extern GlobalProperty hw_compat_10_0[]; extern const size_t hw_compat_10_0_len; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5eaf41a566..b01a0cffd6 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -423,6 +423,7 @@ struct qemu_work_item; * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. * @interrupt_request: Indicates a pending interrupt request. + * Only used by system emulation. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. @@ -943,6 +944,28 @@ CPUState *cpu_by_arch_id(int64_t id); void cpu_interrupt(CPUState *cpu, int mask); /** + * cpu_test_interrupt: + * @cpu: The CPU to check interrupt(s) on. + * @mask: The interrupts to check. + * + * Checks if any of interrupts in @mask are pending on @cpu. + */ +static inline bool cpu_test_interrupt(CPUState *cpu, int mask) +{ + return qatomic_load_acquire(&cpu->interrupt_request) & mask; +} + +/** + * cpu_set_interrupt: + * @cpu: The CPU to set pending interrupt(s) on. + * @mask: The interrupts to set. + * + * Sets interrupts in @mask as pending on @cpu. Unlike @cpu_interrupt, + * this does not kick the vCPU. + */ +void cpu_set_interrupt(CPUState *cpu, int mask); + +/** * cpu_set_pc: * @cpu: The CPU to set the program counter for. * @addr: Program counter value. diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 79b72c54dd..e83157ab35 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -214,6 +214,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_10_1[]; +extern const size_t pc_compat_10_1_len; + extern GlobalProperty pc_compat_10_0[]; extern const size_t pc_compat_10_0_len; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index c18503869f..572d971d22 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -27,6 +27,7 @@ #include "hw/sysbus.h" #include "hw/intc/arm_gic_common.h" #include "qom/object.h" +#include "qemu/notify.h" /* * Maximum number of possible interrupts, determined by the GIC architecture. @@ -271,6 +272,8 @@ struct GICv3State { GICv3CPUState *cpu; /* List of all ITSes connected to this GIC */ GPtrArray *itslist; + + NotifierWithReturn cpr_notifier; }; #define GICV3_BITMAP_ACCESSORS(BMP) \ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 96fe51bc39..be3460b32f 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -133,6 +133,14 @@ QEMU_EXTERN_C int daemon(int, int); #include <setjmp.h> #include <signal.h> +/* + * Avoid conflict with linux/arch/powerpc/include/uapi/asm/elf.h, included + * from <asm/sigcontext.h>, but we might as well do this unconditionally. + */ +#undef ELF_CLASS +#undef ELF_DATA +#undef ELF_ARCH + #ifdef CONFIG_IOVEC #include <sys/uio.h> #endif diff --git a/include/system/kvm.h b/include/system/kvm.h index 3c7d314736..4fc09e3891 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -318,23 +318,6 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); bool kvm_device_supported(int vmfd, uint64_t type); /** - * kvm_park_vcpu - Park QEMU KVM vCPU context - * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. - * - * @returns: none - */ -void kvm_park_vcpu(CPUState *cpu); - -/** - * kvm_unpark_vcpu - unpark QEMU KVM vCPU context - * @s: KVM State - * @vcpu_id: Architecture vCPU ID of the parked vCPU - * - * @returns: KVM fd - */ -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); - -/** * kvm_create_and_park_vcpu - Create and park a KVM vCPU * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. * diff --git a/include/system/memory.h b/include/system/memory.h index e2cd6ed126..aa85fc27a1 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -833,6 +833,7 @@ struct MemoryRegion { bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; + bool lockless_io; bool unmergeable; uint8_t dirty_log_mask; bool is_iommu; @@ -2342,6 +2343,17 @@ void memory_region_set_flush_coalesced(MemoryRegion *mr); void memory_region_clear_flush_coalesced(MemoryRegion *mr); /** + * memory_region_enable_lockless_io: Enable lockless (BQL free) acceess. + * + * Enable BQL-free access for devices that are well prepared to handle + * locking during I/O themselves: either by doing fine grained locking or + * by providing lock-free I/O schemes. + * + * @mr: the memory region to be updated. + */ +void memory_region_enable_lockless_io(MemoryRegion *mr); + +/** * memory_region_add_eventfd: Request an eventfd to be triggered when a word * is written to a location. * diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index e1071adebf..f752ef440b 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -344,6 +344,8 @@ void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); @@ -411,6 +413,11 @@ void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); + /* Vector ops */ void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index c912578fdd..232733cb71 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -134,13 +134,16 @@ DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i128) DEF_ATOMIC2(tcg_gen_atomic_xchg, i32) DEF_ATOMIC2(tcg_gen_atomic_xchg, i64) +DEF_ATOMIC2(tcg_gen_atomic_xchg, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i32) diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index ad8a1d711f..346e37ede8 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -81,8 +81,4 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ target_exception_dump(env, fmt, code) -typedef struct target_pt_regs target_pt_regs; - -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); - #endif diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index fea43cefa6..4c4921152e 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -137,13 +137,10 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { + CPUARMState *env = cpu_env(cs); ARMCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - TaskState *ts = get_task_state(cs); - struct image_info *info = ts->info; - int i; if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { fprintf(stderr, @@ -151,14 +148,12 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) exit(EXIT_FAILURE); } - for (i = 0; i < 31; i++) { - env->xregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - env->xregs[31] = regs->sp; + env->pc = info->entry & ~0x3ULL; + env->xregs[31] = info->start_stack; + #if TARGET_BIG_ENDIAN env->cp15.sctlr_el[1] |= SCTLR_E0E; - for (i = 1; i < 4; ++i) { + for (int i = 1; i < 4; ++i) { env->cp15.sctlr_el[i] |= SCTLR_EE; } arm_rebuild_hflags(env); @@ -167,9 +162,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) if (cpu_isar_feature(aa64_pauth, cpu)) { qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } - - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c new file mode 100644 index 0000000000..77d03b50e1 --- /dev/null +++ b/linux-user/aarch64/elfload.c @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu.h" +#include "loader.h" +#include "target/arm/cpu-features.h" +#include "target_elf.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, + ARM_HWCAP_A64_ATOMICS = 1 << 8, + ARM_HWCAP_A64_FPHP = 1 << 9, + ARM_HWCAP_A64_ASIMDHP = 1 << 10, + ARM_HWCAP_A64_CPUID = 1 << 11, + ARM_HWCAP_A64_ASIMDRDM = 1 << 12, + ARM_HWCAP_A64_JSCVT = 1 << 13, + ARM_HWCAP_A64_FCMA = 1 << 14, + ARM_HWCAP_A64_LRCPC = 1 << 15, + ARM_HWCAP_A64_DCPOP = 1 << 16, + ARM_HWCAP_A64_SHA3 = 1 << 17, + ARM_HWCAP_A64_SM3 = 1 << 18, + ARM_HWCAP_A64_SM4 = 1 << 19, + ARM_HWCAP_A64_ASIMDDP = 1 << 20, + ARM_HWCAP_A64_SHA512 = 1 << 21, + ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1ULL << 31, + ARM_HWCAP_A64_GCS = 1ULL << 32, + ARM_HWCAP_A64_CMPBR = 1ULL << 33, + ARM_HWCAP_A64_FPRCVT = 1ULL << 34, + ARM_HWCAP_A64_F8MM8 = 1ULL << 35, + ARM_HWCAP_A64_F8MM4 = 1ULL << 36, + ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, + ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, + ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, + ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, + ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, + ARM_HWCAP_A64_SME2P2 = 1ULL << 42, + ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, + ARM_HWCAP_A64_SME_AES = 1ULL << 44, + ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, + ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, + ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, + + ARM_HWCAP2_A64_DCPODP = 1 << 0, + ARM_HWCAP2_A64_SVE2 = 1 << 1, + ARM_HWCAP2_A64_SVEAES = 1 << 2, + ARM_HWCAP2_A64_SVEPMULL = 1 << 3, + ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, + ARM_HWCAP2_A64_SVESHA3 = 1 << 5, + ARM_HWCAP2_A64_SVESM4 = 1 << 6, + ARM_HWCAP2_A64_FLAGM2 = 1 << 7, + ARM_HWCAP2_A64_FRINT = 1 << 8, + ARM_HWCAP2_A64_SVEI8MM = 1 << 9, + ARM_HWCAP2_A64_SVEF32MM = 1 << 10, + ARM_HWCAP2_A64_SVEF64MM = 1 << 11, + ARM_HWCAP2_A64_SVEBF16 = 1 << 12, + ARM_HWCAP2_A64_I8MM = 1 << 13, + ARM_HWCAP2_A64_BF16 = 1 << 14, + ARM_HWCAP2_A64_DGH = 1 << 15, + ARM_HWCAP2_A64_RNG = 1 << 16, + ARM_HWCAP2_A64_BTI = 1 << 17, + ARM_HWCAP2_A64_MTE = 1 << 18, + ARM_HWCAP2_A64_ECV = 1 << 19, + ARM_HWCAP2_A64_AFP = 1 << 20, + ARM_HWCAP2_A64_RPRES = 1 << 21, + ARM_HWCAP2_A64_MTE3 = 1 << 22, + ARM_HWCAP2_A64_SME = 1 << 23, + ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, + ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, + ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, + ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, + ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, + ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, + ARM_HWCAP2_A64_SME_FA64 = 1 << 30, + ARM_HWCAP2_A64_WFXT = 1ULL << 31, + ARM_HWCAP2_A64_EBF16 = 1ULL << 32, + ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, + ARM_HWCAP2_A64_CSSC = 1ULL << 34, + ARM_HWCAP2_A64_RPRFM = 1ULL << 35, + ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, + ARM_HWCAP2_A64_SME2 = 1ULL << 37, + ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, + ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, + ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, + ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, + ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, + ARM_HWCAP2_A64_MOPS = 1ULL << 43, + ARM_HWCAP2_A64_HBC = 1ULL << 44, + ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, + ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, + ARM_HWCAP2_A64_LSE128 = 1ULL << 47, + ARM_HWCAP2_A64_FPMR = 1ULL << 48, + ARM_HWCAP2_A64_LUT = 1ULL << 49, + ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, + ARM_HWCAP2_A64_F8CVT = 1ULL << 51, + ARM_HWCAP2_A64_F8FMA = 1ULL << 52, + ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, + ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, + ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, + ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, + ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, + ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, + ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, + ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, + ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, + ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, + ARM_HWCAP2_A64_POE = 1ULL << 63, +}; + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; + + /* probe for the extra features */ + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); + GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); + GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); + GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); + GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); + GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); + GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); + GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + + return hwcaps; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); + GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); + GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); + GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); + GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); + GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); + GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); + GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); + GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); + GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); + GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); + GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); + GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); + GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); + GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); + GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); + GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); + GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); + GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | + ARM_HWCAP2_A64_SME_F32F32 | + ARM_HWCAP2_A64_SME_B16F32 | + ARM_HWCAP2_A64_SME_F16F32 | + ARM_HWCAP2_A64_SME_I8I32)); + GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); + GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); + GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); + GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); + GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); + GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); + GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | + ARM_HWCAP2_A64_SME_I16I32 | + ARM_HWCAP2_A64_SME_BI32I32)); + GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); + GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); + GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); + GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); + GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC); + GET_FEATURE_ID(aa64_lse128, ARM_HWCAP2_A64_LSE128); + + return hwcaps; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", + [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", + [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", + [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", + [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", + [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", + [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", + [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", + [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", + [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", + [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", + [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", + [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", + [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", + [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", + [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", + [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", + [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", + [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", + [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", + [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", + [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", + [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", + [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", + [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", + [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", + [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", + [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", + [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", + [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", + [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", + [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", + [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", + [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", + [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", + [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", + [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", + [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", + [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", + [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", + [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", + [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", + [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", + [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", + [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", + [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", + [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", + [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", + [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", + [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", + [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", + [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", + [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", + [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *get_elf_platform(CPUState *cs) +{ + return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; +} + +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp) +{ + if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + if (pr_datasz != sizeof(uint32_t)) { + error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); + return false; + } + /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ + info->note_flags = *data; + } + return true; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 31; i++) { + r->pt.regs[i] = tswap64(env->xregs[i]); + } + r->pt.sp = tswap64(env->xregs[31]); + r->pt.pc = tswap64(env->pc); + r->pt.pstate = tswap64(pstate_read((CPUARMState *)env)); +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index a7eb962fba..4cdeb64b0d 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -7,8 +7,30 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + +#include "target_ptrace.h" + +#define ELF_MACHINE EM_AARCH64 +#define ELF_CLASS ELFCLASS64 + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 +#define HAVE_ELF_GNU_PROPERTY 1 + +/* + * See linux kernel: arch/arm64/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_pt_regs via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_user_pt_regs pt; +} target_elf_gregset_t; + +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif diff --git a/linux-user/aarch64/target_ptrace.h b/linux-user/aarch64/target_ptrace.h new file mode 100644 index 0000000000..10681338ba --- /dev/null +++ b/linux-user/aarch64/target_ptrace.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef AARCH64_TARGET_PTRACE_H +#define AARCH64_TARGET_PTRACE_H + +/* See arch/arm64/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#endif /* AARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index c055133725..bd05f6c7fe 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -1,13 +1,6 @@ #ifndef AARCH64_TARGET_SYSCALL_H #define AARCH64_TARGET_SYSCALL_H -struct target_pt_regs { - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; -}; - #if TARGET_BIG_ENDIAN #define UNAME_MACHINE "aarch64_be" #else diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 80ad536c5f..728b64906d 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -173,13 +173,10 @@ void cpu_loop(CPUAlphaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 28; i++) { - env->ir[i] = ((abi_ulong *)regs)[i]; - } - env->ir[IR_SP] = regs->usp; - env->pc = regs->pc; + env->pc = info->entry; + env->ir[IR_SP] = info->start_stack; } diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c new file mode 100644 index 0000000000..1e44475c47 --- /dev/null +++ b/linux-user/alpha/elfload.c @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "ev67"; +} diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index b77d638f6d..864dc6e2e6 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -7,8 +7,8 @@ #ifndef ALPHA_TARGET_ELF_H #define ALPHA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "ev67"; -} + +#define ELF_CLASS ELFCLASS64 +#define ELF_MACHINE EM_ALPHA + #endif diff --git a/linux-user/alpha/target_syscall.h b/linux-user/alpha/target_syscall.h index fda3a49f29..53706b749f 100644 --- a/linux-user/alpha/target_syscall.h +++ b/linux-user/alpha/target_syscall.h @@ -1,46 +1,6 @@ #ifndef ALPHA_TARGET_SYSCALL_H #define ALPHA_TARGET_SYSCALL_H -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong r0; - abi_ulong r1; - abi_ulong r2; - abi_ulong r3; - abi_ulong r4; - abi_ulong r5; - abi_ulong r6; - abi_ulong r7; - abi_ulong r8; - abi_ulong r19; - abi_ulong r20; - abi_ulong r21; - abi_ulong r22; - abi_ulong r23; - abi_ulong r24; - abi_ulong r25; - abi_ulong r26; - abi_ulong r27; - abi_ulong r28; - abi_ulong hae; -/* JRP - These are the values provided to a0-a2 by PALcode */ - abi_ulong trap_a0; - abi_ulong trap_a1; - abi_ulong trap_a2; -/* These are saved by PAL-code: */ - abi_ulong ps; - abi_ulong pc; - abi_ulong gp; - abi_ulong r16; - abi_ulong r17; - abi_ulong r18; -/* Those is needed by qemu to temporary store the user stack pointer */ - abi_ulong usp; - abi_ulong unique; -}; - #define UNAME_MACHINE "alpha" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 33f63951a9..9aeb9b0087 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -480,32 +480,57 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; - - cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, - CPSRWriteByInstr); - for(i = 0; i < 16; i++) { - env->regs[i] = regs->uregs[i]; - } -#if TARGET_BIG_ENDIAN - /* Enable BE8. */ - if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 - && (info->elf_flags & EF_ARM_BE8)) { - env->uncached_cpsr |= CPSR_E; - env->cp15.sctlr_el[1] |= SCTLR_E0E; - } else { - env->cp15.sctlr_el[1] |= SCTLR_B; + CPUARMState *env = cpu_env(cs); + abi_ptr stack = info->start_stack; + abi_ptr entry = info->entry; + + cpsr_write(env, ARM_CPU_MODE_USR | (entry & 1 ? CPSR_T : 0), + CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); + + env->regs[15] = entry & 0xfffffffe; + env->regs[13] = stack; + + /* + * Per the SVR4 ABI, r0 contains a pointer to a function to be + * registered with atexit. A value of 0 means we have no such handler. + */ + env->regs[0] = 0; + + /* For uClinux PIC binaries. */ + /* XXX: Linux does this only on ARM with no MMU (do we care?) */ + env->regs[10] = info->start_data; + + /* Support ARM FDPIC. */ + if (info_is_fdpic(info)) { + /* + * As described in the ABI document, r7 points to the loadmap info + * prepared by the kernel. If an interpreter is needed, r8 points + * to the interpreter loadmap and r9 points to the interpreter + * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and + * r9 points to the main program PT_DYNAMIC info. + */ + env->regs[7] = info->loadmap_addr; + if (info->interpreter_loadmap_addr) { + /* Executable is dynamically loaded. */ + env->regs[8] = info->interpreter_loadmap_addr; + env->regs[9] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[8] = 0; + env->regs[9] = info->pt_dynamic_addr; + } } - arm_rebuild_hflags(env); -#endif - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; + if (TARGET_BIG_ENDIAN) { + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; + } + arm_rebuild_hflags(env); + } } diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c new file mode 100644 index 0000000000..308ed23fcb --- /dev/null +++ b/linux-user/arm/elfload.c @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "user-internals.h" +#include "target_elf.h" +#include "target/arm/cpu-features.h" +#include "target_elf.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} + +enum +{ + ARM_HWCAP_ARM_SWP = 1 << 0, + ARM_HWCAP_ARM_HALF = 1 << 1, + ARM_HWCAP_ARM_THUMB = 1 << 2, + ARM_HWCAP_ARM_26BIT = 1 << 3, + ARM_HWCAP_ARM_FAST_MULT = 1 << 4, + ARM_HWCAP_ARM_FPA = 1 << 5, + ARM_HWCAP_ARM_VFP = 1 << 6, + ARM_HWCAP_ARM_EDSP = 1 << 7, + ARM_HWCAP_ARM_JAVA = 1 << 8, + ARM_HWCAP_ARM_IWMMXT = 1 << 9, + ARM_HWCAP_ARM_CRUNCH = 1 << 10, + ARM_HWCAP_ARM_THUMBEE = 1 << 11, + ARM_HWCAP_ARM_NEON = 1 << 12, + ARM_HWCAP_ARM_VFPv3 = 1 << 13, + ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, + ARM_HWCAP_ARM_TLS = 1 << 15, + ARM_HWCAP_ARM_VFPv4 = 1 << 16, + ARM_HWCAP_ARM_IDIVA = 1 << 17, + ARM_HWCAP_ARM_IDIVT = 1 << 18, + ARM_HWCAP_ARM_VFPD32 = 1 << 19, + ARM_HWCAP_ARM_LPAE = 1 << 20, + ARM_HWCAP_ARM_EVTSTRM = 1 << 21, + ARM_HWCAP_ARM_FPHP = 1 << 22, + ARM_HWCAP_ARM_ASIMDHP = 1 << 23, + ARM_HWCAP_ARM_ASIMDDP = 1 << 24, + ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, + ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, + ARM_HWCAP_ARM_I8MM = 1 << 27, +}; + +enum { + ARM_HWCAP2_ARM_AES = 1 << 0, + ARM_HWCAP2_ARM_PMULL = 1 << 1, + ARM_HWCAP2_ARM_SHA1 = 1 << 2, + ARM_HWCAP2_ARM_SHA2 = 1 << 3, + ARM_HWCAP2_ARM_CRC32 = 1 << 4, + ARM_HWCAP2_ARM_SB = 1 << 5, + ARM_HWCAP2_ARM_SSBS = 1 << 6, +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_ARM_SWP; + hwcaps |= ARM_HWCAP_ARM_HALF; + hwcaps |= ARM_HWCAP_ARM_THUMB; + hwcaps |= ARM_HWCAP_ARM_FAST_MULT; + + /* probe for the extra features */ +#define GET_FEATURE(feat, hwcap) \ + do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + + /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ + GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); + GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); + GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); + GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); + GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); + GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); + GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); + GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); + + if (cpu_isar_feature(aa32_fpsp_v3, cpu) || + cpu_isar_feature(aa32_fpdp_v3, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPv3; + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPD32; + } else { + hwcaps |= ARM_HWCAP_ARM_VFPv3D16; + } + } + GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); + /* + * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same + * isar_feature function for both. The kernel reports them as two hwcaps. + */ + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); + GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); + GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); + GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); + GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); + + return hwcaps; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); + GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); + GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); + GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); + GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); + GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); + GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); + return hwcaps; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", + [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", + [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", + [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", + [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", + [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", + [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", + [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", + [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", + [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", + [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", + [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", + [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", + [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", + [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", + [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", + [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", + [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", + [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *get_elf_platform(CPUState *cs) +{ + CPUARMState *env = cpu_env(cs); + +#if TARGET_BIG_ENDIAN +# define END "b" +#else +# define END "l" +#endif + + if (arm_feature(env, ARM_FEATURE_V8)) { + return "v8" END; + } else if (arm_feature(env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_M)) { + return "v7m" END; + } else { + return "v7" END; + } + } else if (arm_feature(env, ARM_FEATURE_V6)) { + return "v6" END; + } else if (arm_feature(env, ARM_FEATURE_V5)) { + return "v5" END; + } else { + return "v4" END; + } + +#undef END +} + +bool init_guest_commpage(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + int host_page_size = qemu_real_host_page_size(); + abi_ptr commpage; + void *want; + void *addr; + + /* + * M-profile allocates maximum of 2GB address space, so can never + * allocate the commpage. Skip it. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + return true; + } + + commpage = HI_COMMPAGE & -host_page_size; + want = g2h_untagged(commpage); + addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | + (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), + -1, 0); + + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + + /* Set kernel helper versions; rest of page is 0. */ + __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); + + if (mprotect(addr, host_page_size, PROT_READ)) { + perror("Protecting guest commpage"); + exit(EXIT_FAILURE); + } + + page_set_flags(commpage, commpage | (host_page_size - 1), + PAGE_READ | PAGE_EXEC | PAGE_VALID); + return true; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 16; ++i) { + r->pt.regs[i] = tswapal(env->regs[i]); + } + r->pt.cpsr = tswapal(cpsr_read((CPUARMState *)env)); + r->pt.orig_r0 = tswapal(env->regs[0]); /* FIXME */ +} + +#if TARGET_BIG_ENDIAN +# include "vdso-be8.c.inc" +# include "vdso-be32.c.inc" +#else +# include "vdso-le.c.inc" +#endif + +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ +#if TARGET_BIG_ENDIAN + return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 + && (elf_flags & EF_ARM_BE8) + ? &vdso_be8_image_info + : &vdso_be32_image_info); +#else + return &vdso_image_info; +#endif +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 58ff6a0986..12cdc8e5a7 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -7,8 +7,27 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + +#include "target_ptrace.h" + +#define ELF_MACHINE EM_ARM +#define ELF_CLASS ELFCLASS32 +#define EXSTACK_DEFAULT true + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 +#define HAVE_VDSO_IMAGE_INFO 1 + +#define HI_COMMPAGE ((intptr_t)0xffff0f00u) + +/* + * See linux kernel: arch/arm/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_pt_regs pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h index ac75af9ca6..a4cd6948c6 100644 --- a/linux-user/arm/target_proc.h +++ b/linux-user/arm/target_proc.h @@ -10,8 +10,8 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) { ARMCPU *cpu = env_archcpu(cpu_env); int arch, midr_rev, midr_part, midr_var, midr_impl; - target_ulong elf_hwcap = get_elf_hwcap(); - target_ulong elf_hwcap2 = get_elf_hwcap2(); + target_ulong elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); + target_ulong elf_hwcap2 = get_elf_hwcap2(env_cpu(cpu_env)); const char *elf_name; int num_cpus, len_part, len_var; diff --git a/linux-user/arm/target_ptrace.h b/linux-user/arm/target_ptrace.h new file mode 100644 index 0000000000..1610b8e03c --- /dev/null +++ b/linux-user/arm/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef ARM_TARGET_PTRACE_H +#define ARM_TARGET_PTRACE_H + +/* + * See arch/arm/include/uapi/asm/ptrace.h. + * Instead of an array and ARM_xx defines, use proper fields. + */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong cpsr; + abi_ulong orig_r0; +}; + +#endif /* ARM_TARGET_PTRACE_H */ diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 412ad434cf..8c4ddba717 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -1,14 +1,6 @@ #ifndef ARM_TARGET_SYSCALL_H #define ARM_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -/* uregs[0..15] are r0 to r15; uregs[16] is CPSR; uregs[17] is ORIG_r0 */ -struct target_pt_regs { - abi_long uregs[18]; -}; - #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ea214105ff..26c090c95d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -28,6 +28,7 @@ #include "qemu/lockable.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "target_elf.h" #include "target_signal.h" #include "tcg/debuginfo.h" @@ -35,29 +36,10 @@ #include "target/arm/cpu-features.h" #endif -#ifdef _ARCH_PPC64 -#undef ARCH_DLINFO -#undef ELF_PLATFORM -#undef ELF_HWCAP -#undef ELF_HWCAP2 -#undef ELF_CLASS -#undef ELF_DATA -#undef ELF_ARCH -#endif - #ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif -typedef struct { - const uint8_t *image; - const uint32_t *relocs; - unsigned image_size; - unsigned reloc_count; - unsigned sigreturn_ofs; - unsigned rt_sigreturn_ofs; -} VdsoImageInfo; - #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -123,30 +105,12 @@ int info_is_fdpic(struct image_info *info) return info->personality == PER_LINUX_FDPIC; } -/* this flag is uneffective under linux too, should be deleted */ -#ifndef MAP_DENYWRITE -#define MAP_DENYWRITE 0 -#endif - -/* should probably go in elf.h */ -#ifndef ELIBBAD -#define ELIBBAD 80 -#endif - #if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else #define ELF_DATA ELFDATA2LSB #endif -#ifdef TARGET_ABI_MIPSN32 -typedef abi_ullong target_elf_greg_t; -#define tswapreg(ptr) tswap64(ptr) -#else -typedef abi_ulong target_elf_greg_t; -#define tswapreg(ptr) tswapal(ptr) -#endif - #ifdef USE_UID16 typedef abi_ushort target_uid_t; typedef abi_ushort target_gid_t; @@ -156,1970 +120,14 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_I386 - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - X86CPU *cpu = X86_CPU(thread_cpu); - - return cpu->env.features[FEAT_1_EDX]; -} - -#ifdef TARGET_X86_64 -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_X86_64 - -#define ELF_PLATFORM "x86_64" - -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->rax = 0; - regs->rsp = infop->start_stack; - regs->rip = infop->entry; -} - -#define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* - * Note that ELF_NREG should be 29 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) -{ - (*regs)[0] = tswapreg(env->regs[15]); - (*regs)[1] = tswapreg(env->regs[14]); - (*regs)[2] = tswapreg(env->regs[13]); - (*regs)[3] = tswapreg(env->regs[12]); - (*regs)[4] = tswapreg(env->regs[R_EBP]); - (*regs)[5] = tswapreg(env->regs[R_EBX]); - (*regs)[6] = tswapreg(env->regs[11]); - (*regs)[7] = tswapreg(env->regs[10]); - (*regs)[8] = tswapreg(env->regs[9]); - (*regs)[9] = tswapreg(env->regs[8]); - (*regs)[10] = tswapreg(env->regs[R_EAX]); - (*regs)[11] = tswapreg(env->regs[R_ECX]); - (*regs)[12] = tswapreg(env->regs[R_EDX]); - (*regs)[13] = tswapreg(env->regs[R_ESI]); - (*regs)[14] = tswapreg(env->regs[R_EDI]); - (*regs)[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[16] = tswapreg(env->eip); - (*regs)[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[18] = tswapreg(env->eflags); - (*regs)[19] = tswapreg(env->regs[R_ESP]); - (*regs)[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - (*regs)[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[26] = tswapreg(env->segs[R_GS].selector & 0xffff); -} - -#if ULONG_MAX > UINT32_MAX -#define INIT_GUEST_COMMPAGE -static bool init_guest_commpage(void) -{ - /* - * The vsyscall page is at a high negative address aka kernel space, - * which means that we cannot actually allocate it with target_mmap. - * We still should be able to use page_set_flags, unless the user - * has specified -R reserved_va, which would trigger an assert(). - */ - if (reserved_va != 0 && - TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { - error_report("Cannot allocate vsyscall page"); - exit(EXIT_FAILURE); - } - page_set_flags(TARGET_VSYSCALL_PAGE, - TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} -#endif -#else - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) ) - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_386 - -#define ELF_PLATFORM get_elf_platform() -#define EXSTACK_DEFAULT true - -static const char *get_elf_platform(void) -{ - static char elf_platform[] = "i386"; - int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); - if (family > 6) { - family = 6; - } - if (family >= 3) { - elf_platform[1] = '0' + family; - } - return elf_platform; -} - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->esp = infop->start_stack; - regs->eip = infop->entry; - - /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program - starts %edx contains a pointer to a function which might be - registered using `atexit'. This provides a mean for the - dynamic linker to call DT_FINI functions for shared libraries - that have been loaded before the code runs. - - A value of 0 tells we have no such handler. */ - regs->edx = 0; -} - -#define ELF_NREG 17 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* - * Note that ELF_NREG should be 19 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) -{ - (*regs)[0] = tswapreg(env->regs[R_EBX]); - (*regs)[1] = tswapreg(env->regs[R_ECX]); - (*regs)[2] = tswapreg(env->regs[R_EDX]); - (*regs)[3] = tswapreg(env->regs[R_ESI]); - (*regs)[4] = tswapreg(env->regs[R_EDI]); - (*regs)[5] = tswapreg(env->regs[R_EBP]); - (*regs)[6] = tswapreg(env->regs[R_EAX]); - (*regs)[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[12] = tswapreg(env->eip); - (*regs)[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[14] = tswapreg(env->eflags); - (*regs)[15] = tswapreg(env->regs[R_ESP]); - (*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff); -} - -/* - * i386 is the only target which supplies AT_SYSINFO for the vdso. - * All others only supply AT_SYSINFO_EHDR. - */ -#define DLINFO_ARCH_ITEMS (vdso_info != NULL) -#define ARCH_DLINFO \ - do { \ - if (vdso_info) { \ - NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ - } \ - } while (0) - -#endif /* TARGET_X86_64 */ - -#define VDSO_HEADER "vdso.c.inc" - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#endif /* TARGET_I386 */ - -#ifdef TARGET_ARM - -#ifndef TARGET_AARCH64 -/* 32 bit ARM definitions */ - -#define ELF_ARCH EM_ARM -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->uregs[16] = ARM_CPU_MODE_USR; - if (infop->entry & 1) { - regs->uregs[16] |= CPSR_T; - } - regs->uregs[15] = infop->entry & 0xfffffffe; - regs->uregs[13] = infop->start_stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->uregs[2], stack + 8); /* envp */ - get_user_ual(regs->uregs[1], stack + 4); /* envp */ - /* XXX: it seems that r0 is zeroed after ! */ - regs->uregs[0] = 0; - /* For uClinux PIC binaries. */ - /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ - regs->uregs[10] = infop->start_data; - - /* Support ARM FDPIC. */ - if (info_is_fdpic(infop)) { - /* As described in the ABI document, r7 points to the loadmap info - * prepared by the kernel. If an interpreter is needed, r8 points - * to the interpreter loadmap and r9 points to the interpreter - * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and - * r9 points to the main program PT_DYNAMIC info. - */ - regs->uregs[7] = infop->loadmap_addr; - if (infop->interpreter_loadmap_addr) { - /* Executable is dynamically loaded. */ - regs->uregs[8] = infop->interpreter_loadmap_addr; - regs->uregs[9] = infop->interpreter_pt_dynamic_addr; - } else { - regs->uregs[8] = 0; - regs->uregs[9] = infop->pt_dynamic_addr; - } - } -} - -#define ELF_NREG 18 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUARMState *env) -{ - (*regs)[0] = tswapreg(env->regs[0]); - (*regs)[1] = tswapreg(env->regs[1]); - (*regs)[2] = tswapreg(env->regs[2]); - (*regs)[3] = tswapreg(env->regs[3]); - (*regs)[4] = tswapreg(env->regs[4]); - (*regs)[5] = tswapreg(env->regs[5]); - (*regs)[6] = tswapreg(env->regs[6]); - (*regs)[7] = tswapreg(env->regs[7]); - (*regs)[8] = tswapreg(env->regs[8]); - (*regs)[9] = tswapreg(env->regs[9]); - (*regs)[10] = tswapreg(env->regs[10]); - (*regs)[11] = tswapreg(env->regs[11]); - (*regs)[12] = tswapreg(env->regs[12]); - (*regs)[13] = tswapreg(env->regs[13]); - (*regs)[14] = tswapreg(env->regs[14]); - (*regs)[15] = tswapreg(env->regs[15]); - - (*regs)[16] = tswapreg(cpsr_read((CPUARMState *)env)); - (*regs)[17] = tswapreg(env->regs[0]); /* XXX */ -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -enum -{ - ARM_HWCAP_ARM_SWP = 1 << 0, - ARM_HWCAP_ARM_HALF = 1 << 1, - ARM_HWCAP_ARM_THUMB = 1 << 2, - ARM_HWCAP_ARM_26BIT = 1 << 3, - ARM_HWCAP_ARM_FAST_MULT = 1 << 4, - ARM_HWCAP_ARM_FPA = 1 << 5, - ARM_HWCAP_ARM_VFP = 1 << 6, - ARM_HWCAP_ARM_EDSP = 1 << 7, - ARM_HWCAP_ARM_JAVA = 1 << 8, - ARM_HWCAP_ARM_IWMMXT = 1 << 9, - ARM_HWCAP_ARM_CRUNCH = 1 << 10, - ARM_HWCAP_ARM_THUMBEE = 1 << 11, - ARM_HWCAP_ARM_NEON = 1 << 12, - ARM_HWCAP_ARM_VFPv3 = 1 << 13, - ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, - ARM_HWCAP_ARM_TLS = 1 << 15, - ARM_HWCAP_ARM_VFPv4 = 1 << 16, - ARM_HWCAP_ARM_IDIVA = 1 << 17, - ARM_HWCAP_ARM_IDIVT = 1 << 18, - ARM_HWCAP_ARM_VFPD32 = 1 << 19, - ARM_HWCAP_ARM_LPAE = 1 << 20, - ARM_HWCAP_ARM_EVTSTRM = 1 << 21, - ARM_HWCAP_ARM_FPHP = 1 << 22, - ARM_HWCAP_ARM_ASIMDHP = 1 << 23, - ARM_HWCAP_ARM_ASIMDDP = 1 << 24, - ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, - ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, - ARM_HWCAP_ARM_I8MM = 1 << 27, -}; - -enum { - ARM_HWCAP2_ARM_AES = 1 << 0, - ARM_HWCAP2_ARM_PMULL = 1 << 1, - ARM_HWCAP2_ARM_SHA1 = 1 << 2, - ARM_HWCAP2_ARM_SHA2 = 1 << 3, - ARM_HWCAP2_ARM_CRC32 = 1 << 4, - ARM_HWCAP2_ARM_SB = 1 << 5, - ARM_HWCAP2_ARM_SSBS = 1 << 6, -}; - -/* The commpage only exists for 32 bit kernels */ - -#define HI_COMMPAGE (intptr_t)0xffff0f00u - -static bool init_guest_commpage(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - int host_page_size = qemu_real_host_page_size(); - abi_ptr commpage; - void *want; - void *addr; - - /* - * M-profile allocates maximum of 2GB address space, so can never - * allocate the commpage. Skip it. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_M)) { - return true; - } - - commpage = HI_COMMPAGE & -host_page_size; - want = g2h_untagged(commpage); - addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | - (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), - -1, 0); - - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - - /* Set kernel helper versions; rest of page is 0. */ - __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); - - if (mprotect(addr, host_page_size, PROT_READ)) { - perror("Protecting guest commpage"); - exit(EXIT_FAILURE); - } - - page_set_flags(commpage, commpage | (host_page_size - 1), - PAGE_READ | PAGE_EXEC | PAGE_VALID); - return true; -} - -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_ARM_SWP; - hwcaps |= ARM_HWCAP_ARM_HALF; - hwcaps |= ARM_HWCAP_ARM_THUMB; - hwcaps |= ARM_HWCAP_ARM_FAST_MULT; - - /* probe for the extra features */ -#define GET_FEATURE(feat, hwcap) \ - do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - - /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ - GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); - GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); - GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); - GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); - GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); - GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); - GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); - GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); - - if (cpu_isar_feature(aa32_fpsp_v3, cpu) || - cpu_isar_feature(aa32_fpdp_v3, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPv3; - if (cpu_isar_feature(aa32_simd_r32, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPD32; - } else { - hwcaps |= ARM_HWCAP_ARM_VFPv3D16; - } - } - GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); - /* - * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same - * isar_feature function for both. The kernel reports them as two hwcaps. - */ - GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); - GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); - GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); - GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); - GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); - GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); - - return hwcaps; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); - GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); - GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); - GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); - GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); - GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); - GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); - return hwcaps; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", - [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", - [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", - [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", - [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", - [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", - [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", - [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", - [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", - [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", - [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", - [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", - [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", - [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", - [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", - [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", - [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", - [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", - [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", - [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", - [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", - [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", - [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", - [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -#undef GET_FEATURE -#undef GET_FEATURE_ID - -#define ELF_PLATFORM get_elf_platform() - -static const char *get_elf_platform(void) -{ - CPUARMState *env = cpu_env(thread_cpu); - -#if TARGET_BIG_ENDIAN -# define END "b" -#else -# define END "l" -#endif - - if (arm_feature(env, ARM_FEATURE_V8)) { - return "v8" END; - } else if (arm_feature(env, ARM_FEATURE_V7)) { - if (arm_feature(env, ARM_FEATURE_M)) { - return "v7m" END; - } else { - return "v7" END; - } - } else if (arm_feature(env, ARM_FEATURE_V6)) { - return "v6" END; - } else if (arm_feature(env, ARM_FEATURE_V5)) { - return "v5" END; - } else { - return "v4" END; - } - -#undef END -} - -#if TARGET_BIG_ENDIAN -#include "elf.h" -#include "vdso-be8.c.inc" -#include "vdso-be32.c.inc" - -static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) -{ - return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 - && (elf_flags & EF_ARM_BE8) - ? &vdso_be8_image_info - : &vdso_be32_image_info); -} -#define vdso_image_info vdso_image_info -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - -#else -/* 64 bit ARM definitions */ - -#define ELF_ARCH EM_AARCH64 -#define ELF_CLASS ELFCLASS64 -#if TARGET_BIG_ENDIAN -# define ELF_PLATFORM "aarch64_be" -#else -# define ELF_PLATFORM "aarch64" -#endif - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->pc = infop->entry & ~0x3ULL; - regs->sp = stack; -} - -#define ELF_NREG 34 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUARMState *env) -{ - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(env->xregs[i]); - } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(pstate_read((CPUARMState *)env)); -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -enum { - ARM_HWCAP_A64_FP = 1 << 0, - ARM_HWCAP_A64_ASIMD = 1 << 1, - ARM_HWCAP_A64_EVTSTRM = 1 << 2, - ARM_HWCAP_A64_AES = 1 << 3, - ARM_HWCAP_A64_PMULL = 1 << 4, - ARM_HWCAP_A64_SHA1 = 1 << 5, - ARM_HWCAP_A64_SHA2 = 1 << 6, - ARM_HWCAP_A64_CRC32 = 1 << 7, - ARM_HWCAP_A64_ATOMICS = 1 << 8, - ARM_HWCAP_A64_FPHP = 1 << 9, - ARM_HWCAP_A64_ASIMDHP = 1 << 10, - ARM_HWCAP_A64_CPUID = 1 << 11, - ARM_HWCAP_A64_ASIMDRDM = 1 << 12, - ARM_HWCAP_A64_JSCVT = 1 << 13, - ARM_HWCAP_A64_FCMA = 1 << 14, - ARM_HWCAP_A64_LRCPC = 1 << 15, - ARM_HWCAP_A64_DCPOP = 1 << 16, - ARM_HWCAP_A64_SHA3 = 1 << 17, - ARM_HWCAP_A64_SM3 = 1 << 18, - ARM_HWCAP_A64_SM4 = 1 << 19, - ARM_HWCAP_A64_ASIMDDP = 1 << 20, - ARM_HWCAP_A64_SHA512 = 1 << 21, - ARM_HWCAP_A64_SVE = 1 << 22, - ARM_HWCAP_A64_ASIMDFHM = 1 << 23, - ARM_HWCAP_A64_DIT = 1 << 24, - ARM_HWCAP_A64_USCAT = 1 << 25, - ARM_HWCAP_A64_ILRCPC = 1 << 26, - ARM_HWCAP_A64_FLAGM = 1 << 27, - ARM_HWCAP_A64_SSBS = 1 << 28, - ARM_HWCAP_A64_SB = 1 << 29, - ARM_HWCAP_A64_PACA = 1 << 30, - ARM_HWCAP_A64_PACG = 1ULL << 31, - ARM_HWCAP_A64_GCS = 1ULL << 32, - ARM_HWCAP_A64_CMPBR = 1ULL << 33, - ARM_HWCAP_A64_FPRCVT = 1ULL << 34, - ARM_HWCAP_A64_F8MM8 = 1ULL << 35, - ARM_HWCAP_A64_F8MM4 = 1ULL << 36, - ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, - ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, - ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, - ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, - ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, - ARM_HWCAP_A64_SME2P2 = 1ULL << 42, - ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, - ARM_HWCAP_A64_SME_AES = 1ULL << 44, - ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, - ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, - ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, - - ARM_HWCAP2_A64_DCPODP = 1 << 0, - ARM_HWCAP2_A64_SVE2 = 1 << 1, - ARM_HWCAP2_A64_SVEAES = 1 << 2, - ARM_HWCAP2_A64_SVEPMULL = 1 << 3, - ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, - ARM_HWCAP2_A64_SVESHA3 = 1 << 5, - ARM_HWCAP2_A64_SVESM4 = 1 << 6, - ARM_HWCAP2_A64_FLAGM2 = 1 << 7, - ARM_HWCAP2_A64_FRINT = 1 << 8, - ARM_HWCAP2_A64_SVEI8MM = 1 << 9, - ARM_HWCAP2_A64_SVEF32MM = 1 << 10, - ARM_HWCAP2_A64_SVEF64MM = 1 << 11, - ARM_HWCAP2_A64_SVEBF16 = 1 << 12, - ARM_HWCAP2_A64_I8MM = 1 << 13, - ARM_HWCAP2_A64_BF16 = 1 << 14, - ARM_HWCAP2_A64_DGH = 1 << 15, - ARM_HWCAP2_A64_RNG = 1 << 16, - ARM_HWCAP2_A64_BTI = 1 << 17, - ARM_HWCAP2_A64_MTE = 1 << 18, - ARM_HWCAP2_A64_ECV = 1 << 19, - ARM_HWCAP2_A64_AFP = 1 << 20, - ARM_HWCAP2_A64_RPRES = 1 << 21, - ARM_HWCAP2_A64_MTE3 = 1 << 22, - ARM_HWCAP2_A64_SME = 1 << 23, - ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, - ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, - ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, - ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, - ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, - ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, - ARM_HWCAP2_A64_SME_FA64 = 1 << 30, - ARM_HWCAP2_A64_WFXT = 1ULL << 31, - ARM_HWCAP2_A64_EBF16 = 1ULL << 32, - ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, - ARM_HWCAP2_A64_CSSC = 1ULL << 34, - ARM_HWCAP2_A64_RPRFM = 1ULL << 35, - ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, - ARM_HWCAP2_A64_SME2 = 1ULL << 37, - ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, - ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, - ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, - ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, - ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, - ARM_HWCAP2_A64_MOPS = 1ULL << 43, - ARM_HWCAP2_A64_HBC = 1ULL << 44, - ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, - ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, - ARM_HWCAP2_A64_LSE128 = 1ULL << 47, - ARM_HWCAP2_A64_FPMR = 1ULL << 48, - ARM_HWCAP2_A64_LUT = 1ULL << 49, - ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, - ARM_HWCAP2_A64_F8CVT = 1ULL << 51, - ARM_HWCAP2_A64_F8FMA = 1ULL << 52, - ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, - ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, - ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, - ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, - ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, - ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, - ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, - ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, - ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, - ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, - ARM_HWCAP2_A64_POE = 1ULL << 63, -}; - -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_A64_FP; - hwcaps |= ARM_HWCAP_A64_ASIMD; - hwcaps |= ARM_HWCAP_A64_CPUID; - - /* probe for the extra features */ - - GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); - GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); - GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); - GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); - GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); - GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); - GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); - GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); - GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); - GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); - GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); - GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); - GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); - GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); - GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); - GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); - GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); - GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); - GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); - GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); - GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); - GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); - GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); - GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); - - return hwcaps; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); - GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); - GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); - GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); - GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); - GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); - GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); - GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); - GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); - GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); - GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); - GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); - GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); - GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); - GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); - GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); - GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); - GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); - GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); - GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | - ARM_HWCAP2_A64_SME_F32F32 | - ARM_HWCAP2_A64_SME_B16F32 | - ARM_HWCAP2_A64_SME_F16F32 | - ARM_HWCAP2_A64_SME_I8I32)); - GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); - GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); - GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); - GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); - GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); - GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); - GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | - ARM_HWCAP2_A64_SME_I16I32 | - ARM_HWCAP2_A64_SME_BI32I32)); - GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); - GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); - GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); - GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); - - return hwcaps; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", - [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", - [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", - [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", - [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", - [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", - [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", - [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", - [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", - [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", - [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", - [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", - [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", - [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", - [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", - [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", - [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", - [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", - [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", - [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", - [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", - [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", - [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", - [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", - [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", - [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", - [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", - [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", - [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", - [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", - [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", - [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", - [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", - [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", - [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", - [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", - [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", - [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", - [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", - [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", - [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", - [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", - [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", - [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", - [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", - [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", - [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", - [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", - [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", - [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", - [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", - [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", - [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", - [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", - [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", - [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", - [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", - [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", - [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", - [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", - [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", - [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", - [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", - [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", - [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -#undef GET_FEATURE_ID - -#if TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-be.c.inc" -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - -#endif /* not TARGET_AARCH64 */ - -#endif /* TARGET_ARM */ - -#ifdef TARGET_SPARC - -#ifndef TARGET_SPARC64 -# define ELF_CLASS ELFCLASS32 -# define ELF_ARCH EM_SPARC -#elif defined(TARGET_ABI32) -# define ELF_CLASS ELFCLASS32 -# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) -#else -# define ELF_CLASS ELFCLASS64 -# define ELF_ARCH EM_SPARCV9 -#endif - -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - /* There are not many sparc32 hwcap bits -- we have all of them. */ - uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | - HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; - -#ifdef TARGET_SPARC64 - CPUSPARCState *env = cpu_env(thread_cpu); - uint32_t features = env->def.features; - - r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; - /* 32x32 multiply and divide are efficient. */ - r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; - /* We don't have an internal feature bit for this. */ - r |= HWCAP_SPARC_POPC; - r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; - r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; - r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; - r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; - r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; - r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; -#endif - - return r; -} - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Note that target_cpu_copy_regs does not read psr/tstate. */ - regs->pc = infop->entry; - regs->npc = regs->pc + 4; - regs->y = 0; - regs->u_regs[14] = (infop->start_stack - 16 * sizeof(abi_ulong) - - TARGET_STACK_BIAS); -} -#endif /* TARGET_SPARC */ - -#ifdef TARGET_PPC - -#define ELF_MACHINE PPC_ELF_MACHINE - -#if defined(TARGET_PPC64) - -#define elf_check_arch(x) ( (x) == EM_PPC64 ) - -#define ELF_CLASS ELFCLASS64 - -#else - -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -#endif - -#define ELF_ARCH EM_PPC - -/* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). - See arch/powerpc/include/asm/cputable.h. */ -enum { - QEMU_PPC_FEATURE_32 = 0x80000000, - QEMU_PPC_FEATURE_64 = 0x40000000, - QEMU_PPC_FEATURE_601_INSTR = 0x20000000, - QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, - QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, - QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, - QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, - QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, - QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, - QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, - QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, - QEMU_PPC_FEATURE_NO_TB = 0x00100000, - QEMU_PPC_FEATURE_POWER4 = 0x00080000, - QEMU_PPC_FEATURE_POWER5 = 0x00040000, - QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, - QEMU_PPC_FEATURE_CELL = 0x00010000, - QEMU_PPC_FEATURE_BOOKE = 0x00008000, - QEMU_PPC_FEATURE_SMT = 0x00004000, - QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, - QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, - QEMU_PPC_FEATURE_PA6T = 0x00000800, - QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, - QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, - QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, - QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, - QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, - - QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, - QEMU_PPC_FEATURE_PPC_LE = 0x00000001, - - /* Feature definitions in AT_HWCAP2. */ - QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ - QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ - QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ - QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ - QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ - QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ - QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, - QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, - QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ - QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ - QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ - QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ - QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ - QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ - QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - - /* We don't have to be terribly complete here; the high points are - Altivec/FP/SPE support. Anything else is just a bonus. */ -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flags, feature) \ - do { \ - if ((cpu->env.insns_flags2 & flags) == flags) { \ - features |= feature; \ - } \ - } while (0) - GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); - GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); - GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); - GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); - GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); - GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); - GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); - GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); - GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); - GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); - GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | - PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), - QEMU_PPC_FEATURE_ARCH_2_06); -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} - -#define ELF_HWCAP2 get_elf_hwcap2() - -static uint32_t get_elf_hwcap2(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flag, feature) \ - do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) - - GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); - GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); - GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | - QEMU_PPC_FEATURE2_VEC_CRYPTO); - GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | - QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); - GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | - QEMU_PPC_FEATURE2_MMA); - -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} - -/* - * The requirements here are: - * - keep the final alignment of sp (sp & 0xf) - * - make sure the 32-bit value at the first 16 byte aligned position of - * AUXV is greater than 16 for glibc compatibility. - * AT_IGNOREPPC is used for that. - * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, - * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. - */ -#define DLINFO_ARCH_ITEMS 5 -#define ARCH_DLINFO \ - do { \ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ - /* \ - * Handle glibc compatibility: these magic entries must \ - * be at the lowest addresses in the final auxv. \ - */ \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ - NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ - NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ - } while (0) - -static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) -{ - _regs->gpr[1] = infop->start_stack; -#if defined(TARGET_PPC64) - if (get_ppc64_abi(infop) < 2) { - uint64_t val; - get_user_u64(val, infop->entry + 8); - _regs->gpr[2] = val + infop->load_bias; - get_user_u64(val, infop->entry); - infop->entry = val + infop->load_bias; - } else { - _regs->gpr[12] = infop->entry; /* r12 set to global entry address */ - } -#endif - _regs->nip = infop->entry; -} - -/* See linux kernel: arch/powerpc/include/asm/elf.h. */ -#define ELF_NREG 48 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *env) -{ - int i; - target_ulong ccr = 0; - - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[i] = tswapreg(env->gpr[i]); - } - - (*regs)[32] = tswapreg(env->nip); - (*regs)[33] = tswapreg(env->msr); - (*regs)[35] = tswapreg(env->ctr); - (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(cpu_read_xer(env)); - - ccr = ppc_get_cr(env); - (*regs)[38] = tswapreg(ccr); -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#ifndef TARGET_PPC64 -# define VDSO_HEADER "vdso-32.c.inc" -#elif TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-64.c.inc" -#else -# define VDSO_HEADER "vdso-64le.c.inc" -#endif - -#endif - -#ifdef TARGET_LOONGARCH64 - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_LOONGARCH -#define EXSTACK_DEFAULT true - -#define elf_check_arch(x) ((x) == EM_LOONGARCH) - -#define VDSO_HEADER "vdso.c.inc" - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /*Set crmd PG,DA = 1,0 */ - regs->csr.crmd = 2 << 3; - regs->csr.era = infop->entry; - regs->regs[3] = infop->start_stack; -} - -/* See linux kernel: arch/loongarch/include/asm/elf.h */ -#define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -enum { - TARGET_EF_R0 = 0, - TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, - TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, -}; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPULoongArchState *env) -{ - int i; - - (*regs)[TARGET_EF_R0] = 0; - - for (i = 1; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); - } - - (*regs)[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - (*regs)[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#define ELF_HWCAP get_elf_hwcap() - -/* See arch/loongarch/include/uapi/asm/hwcap.h */ -enum { - HWCAP_LOONGARCH_CPUCFG = (1 << 0), - HWCAP_LOONGARCH_LAM = (1 << 1), - HWCAP_LOONGARCH_UAL = (1 << 2), - HWCAP_LOONGARCH_FPU = (1 << 3), - HWCAP_LOONGARCH_LSX = (1 << 4), - HWCAP_LOONGARCH_LASX = (1 << 5), - HWCAP_LOONGARCH_CRC32 = (1 << 6), - HWCAP_LOONGARCH_COMPLEX = (1 << 7), - HWCAP_LOONGARCH_CRYPTO = (1 << 8), - HWCAP_LOONGARCH_LVZ = (1 << 9), - HWCAP_LOONGARCH_LBT_X86 = (1 << 10), - HWCAP_LOONGARCH_LBT_ARM = (1 << 11), - HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), -}; - -static uint32_t get_elf_hwcap(void) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= HWCAP_LOONGARCH_CRC32; - - if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { - hwcaps |= HWCAP_LOONGARCH_UAL; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { - hwcaps |= HWCAP_LOONGARCH_FPU; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { - hwcaps |= HWCAP_LOONGARCH_LAM; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - hwcaps |= HWCAP_LOONGARCH_LSX; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { - hwcaps |= HWCAP_LOONGARCH_LASX; - } - - return hwcaps; -} - -#define ELF_PLATFORM "loongarch" - -#endif /* TARGET_LOONGARCH64 */ - -#ifdef TARGET_MIPS - -#ifdef TARGET_MIPS64 -#define ELF_CLASS ELFCLASS64 -#else -#define ELF_CLASS ELFCLASS32 -#endif -#define ELF_ARCH EM_MIPS -#define EXSTACK_DEFAULT true - -#ifdef TARGET_ABI_MIPSN32 -#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) -#else -#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) -#endif - -#define ELF_BASE_PLATFORM get_elf_base_platform() - -#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ - do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ - { return _base_platform; } } while (0) - -static const char *get_elf_base_platform(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - - /* 64 bit ISAs goes first */ - MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); - MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); - MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); - MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); - MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); - MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); - MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); - - /* 32 bit ISAs */ - MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); - MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); - MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); - MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); - MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); - - /* Fallback */ - return "mips"; -} -#undef MATCH_PLATFORM_INSN - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->cp0_status = 2 << CP0St_KSU; - regs->cp0_epc = infop->entry; - regs->regs[29] = infop->start_stack; -} - -/* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* See linux kernel: arch/mips/include/asm/reg.h. */ -enum { -#ifdef TARGET_MIPS64 - TARGET_EF_R0 = 0, -#else - TARGET_EF_R0 = 6, -#endif - TARGET_EF_R26 = TARGET_EF_R0 + 26, - TARGET_EF_R27 = TARGET_EF_R0 + 27, - TARGET_EF_LO = TARGET_EF_R0 + 32, - TARGET_EF_HI = TARGET_EF_R0 + 33, - TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, - TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, - TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, - TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 -}; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *env) -{ - int i; - - for (i = 0; i < TARGET_EF_R0; i++) { - (*regs)[i] = 0; - } - (*regs)[TARGET_EF_R0] = 0; - - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); - } - - (*regs)[TARGET_EF_R26] = 0; - (*regs)[TARGET_EF_R27] = 0; - (*regs)[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - (*regs)[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - (*regs)[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - (*regs)[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - (*regs)[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - (*regs)[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -/* See arch/mips/include/uapi/asm/hwcap.h. */ -enum { - HWCAP_MIPS_R6 = (1 << 0), - HWCAP_MIPS_MSA = (1 << 1), - HWCAP_MIPS_CRC32 = (1 << 2), - HWCAP_MIPS_MIPS16 = (1 << 3), - HWCAP_MIPS_MDMX = (1 << 4), - HWCAP_MIPS_MIPS3D = (1 << 5), - HWCAP_MIPS_SMARTMIPS = (1 << 6), - HWCAP_MIPS_DSP = (1 << 7), - HWCAP_MIPS_DSP2 = (1 << 8), - HWCAP_MIPS_DSP3 = (1 << 9), - HWCAP_MIPS_MIPS16E2 = (1 << 10), - HWCAP_LOONGSON_MMI = (1 << 11), - HWCAP_LOONGSON_EXT = (1 << 12), - HWCAP_LOONGSON_EXT2 = (1 << 13), - HWCAP_LOONGSON_CPUCFG = (1 << 14), -}; - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE_INSN(_flag, _hwcap) \ - do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ - do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ - do { \ - if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ - hwcaps |= _hwcap; \ - } \ - } while (0) - -static uint32_t get_elf_hwcap(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - uint32_t hwcaps = 0; - - GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, - 2, HWCAP_MIPS_R6); - GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); - GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); - GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); - - return hwcaps; -} - -#undef GET_FEATURE_REG_EQU -#undef GET_FEATURE_REG_SET -#undef GET_FEATURE_INSN - -#endif /* TARGET_MIPS */ - -#ifdef TARGET_MICROBLAZE - -#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MICROBLAZE - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->r1 = infop->start_stack; - -} - -#define ELF_EXEC_PAGESIZE 4096 - -#define USE_ELF_CORE_DUMP -#define ELF_NREG 38 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env) -{ - int i, pos = 0; - - for (i = 0; i < 32; i++) { - (*regs)[pos++] = tswapreg(env->regs[i]); - } - - (*regs)[pos++] = tswapreg(env->pc); - (*regs)[pos++] = tswapreg(mb_cpu_read_msr(env)); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->ear); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->esr); -} - -#endif /* TARGET_MICROBLAZE */ - -#ifdef TARGET_OPENRISC - -#define ELF_ARCH EM_OPENRISC -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->gpr[1] = infop->start_stack; -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 8192 - -/* See linux kernel arch/openrisc/include/asm/elf.h. */ -#define ELF_NREG 34 /* gprs and pc, sr */ -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUOpenRISCState *env) -{ - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(cpu_get_gpr(env, i)); - } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(cpu_get_sr(env)); -} -#define ELF_HWCAP 0 -#define ELF_PLATFORM NULL - -#endif /* TARGET_OPENRISC */ - -#ifdef TARGET_SH4 - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SH - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Check other registers XXXXX */ - regs->pc = infop->entry; - regs->regs[15] = infop->start_stack; -} - -/* See linux kernel: arch/sh/include/asm/elf.h. */ -#define ELF_NREG 23 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* See linux kernel: arch/sh/include/asm/ptrace.h. */ -enum { - TARGET_REG_PC = 16, - TARGET_REG_PR = 17, - TARGET_REG_SR = 18, - TARGET_REG_GBR = 19, - TARGET_REG_MACH = 20, - TARGET_REG_MACL = 21, - TARGET_REG_SYSCALL = 22 -}; - -static inline void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUSH4State *env) -{ - int i; - - for (i = 0; i < 16; i++) { - (*regs)[i] = tswapreg(env->gregs[i]); - } - - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PR] = tswapreg(env->pr); - (*regs)[TARGET_REG_SR] = tswapreg(env->sr); - (*regs)[TARGET_REG_GBR] = tswapreg(env->gbr); - (*regs)[TARGET_REG_MACH] = tswapreg(env->mach); - (*regs)[TARGET_REG_MACL] = tswapreg(env->macl); - (*regs)[TARGET_REG_SYSCALL] = 0; /* FIXME */ -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -enum { - SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ - SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ - SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ - SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ - SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ - SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ - SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ - SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ - SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ - SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - SuperHCPU *cpu = SUPERH_CPU(thread_cpu); - uint32_t hwcap = 0; - - hwcap |= SH_CPU_HAS_FPU; - - if (cpu->env.features & SH_FEATURE_SH4A) { - hwcap |= SH_CPU_HAS_LLSC; - } - - return hwcap; -} - -#endif - -#ifdef TARGET_M68K - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_68K - -/* ??? Does this need to do anything? - #define ELF_PLAT_INIT(_r) */ - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->usp = infop->start_stack; - regs->sr = 0; - regs->pc = infop->entry; -} - -/* See linux kernel: arch/m68k/include/asm/elf.h. */ -#define ELF_NREG 20 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *env) -{ - (*regs)[0] = tswapreg(env->dregs[1]); - (*regs)[1] = tswapreg(env->dregs[2]); - (*regs)[2] = tswapreg(env->dregs[3]); - (*regs)[3] = tswapreg(env->dregs[4]); - (*regs)[4] = tswapreg(env->dregs[5]); - (*regs)[5] = tswapreg(env->dregs[6]); - (*regs)[6] = tswapreg(env->dregs[7]); - (*regs)[7] = tswapreg(env->aregs[0]); - (*regs)[8] = tswapreg(env->aregs[1]); - (*regs)[9] = tswapreg(env->aregs[2]); - (*regs)[10] = tswapreg(env->aregs[3]); - (*regs)[11] = tswapreg(env->aregs[4]); - (*regs)[12] = tswapreg(env->aregs[5]); - (*regs)[13] = tswapreg(env->aregs[6]); - (*regs)[14] = tswapreg(env->dregs[0]); - (*regs)[15] = tswapreg(env->aregs[7]); - (*regs)[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - (*regs)[17] = tswapreg(env->sr); - (*regs)[18] = tswapreg(env->pc); - (*regs)[19] = 0; /* FIXME: regs->format | regs->vector */ -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 8192 - -#endif - -#ifdef TARGET_ALPHA - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_ALPHA - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->ps = 8; - regs->usp = infop->start_stack; -} - -#define ELF_EXEC_PAGESIZE 8192 - -#endif /* TARGET_ALPHA */ - -#ifdef TARGET_S390X - -#define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_S390 - -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE(_feat, _hwcap) \ - do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - /* - * Let's assume we always have esan3 and zarch. - * 31-bit processes can use 64-bit registers (high gprs). - */ - uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; - - GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); - GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); - GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); - GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); - if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && - s390_has_feat(S390_FEAT_ETF3_ENH)) { - hwcap |= HWCAP_S390_ETF3EH; - } - GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); - GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); - GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); - - return hwcap; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [HWCAP_S390_NR_ESAN3] = "esan3", - [HWCAP_S390_NR_ZARCH] = "zarch", - [HWCAP_S390_NR_STFLE] = "stfle", - [HWCAP_S390_NR_MSA] = "msa", - [HWCAP_S390_NR_LDISP] = "ldisp", - [HWCAP_S390_NR_EIMM] = "eimm", - [HWCAP_S390_NR_DFP] = "dfp", - [HWCAP_S390_NR_HPAGE] = "edat", - [HWCAP_S390_NR_ETF3EH] = "etf3eh", - [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", - [HWCAP_S390_NR_TE] = "te", - [HWCAP_S390_NR_VXRS] = "vx", - [HWCAP_S390_NR_VXRS_BCD] = "vxd", - [HWCAP_S390_NR_VXRS_EXT] = "vxe", - [HWCAP_S390_NR_GS] = "gs", - [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", - [HWCAP_S390_NR_VXRS_PDE] = "vxp", - [HWCAP_S390_NR_SORT] = "sort", - [HWCAP_S390_NR_DFLT] = "dflt", - [HWCAP_S390_NR_NNPA] = "nnpa", - [HWCAP_S390_NR_PCI_MIO] = "pcimio", - [HWCAP_S390_NR_SIE] = "sie", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->psw.addr = infop->entry; - regs->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \ - PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | \ - PSW_MASK_32; - regs->gprs[15] = infop->start_stack; -} - -/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ -#define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -enum { - TARGET_REG_PSWM = 0, - TARGET_REG_PSWA = 1, - TARGET_REG_GPRS = 2, - TARGET_REG_ARS = 18, - TARGET_REG_ORIG_R2 = 26, -}; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUS390XState *env) -{ - int i; - uint32_t *aregs; - - (*regs)[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - (*regs)[TARGET_REG_PSWA] = tswapreg(env->psw.addr); - for (i = 0; i < 16; i++) { - (*regs)[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); - } - aregs = (uint32_t *)&((*regs)[TARGET_REG_ARS]); - for (i = 0; i < 16; i++) { - aregs[i] = tswap32(env->aregs[i]); - } - (*regs)[TARGET_REG_ORIG_R2] = 0; -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_S390X */ - -#ifdef TARGET_RISCV - -#define ELF_ARCH EM_RISCV - -#ifdef TARGET_RISCV32 -#define ELF_CLASS ELFCLASS32 -#define VDSO_HEADER "vdso-32.c.inc" -#else -#define ELF_CLASS ELFCLASS64 -#define VDSO_HEADER "vdso-64.c.inc" -#endif - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ -#define MISA_BIT(EXT) (1 << (EXT - 'A')) - RISCVCPU *cpu = RISCV_CPU(thread_cpu); - uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') - | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') - | MISA_BIT('V'); - - return cpu->env.misa_ext & mask; -#undef MISA_BIT -} - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} - -#define ELF_EXEC_PAGESIZE 4096 - -#endif /* TARGET_RISCV */ - -#ifdef TARGET_HPPA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PARISC -#define ELF_PLATFORM "PARISC" -#define STACK_GROWS_DOWN 0 -#define STACK_ALIGNMENT 64 - -#define VDSO_HEADER "vdso.c.inc" - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->iaoq[0] = infop->entry | PRIV_USER; - regs->iaoq[1] = regs->iaoq[0] + 4; - regs->gr[23] = 0; - regs->gr[24] = infop->argv; - regs->gr[25] = infop->argc; - /* The top-of-stack contains a linkage buffer. */ - regs->gr[30] = infop->start_stack + 64; - regs->gr[31] = infop->entry; -} - -#define LO_COMMPAGE 0 - -static bool init_guest_commpage(void) -{ - /* If reserved_va, then we have already mapped 0 page on the host. */ - if (!reserved_va) { - void *want, *addr; - - want = g2h_untagged(LO_COMMPAGE); - addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - } - - /* - * On Linux, page zero is normally marked execute only + gateway. - * Normal read or write is supposed to fail (thus PROT_NONE above), - * but specific offsets have kernel code mapped to raise permissions - * and implement syscalls. Here, simply mark the page executable. - * Special case the entry points during translation (see do_page_zero). - */ - page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} - -#endif /* TARGET_HPPA */ - -#ifdef TARGET_XTENSA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_XTENSA - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->windowbase = 0; - regs->windowstart = 1; - regs->areg[1] = infop->start_stack; - regs->pc = infop->entry; - if (info_is_fdpic(infop)) { - regs->areg[4] = infop->loadmap_addr; - regs->areg[5] = infop->interpreter_loadmap_addr; - if (infop->interpreter_loadmap_addr) { - regs->areg[6] = infop->interpreter_pt_dynamic_addr; - } else { - regs->areg[6] = infop->pt_dynamic_addr; - } - } -} - -/* See linux kernel: arch/xtensa/include/asm/elf.h. */ -#define ELF_NREG 128 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -enum { - TARGET_REG_PC, - TARGET_REG_PS, - TARGET_REG_LBEG, - TARGET_REG_LEND, - TARGET_REG_LCOUNT, - TARGET_REG_SAR, - TARGET_REG_WINDOWSTART, - TARGET_REG_WINDOWBASE, - TARGET_REG_THREADPTR, - TARGET_REG_AR0 = 64, -}; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUXtensaState *env) -{ - unsigned i; - - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - (*regs)[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - (*regs)[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - (*regs)[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - (*regs)[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - (*regs)[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - (*regs)[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - (*regs)[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); - xtensa_sync_phys_from_window((CPUXtensaState *)env); - for (i = 0; i < env->config->nareg; ++i) { - (*regs)[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); - } -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#endif /* TARGET_XTENSA */ - -#ifdef TARGET_HEXAGON - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_HEXAGON - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} - -#endif /* TARGET_HEXAGON */ - -#ifndef ELF_BASE_PLATFORM -#define ELF_BASE_PLATFORM (NULL) -#endif - -#ifndef ELF_PLATFORM -#define ELF_PLATFORM (NULL) -#endif - -#ifndef ELF_MACHINE -#define ELF_MACHINE ELF_ARCH -#endif - -#ifndef elf_check_arch -#define elf_check_arch(x) ((x) == ELF_ARCH) +#ifndef elf_check_machine +#define elf_check_machine(x) ((x) == ELF_MACHINE) #endif #ifndef elf_check_abi #define elf_check_abi(x) (1) #endif -#ifndef ELF_HWCAP -#define ELF_HWCAP 0 -#endif - #ifndef STACK_GROWS_DOWN #define STACK_GROWS_DOWN 1 #endif @@ -2139,59 +147,36 @@ static inline void init_thread(struct target_pt_regs *regs, #define EXSTACK_DEFAULT false #endif -#include "elf.h" - -/* We must delay the following stanzas until after "elf.h". */ -#if defined(TARGET_AARCH64) - -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) -{ - if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { - if (pr_datasz != sizeof(uint32_t)) { - error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); - return false; - } - /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ - info->note_flags = *data; - } - return true; -} -#define ARCH_USE_GNU_PROPERTY 1 - -#else +/* + * Provide fallback definitions that the target may omit. + * One way or another, we'll get a link error if the setting of + * HAVE_* doesn't match the implementation. + */ +#ifndef HAVE_ELF_HWCAP +abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } +#endif +#ifndef HAVE_ELF_HWCAP2 +abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } +#define HAVE_ELF_HWCAP2 0 +#endif +#ifndef HAVE_ELF_PLATFORM +const char *get_elf_platform(CPUState *cs) { return NULL; } +#endif +#ifndef HAVE_ELF_BASE_PLATFORM +const char *get_elf_base_platform(CPUState *cs) { return NULL; } +#endif -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) +#ifndef HAVE_ELF_GNU_PROPERTY +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, struct image_info *info, + Error **errp) { g_assert_not_reached(); } -#define ARCH_USE_GNU_PROPERTY 0 - +#define HAVE_ELF_GNU_PROPERTY 0 #endif -struct exec -{ - unsigned int a_info; /* Use macros N_MAGIC, etc for access */ - unsigned int a_text; /* length of text, in bytes */ - unsigned int a_data; /* length of data, in bytes */ - unsigned int a_bss; /* length of uninitialized data area, in bytes */ - unsigned int a_syms; /* length of symbol table data in file, in bytes */ - unsigned int a_entry; /* start address */ - unsigned int a_trsize; /* length of relocation info for text, in bytes */ - unsigned int a_drsize; /* length of relocation info for data, in bytes */ -}; - - -#define N_MAGIC(exec) ((exec).a_info & 0xffff) -#define OMAGIC 0407 -#define NMAGIC 0410 -#define ZMAGIC 0413 -#define QMAGIC 0314 +#include "elf.h" #define DLINFO_ITEMS 16 @@ -2286,9 +271,9 @@ static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) } #endif -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); -#endif /* USE_ELF_CORE_DUMP */ +#endif /* HAVE_ELF_CORE_DUMP */ static void load_symbols(struct elfhdr *hdr, const ImageSource *src, abi_ulong load_bias); @@ -2309,7 +294,7 @@ static bool elf_check_ident(struct elfhdr *ehdr) This has to wait until after bswapping the header. */ static bool elf_check_ehdr(struct elfhdr *ehdr) { - return (elf_check_arch(ehdr->e_machine) + return (elf_check_machine(ehdr->e_machine) && elf_check_abi(ehdr->e_flags) && ehdr->e_ehsize == sizeof(struct elfhdr) && ehdr->e_phentsize == sizeof(struct elf_phdr) @@ -2592,7 +577,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_base_platform = 0; - k_base_platform = ELF_BASE_PLATFORM; + k_base_platform = get_elf_base_platform(thread_cpu); if (k_base_platform) { size_t len = strlen(k_base_platform) + 1; if (STACK_GROWS_DOWN) { @@ -2608,7 +593,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_platform = 0; - k_platform = ELF_PLATFORM; + k_platform = get_elf_platform(thread_cpu); if (k_platform) { size_t len = strlen(k_platform) + 1; if (STACK_GROWS_DOWN) { @@ -2660,9 +645,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif -#ifdef ELF_HWCAP2 - size += 2; -#endif + if (HAVE_ELF_HWCAP2) { + size += 2; + } info->auxv_len = size * n; size += envc + argc + 2; @@ -2716,16 +701,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); - NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); + NEW_AUX_ENT(AT_HWCAP, get_elf_hwcap(thread_cpu)); NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes); NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); NEW_AUX_ENT(AT_EXECFN, info->file_string); -#ifdef ELF_HWCAP2 - NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); -#endif - + if (HAVE_ELF_HWCAP2) { + NEW_AUX_ENT(AT_HWCAP2, get_elf_hwcap(thread_cpu)); + } if (u_base_platform) { NEW_AUX_ENT(AT_BASE_PLATFORM, u_base_platform); } @@ -2771,8 +755,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #else #define HI_COMMPAGE 0 #define LO_COMMPAGE -1 -#ifndef INIT_GUEST_COMMPAGE -#define init_guest_commpage() true +#ifndef HAVE_GUEST_COMMPAGE +bool init_guest_commpage(void) { return true; } #endif #endif @@ -3207,7 +1191,7 @@ static bool parse_elf_properties(const ImageSource *src, uint32_t prev_type; /* Unless the arch requires properties, ignore them. */ - if (!ARCH_USE_GNU_PROPERTY) { + if (!HAVE_ELF_GNU_PROPERTY) { return true; } @@ -3630,14 +1614,17 @@ static void load_elf_interp(const char *filename, struct image_info *info, load_elf_image(filename, &src, info, &ehdr, NULL); } -#ifndef vdso_image_info +#ifndef HAVE_VDSO_IMAGE_INFO +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ #ifdef VDSO_HEADER #include VDSO_HEADER -#define vdso_image_info(flags) &vdso_image_info + return &vdso_image_info; #else -#define vdso_image_info(flags) NULL -#endif /* VDSO_HEADER */ -#endif /* vdso_image_info */ + return NULL; +#endif +} +#endif /* HAVE_VDSO_IMAGE_INFO */ static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) { @@ -3968,7 +1955,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Load a vdso if available, which will amongst other things contain the * signal trampolines. Otherwise, allocate a separate page for them. */ - const VdsoImageInfo *vdso = vdso_image_info(info->elf_flags); + const VdsoImageInfo *vdso = get_vdso_image_info(info->elf_flags); if (vdso) { load_elf_vdso(&vdso_info, vdso); info->vdso = vdso_info.load_bias; @@ -3999,14 +1986,14 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) g_free(elf_interpreter); } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP bprm->core_dump = &elf_core_dump; #endif return 0; } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP /* * Definitions to generate Intel SVR4-like core files. @@ -4022,23 +2009,18 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Core dump code is copied from linux kernel (fs/binfmt_elf.c). * * Porting ELF coredump for target is (quite) simple process. First you - * define USE_ELF_CORE_DUMP in target ELF code (where init_thread() for + * define HAVE_ELF_CORE_DUMP in target ELF code (where init_thread() for * the target resides): * - * #define USE_ELF_CORE_DUMP + * #define HAVE_ELF_CORE_DUMP * - * Next you define type of register set used for dumping. ELF specification - * says that it needs to be array of elf_greg_t that has size of ELF_NREG. - * - * typedef <target_regtype> target_elf_greg_t; - * #define ELF_NREG <number of registers> - * typedef taret_elf_greg_t target_elf_gregset_t[ELF_NREG]; + * Next you define type of register set used for dumping: + * typedef struct target_elf_gregset_t { ... } target_elf_gregset_t; * * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: * - * static void elf_core_copy_regs(taret_elf_gregset_t *regs, - * const CPUArchState *env); + * void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUArchState *env); * * Parameters: * regs - copy register values into here (allocated and zeroed by caller) @@ -4382,7 +2364,7 @@ static int wmr_fill_region_phdr(void *opaque, vaddr start, phdr->p_flags = (flags & PAGE_READ ? PF_R : 0) | (flags & PAGE_WRITE_ORG ? PF_W : 0) | (flags & PAGE_EXEC ? PF_X : 0); - phdr->p_align = ELF_EXEC_PAGESIZE; + phdr->p_align = TARGET_PAGE_SIZE; bswap_phdr(phdr, 1); d->phdr = phdr + 1; @@ -4490,7 +2472,7 @@ static int elf_core_dump(int signr, const CPUArchState *env) offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; note_size = offset - note_offset; - data_offset = ROUND_UP(offset, ELF_EXEC_PAGESIZE); + data_offset = TARGET_PAGE_ALIGN(offset); /* Do not dump if the corefile size exceeds the limit. */ if (dumpsize.rlim_cur != RLIM_INFINITY @@ -4569,9 +2551,4 @@ static int elf_core_dump(int signr, const CPUArchState *env) } return ret; } -#endif /* USE_ELF_CORE_DUMP */ - -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - init_thread(regs, infop); -} +#endif /* HAVE_ELF_CORE_DUMP */ diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index e18a0183b5..25c97edcae 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -79,9 +79,11 @@ void cpu_loop(CPUHexagonState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->gpr[HEX_REG_PC] = regs->sepc; - env->gpr[HEX_REG_SP] = regs->sp; + CPUArchState *env = cpu_env(cs); + + env->gpr[HEX_REG_PC] = info->entry; + env->gpr[HEX_REG_SP] = info->start_stack; env->gpr[HEX_REG_USR] = 0x56000; } diff --git a/linux-user/hexagon/elfload.c b/linux-user/hexagon/elfload.c new file mode 100644 index 0000000000..d8b545032a --- /dev/null +++ b/linux-user/hexagon/elfload.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + static char buf[32]; + int err; + + /* For now, treat anything newer than v5 as a v73 */ + /* FIXME - Disable instructions that are newer than the specified arch */ + if (eflags == 0x04 || /* v5 */ + eflags == 0x05 || /* v55 */ + eflags == 0x60 || /* v60 */ + eflags == 0x61 || /* v61 */ + eflags == 0x62 || /* v62 */ + eflags == 0x65 || /* v65 */ + eflags == 0x66 || /* v66 */ + eflags == 0x67 || /* v67 */ + eflags == 0x8067 || /* v67t */ + eflags == 0x68 || /* v68 */ + eflags == 0x69 || /* v69 */ + eflags == 0x71 || /* v71 */ + eflags == 0x8071 || /* v71t */ + eflags == 0x73 /* v73 */ + ) { + return "v73"; + } + + err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); + return err >= 0 && err < sizeof(buf) ? buf : "unknown"; +} diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index 36056fc9f0..f81ae3895a 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -18,33 +18,7 @@ #ifndef HEXAGON_TARGET_ELF_H #define HEXAGON_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - static char buf[32]; - int err; - - /* For now, treat anything newer than v5 as a v73 */ - /* FIXME - Disable instructions that are newer than the specified arch */ - if (eflags == 0x04 || /* v5 */ - eflags == 0x05 || /* v55 */ - eflags == 0x60 || /* v60 */ - eflags == 0x61 || /* v61 */ - eflags == 0x62 || /* v62 */ - eflags == 0x65 || /* v65 */ - eflags == 0x66 || /* v66 */ - eflags == 0x67 || /* v67 */ - eflags == 0x8067 || /* v67t */ - eflags == 0x68 || /* v68 */ - eflags == 0x69 || /* v69 */ - eflags == 0x71 || /* v71 */ - eflags == 0x8071 || /* v71t */ - eflags == 0x73 /* v73 */ - ) { - return "v73"; - } - - err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); - return err >= 0 && err < sizeof(buf) ? buf : "unknown"; -} +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_HEXAGON #endif diff --git a/linux-user/hexagon/target_syscall.h b/linux-user/hexagon/target_syscall.h index 7f91a4abc7..d9c94737a5 100644 --- a/linux-user/hexagon/target_syscall.h +++ b/linux-user/hexagon/target_syscall.h @@ -18,11 +18,6 @@ #ifndef HEXAGON_TARGET_SYSCALL_H #define HEXAGON_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long sp; -}; - #define UNAME_MACHINE "hexagon" #define UNAME_MINIMUM_RELEASE "4.15.0" diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 9abaad5ef8..3af50653bb 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -196,12 +196,16 @@ void cpu_loop(CPUHPPAState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 1; i < 32; i++) { - env->gr[i] = regs->gr[i]; - } - env->iaoq_f = regs->iaoq[0]; - env->iaoq_b = regs->iaoq[1]; + CPUArchState *env = cpu_env(cs); + + env->iaoq_f = info->entry | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; + env->gr[23] = 0; + env->gr[24] = info->argv; + env->gr[25] = info->argc; + /* The top-of-stack contains a linkage buffer. */ + env->gr[30] = info->start_stack + 64; + env->gr[31] = info->entry; } diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c new file mode 100644 index 0000000000..018034f244 --- /dev/null +++ b/linux-user/hppa/elfload.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "hppa"; +} + +const char *get_elf_platform(CPUState *cs) +{ + return "PARISC"; +} + +bool init_guest_commpage(void) +{ + /* If reserved_va, then we have already mapped 0 page on the host. */ + if (!reserved_va) { + void *want, *addr; + + want = g2h_untagged(LO_COMMPAGE); + addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + } + + /* + * On Linux, page zero is normally marked execute only + gateway. + * Normal read or write is supposed to fail (thus PROT_NONE above), + * but specific offsets have kernel code mapped to raise permissions + * and implement syscalls. Here, simply mark the page executable. + * Special case the entry points during translation (see do_page_zero). + */ + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 19cae8bd65..76930c9369 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -7,8 +7,15 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "hppa"; -} + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_PARISC + +#define HAVE_ELF_PLATFORM 1 + +#define LO_COMMPAGE 0 +#define STACK_GROWS_DOWN 0 +#define STACK_ALIGNMENT 64 +#define VDSO_HEADER "vdso.c.inc" + #endif diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h index 9a8f8ca628..4b21e85371 100644 --- a/linux-user/hppa/target_syscall.h +++ b/linux-user/hppa/target_syscall.h @@ -1,24 +1,6 @@ #ifndef HPPA_TARGET_SYSCALL_H #define HPPA_TARGET_SYSCALL_H -struct target_pt_regs { - target_ulong gr[32]; - uint64_t fr[32]; - target_ulong sr[8]; - target_ulong iasq[2]; - target_ulong iaoq[2]; - target_ulong cr27; - target_ulong __pad0; - target_ulong orig_r28; - target_ulong ksp; - target_ulong kpc; - target_ulong sar; - target_ulong iir; - target_ulong isr; - target_ulong ior; - target_ulong ipsw; -}; - #define UNAME_MACHINE "parisc" #define UNAME_MINIMUM_RELEASE "2.6.32" #define TARGET_CLONE_BACKWARDS diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index d96d5553fa..7b2d8b03d8 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -331,11 +331,10 @@ static void target_cpu_free(void *obj) g_free(obj); } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cpu, struct image_info *info) { - CPUState *cpu = env_cpu(env); + CPUArchState *env = cpu_env(cpu); bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; - int i; OBJECT(cpu)->free = target_cpu_free; env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; @@ -361,28 +360,25 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) /* flags setup : we activate the IRQs by default as in user mode */ env->eflags |= IF_MASK; - /* linux register setup */ -#ifndef TARGET_ABI32 - env->regs[R_EAX] = regs->rax; - env->regs[R_EBX] = regs->rbx; - env->regs[R_ECX] = regs->rcx; - env->regs[R_EDX] = regs->rdx; - env->regs[R_ESI] = regs->rsi; - env->regs[R_EDI] = regs->rdi; - env->regs[R_EBP] = regs->rbp; - env->regs[R_ESP] = regs->rsp; - env->eip = regs->rip; -#else - env->regs[R_EAX] = regs->eax; - env->regs[R_EBX] = regs->ebx; - env->regs[R_ECX] = regs->ecx; - env->regs[R_EDX] = regs->edx; - env->regs[R_ESI] = regs->esi; - env->regs[R_EDI] = regs->edi; - env->regs[R_EBP] = regs->ebp; - env->regs[R_ESP] = regs->esp; - env->eip = regs->eip; -#endif + /* + * Linux register setup. + * + * SVR4/i386 ABI (pages 3-31, 3-32) says that when the program + * starts %edx contains a pointer to a function which might be + * registered using `atexit'. This provides a mean for the + * dynamic linker to call DT_FINI functions for shared libraries + * that have been loaded before the code runs. + * A value of 0 tells we have no such handler. + * + * This applies to x86_64 as well as i386. + * + * That said, the kernel's ELF_PLAT_INIT simply zeros all of the general + * registers. Note that x86_cpu_reset_hold will set %edx to cpuid_version; + * clear all general registers defensively. + */ + memset(env->regs, 0, sizeof(env->regs)); + env->regs[R_ESP] = info->start_stack; + env->eip = info->entry; /* linux interrupt setup */ #ifndef TARGET_ABI32 @@ -394,7 +390,7 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); idt_table = g2h_untagged(env->idt.base); - for (i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { set_idt(i, 0, is64); } set_idt(3, 3, is64); diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c new file mode 100644 index 0000000000..26b12001a3 --- /dev/null +++ b/linux-user/i386/elfload.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} + +const char *get_elf_platform(CPUState *cs) +{ + static const char elf_platform[4][5] = { "i386", "i486", "i586", "i686" }; + int family = object_property_get_int(OBJECT(cs), "family", NULL); + + family = MAX(MIN(family, 6), 3); + return elf_platform[family - 3]; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 238a9aba73..eafac8f382 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -7,8 +7,41 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_386 +#define EXSTACK_DEFAULT true +#define VDSO_HEADER "vdso.c.inc" + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/x86/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_regs_struct via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_user_regs_struct pt; +} target_elf_gregset_t; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_machine(x) ((x) == EM_386 || (x) == EM_486) + +/* + * i386 is the only target which supplies AT_SYSINFO for the vdso. + * All others only supply AT_SYSINFO_EHDR. + */ +#define DLINFO_ARCH_ITEMS (vdso_info != NULL) +#define ARCH_DLINFO \ + do { \ + if (vdso_info) { \ + NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ + } \ + } while (0) + #endif diff --git a/linux-user/i386/target_ptrace.h b/linux-user/i386/target_ptrace.h new file mode 100644 index 0000000000..bc57926f25 --- /dev/null +++ b/linux-user/i386/target_ptrace.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef I386_TARGET_PTRACE_H +#define I386_TARGET_PTRACE_H + +/* + * Note that arch/x86/include/uapi/asm/ptrace.h (struct pt_regs) and + * arch/x86/include/asm/user_32.h (struct user_regs_struct) have the + * same layout, though the exact types differ (int vs long vs unsigned). + * Define user_regs_struct because that's what's actually used. + */ +struct target_user_regs_struct { + abi_ulong bx; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong bp; + abi_ulong ax; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; +}; + +#endif /* I386_TARGET_PTRACE_H */ diff --git a/linux-user/i386/target_syscall.h b/linux-user/i386/target_syscall.h index aaade06b13..c214a909a6 100644 --- a/linux-user/i386/target_syscall.h +++ b/linux-user/i386/target_syscall.h @@ -5,24 +5,6 @@ #define __USER_CS (0x23) #define __USER_DS (0x2B) -struct target_pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}; - /* ioctls */ #define TARGET_LDT_ENTRIES 8192 diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 37f132be4a..85d700953e 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -139,8 +139,7 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, } int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *bprm) + struct image_info *infop, struct linux_binprm *bprm) { int retval; @@ -175,8 +174,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } - /* Success. Initialize important registers. */ - do_init_thread(regs, infop); + /* Success. */ return 0; } diff --git a/linux-user/loader.h b/linux-user/loader.h index e102e6f410..e42b8fa1e3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -82,12 +82,10 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *); + struct image_info *infop, struct linux_binprm *); uint32_t get_elf_eflags(int fd); int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); @@ -98,13 +96,35 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, extern unsigned long guest_stack_size; -#if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint32_t get_elf_hwcap(void); +/* Note that Elf32 and Elf64 use uint32_t for e_flags. */ +const char *get_elf_cpu_model(uint32_t eflags); + +abi_ulong get_elf_hwcap(CPUState *cs); +abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); -#endif -#if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint64_t get_elf_hwcap2(void); const char *elf_hwcap2_str(uint32_t bit); -#endif +const char *get_elf_platform(CPUState *cs); +const char *get_elf_base_platform(CPUState *cs); +bool init_guest_commpage(void); + +struct target_elf_gregset_t; +void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); + +typedef struct { + const uint8_t *image; + const uint32_t *relocs; + unsigned image_size; + unsigned reloc_count; + unsigned sigreturn_ofs; + unsigned rt_sigreturn_ofs; +} VdsoImageInfo; + +/* Note that both Elf32_Word and Elf64_Word are uint32_t. */ +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags); + +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp); #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index ec8a06c88c..a0a4cbb7cc 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -120,13 +120,10 @@ void cpu_loop(CPULoongArchState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - - for (i = 0; i < 32; i++) { - env->gpr[i] = regs->regs[i]; - } - env->pc = regs->csr.era; + CPUArchState *env = cpu_env(cs); + env->pc = info->entry; + env->gpr[3] = info->start_stack; } diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c new file mode 100644 index 0000000000..ce3bd0c607 --- /dev/null +++ b/linux-user/loongarch64/elfload.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "la464"; +} + +/* See arch/loongarch/include/uapi/asm/hwcap.h */ +enum { + HWCAP_LOONGARCH_CPUCFG = (1 << 0), + HWCAP_LOONGARCH_LAM = (1 << 1), + HWCAP_LOONGARCH_UAL = (1 << 2), + HWCAP_LOONGARCH_FPU = (1 << 3), + HWCAP_LOONGARCH_LSX = (1 << 4), + HWCAP_LOONGARCH_LASX = (1 << 5), + HWCAP_LOONGARCH_CRC32 = (1 << 6), + HWCAP_LOONGARCH_COMPLEX = (1 << 7), + HWCAP_LOONGARCH_CRYPTO = (1 << 8), + HWCAP_LOONGARCH_LVZ = (1 << 9), + HWCAP_LOONGARCH_LBT_X86 = (1 << 10), + HWCAP_LOONGARCH_LBT_ARM = (1 << 11), + HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= HWCAP_LOONGARCH_CRC32; + + if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { + hwcaps |= HWCAP_LOONGARCH_UAL; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { + hwcaps |= HWCAP_LOONGARCH_FPU; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { + hwcaps |= HWCAP_LOONGARCH_LAM; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + hwcaps |= HWCAP_LOONGARCH_LSX; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + hwcaps |= HWCAP_LOONGARCH_LASX; + } + + return hwcaps; +} + +const char *get_elf_platform(CPUState *cs) +{ + return "loongarch"; +} + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) +{ + r->pt.regs[0] = 0; + + for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { + r->pt.regs[i] = tswapreg(env->gpr[i]); + } + + r->pt.csr_era = tswapreg(env->pc); + r->pt.csr_badv = tswapreg(env->CSR_BADV); +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 95c3f05a46..3aa8c83958 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -5,8 +5,21 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "la464"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS64 +#define ELF_MACHINE EM_LOONGARCH +#define EXSTACK_DEFAULT true +#define VDSO_HEADER "vdso.c.inc" + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* See linux kernel: arch/loongarch/include/asm/elf.h */ +typedef struct target_elf_gregset_t { + struct target_user_pt_regs pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/loongarch64/target_ptrace.h b/linux-user/loongarch64/target_ptrace.h new file mode 100644 index 0000000000..2578e09207 --- /dev/null +++ b/linux-user/loongarch64/target_ptrace.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef LOONGARCH64_TARGET_PTRACE_H +#define LOONGARCH64_TARGET_PTRACE_H + +/* See arch/loongarch/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + abi_ulong regs[32]; + abi_ulong orig_a0; + abi_ulong csr_era; + abi_ulong csr_badv; + abi_ulong reserved[10]; +}; + +#endif /* LOONGARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/loongarch64/target_syscall.h b/linux-user/loongarch64/target_syscall.h index 39f229bb9c..f7ced7b2be 100644 --- a/linux-user/loongarch64/target_syscall.h +++ b/linux-user/loongarch64/target_syscall.h @@ -8,29 +8,6 @@ #include "qemu/units.h" -/* - * this struct defines the way the registers are stored on the - * stack during a system call. - */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - struct { - target_ulong era; - target_ulong badv; - target_ulong crmd; - target_ulong prmd; - target_ulong euen; - target_ulong ecfg; - target_ulong estat; - } csr; - target_ulong orig_a0; - target_ulong __last[0]; -}; - #define UNAME_MACHINE "loongarch64" #define UNAME_MINIMUM_RELEASE "5.19.0" diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 5da91b997a..aca0bf23dc 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -92,33 +92,11 @@ void cpu_loop(CPUM68KState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; + CPUArchState *env = cpu_env(cs); - env->pc = regs->pc; - env->dregs[0] = regs->d0; - env->dregs[1] = regs->d1; - env->dregs[2] = regs->d2; - env->dregs[3] = regs->d3; - env->dregs[4] = regs->d4; - env->dregs[5] = regs->d5; - env->dregs[6] = regs->d6; - env->dregs[7] = regs->d7; - env->aregs[0] = regs->a0; - env->aregs[1] = regs->a1; - env->aregs[2] = regs->a2; - env->aregs[3] = regs->a3; - env->aregs[4] = regs->a4; - env->aregs[5] = regs->a5; - env->aregs[6] = regs->a6; - env->aregs[7] = regs->usp; - env->sr = regs->sr; - - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; + env->pc = info->entry; + env->aregs[7] = info->start_stack; + env->sr = 0; } diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c new file mode 100644 index 0000000000..423d1f680a --- /dev/null +++ b/linux-user/m68k/elfload.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + if (eflags == 0 || (eflags & EF_M68K_M68000)) { + /* 680x0 */ + return "m68040"; + } + + /* Coldfire */ + return "any"; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +{ + r->d1 = tswapal(env->dregs[1]); + r->d2 = tswapal(env->dregs[2]); + r->d3 = tswapal(env->dregs[3]); + r->d4 = tswapal(env->dregs[4]); + r->d5 = tswapal(env->dregs[5]); + r->d6 = tswapal(env->dregs[6]); + r->d7 = tswapal(env->dregs[7]); + r->a0 = tswapal(env->aregs[0]); + r->a1 = tswapal(env->aregs[1]); + r->a2 = tswapal(env->aregs[2]); + r->a3 = tswapal(env->aregs[3]); + r->a4 = tswapal(env->aregs[4]); + r->a5 = tswapal(env->aregs[5]); + r->a6 = tswapal(env->aregs[6]); + r->d0 = tswapal(env->dregs[0]); + r->usp = tswapal(env->aregs[7]); + r->orig_d0 = tswapal(env->dregs[0]); /* FIXME */ + r->sr = tswapal(env->sr); + r->pc = tswapal(env->pc); + /* FIXME: regs->format | regs->vector */ +} diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 998fe0fe2f..b997fa0b6d 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -7,14 +7,32 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if (eflags == 0 || (eflags & EF_M68K_M68000)) { - /* 680x0 */ - return "m68040"; - } - /* Coldfire */ - return "any"; -} +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_68K + +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/m68k/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + * + * Note that user_regs_struct has + * short stkadj, sr; + * ... + * short fmtvec, __fill; + * but ELF_CORE_COPY_REGS writes to unsigned longs. + * Therefore adjust the sr and fmtvec fields to match. + */ +typedef struct target_elf_gregset_t { + abi_ulong d1, d2, d3, d4, d5, d6, d7; + abi_ulong a0, a1, a2, a3, a4, a5, a6; + abi_ulong d0; + abi_ulong usp; + abi_ulong orig_d0; + abi_ulong sr; + abi_ulong pc; + abi_ulong fmtvec; +} target_elf_gregset_t; + #endif diff --git a/linux-user/m68k/target_syscall.h b/linux-user/m68k/target_syscall.h index 8d4ddbd76c..3ca0231c70 100644 --- a/linux-user/m68k/target_syscall.h +++ b/linux-user/m68k/target_syscall.h @@ -1,22 +1,6 @@ #ifndef M68K_TARGET_SYSCALL_H #define M68K_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - abi_long d1, d2, d3, d4, d5, d6, d7; - abi_long a0, a1, a2, a3, a4, a5, a6; - abi_ulong d0; - abi_ulong usp; - abi_ulong orig_d0; - int16_t stkadj; - uint16_t sr; - abi_ulong pc; - uint16_t fntvex; - uint16_t __fill; -}; - #define UNAME_MACHINE "m68k" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/main.c b/linux-user/main.c index 68972f00a1..7b0ccb6fd6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -49,7 +49,6 @@ #include "qemu/guest-random.h" #include "elf.h" #include "trace/control.h" -#include "target_elf.h" #include "user/cpu_loop.h" #include "crypto/init.h" #include "fd-trans.h" @@ -341,16 +340,6 @@ static void handle_arg_ld_prefix(const char *arg) interp_prefix = strdup(arg); } -static void handle_arg_pagesize(const char *arg) -{ - unsigned size, want = qemu_real_host_page_size(); - - if (qemu_strtoui(arg, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } -} - static void handle_arg_seed(const char *arg) { seed_optarg = arg; @@ -523,8 +512,6 @@ static const struct qemu_argument arg_table[] = { "range[,...]","filter logging based on address range"}, {"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename, "logfile", "write logs to 'logfile' (default stderr)"}, - {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, - "pagesize", "deprecated change to host page size"}, {"one-insn-per-tb", "QEMU_ONE_INSN_PER_TB", false, handle_arg_one_insn_per_tb, "", "run with one guest instruction per emulated TB"}, @@ -697,7 +684,6 @@ static int parse_args(int argc, char **argv) int main(int argc, char **argv, char **envp) { - struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; TaskState *ts; @@ -763,9 +749,6 @@ int main(int argc, char **argv, char **envp) trace_init_file(); qemu_plugin_load_list(&plugins, &error_fatal); - /* Zero out regs */ - memset(regs, 0, sizeof(struct target_pt_regs)); - /* Zero out image_info */ memset(info, 0, sizeof(struct image_info)); @@ -809,7 +792,7 @@ int main(int argc, char **argv, char **envp) } if (cpu_model == NULL) { - cpu_model = cpu_get_model(get_elf_eflags(execfd)); + cpu_model = get_elf_cpu_model(get_elf_eflags(execfd)); } cpu_type = parse_cpu_option(cpu_model); @@ -989,8 +972,8 @@ int main(int argc, char **argv, char **envp) fd_trans_init(); - ret = loader_exec(execfd, exec_path, target_argv, target_environ, regs, - info, &bprm); + ret = loader_exec(execfd, exec_path, target_argv, target_environ, + info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", exec_path, strerror(-ret)); _exit(EXIT_FAILURE); @@ -1042,7 +1025,7 @@ int main(int argc, char **argv, char **envp) the real value of GUEST_BASE into account. */ tcg_prologue_init(); - target_cpu_copy_regs(env, regs); + init_main_thread(cpu, info); if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 87236c166f..d8277961c7 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -127,39 +127,10 @@ void cpu_loop(CPUMBState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - env->regs[16] = regs->r16; - env->regs[17] = regs->r17; - env->regs[18] = regs->r18; - env->regs[19] = regs->r19; - env->regs[20] = regs->r20; - env->regs[21] = regs->r21; - env->regs[22] = regs->r22; - env->regs[23] = regs->r23; - env->regs[24] = regs->r24; - env->regs[25] = regs->r25; - env->regs[26] = regs->r26; - env->regs[27] = regs->r27; - env->regs[28] = regs->r28; - env->regs[29] = regs->r29; - env->regs[30] = regs->r30; - env->regs[31] = regs->r31; - env->pc = regs->pc; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->regs[1] = info->start_stack; } diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c new file mode 100644 index 0000000000..7eb1b26d17 --- /dev/null +++ b/linux-user/microblaze/elfload.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) +{ + for (int i = 0; i < 32; i++) { + r->pt.r[i] = tswapal(env->regs[i]); + } + + r->pt.pc = tswapal(env->pc); + r->pt.msr = tswapal(mb_cpu_read_msr(env)); + r->pt.ear = tswapal(env->ear); + r->pt.esr = tswapal(env->esr); +} diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index f6d47d76ff..e874e4def1 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" struct target_sigcontext { struct target_pt_regs regs; /* needs to be first */ @@ -50,75 +51,17 @@ struct target_rt_sigframe { static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->regs.r14); - __put_user(env->regs[15], &sc->regs.r15); - __put_user(env->regs[16], &sc->regs.r16); - __put_user(env->regs[17], &sc->regs.r17); - __put_user(env->regs[18], &sc->regs.r18); - __put_user(env->regs[19], &sc->regs.r19); - __put_user(env->regs[20], &sc->regs.r20); - __put_user(env->regs[21], &sc->regs.r21); - __put_user(env->regs[22], &sc->regs.r22); - __put_user(env->regs[23], &sc->regs.r23); - __put_user(env->regs[24], &sc->regs.r24); - __put_user(env->regs[25], &sc->regs.r25); - __put_user(env->regs[26], &sc->regs.r26); - __put_user(env->regs[27], &sc->regs.r27); - __put_user(env->regs[28], &sc->regs.r28); - __put_user(env->regs[29], &sc->regs.r29); - __put_user(env->regs[30], &sc->regs.r30); - __put_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __put_user(env->regs[i], &sc->regs.r[i]); + } __put_user(env->pc, &sc->regs.pc); } static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->regs.r14); - __get_user(env->regs[15], &sc->regs.r15); - __get_user(env->regs[16], &sc->regs.r16); - __get_user(env->regs[17], &sc->regs.r17); - __get_user(env->regs[18], &sc->regs.r18); - __get_user(env->regs[19], &sc->regs.r19); - __get_user(env->regs[20], &sc->regs.r20); - __get_user(env->regs[21], &sc->regs.r21); - __get_user(env->regs[22], &sc->regs.r22); - __get_user(env->regs[23], &sc->regs.r23); - __get_user(env->regs[24], &sc->regs.r24); - __get_user(env->regs[25], &sc->regs.r25); - __get_user(env->regs[26], &sc->regs.r26); - __get_user(env->regs[27], &sc->regs.r27); - __get_user(env->regs[28], &sc->regs.r28); - __get_user(env->regs[29], &sc->regs.r29); - __get_user(env->regs[30], &sc->regs.r30); - __get_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __get_user(env->regs[i], &sc->regs.r[i]); + } __get_user(env->pc, &sc->regs.pc); } diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 8a8f1debff..7b3ef70d23 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -7,8 +7,22 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_MICROBLAZE + +#define elf_check_machine(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) + +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/microblaze/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_pt_regs pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/microblaze/target_ptrace.h b/linux-user/microblaze/target_ptrace.h new file mode 100644 index 0000000000..ead913e5a4 --- /dev/null +++ b/linux-user/microblaze/target_ptrace.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MICROBLAZE_TARGET_PTRACE_H +#define MICROBLAZE_TARGET_PTRACE_H + +/* We use microblaze_reg_t to keep things similar to the kernel sources. */ +typedef uint32_t microblaze_reg_t; + +struct target_pt_regs { + /* Note the kernel enumerates all 32 registers. */ + microblaze_reg_t r[32]; + microblaze_reg_t pc; + microblaze_reg_t msr; + microblaze_reg_t ear; + microblaze_reg_t esr; + microblaze_reg_t fsr; + uint32_t kernel_mode; +}; + +#endif /* MICROBLAZE_TARGET_PTRACE_H */ diff --git a/linux-user/microblaze/target_syscall.h b/linux-user/microblaze/target_syscall.h index 43362a1664..66f5a9ebe2 100644 --- a/linux-user/microblaze/target_syscall.h +++ b/linux-user/microblaze/target_syscall.h @@ -4,50 +4,6 @@ #define UNAME_MACHINE "microblaze" #define UNAME_MINIMUM_RELEASE "2.6.32" -/* We use microblaze_reg_t to keep things similar to the kernel sources. */ -typedef uint32_t microblaze_reg_t; - -struct target_pt_regs { - microblaze_reg_t r0; - microblaze_reg_t r1; - microblaze_reg_t r2; - microblaze_reg_t r3; - microblaze_reg_t r4; - microblaze_reg_t r5; - microblaze_reg_t r6; - microblaze_reg_t r7; - microblaze_reg_t r8; - microblaze_reg_t r9; - microblaze_reg_t r10; - microblaze_reg_t r11; - microblaze_reg_t r12; - microblaze_reg_t r13; - microblaze_reg_t r14; - microblaze_reg_t r15; - microblaze_reg_t r16; - microblaze_reg_t r17; - microblaze_reg_t r18; - microblaze_reg_t r19; - microblaze_reg_t r20; - microblaze_reg_t r21; - microblaze_reg_t r22; - microblaze_reg_t r23; - microblaze_reg_t r24; - microblaze_reg_t r25; - microblaze_reg_t r26; - microblaze_reg_t r27; - microblaze_reg_t r28; - microblaze_reg_t r29; - microblaze_reg_t r30; - microblaze_reg_t r31; - microblaze_reg_t pc; - microblaze_reg_t msr; - microblaze_reg_t ear; - microblaze_reg_t esr; - microblaze_reg_t fsr; - uint32_t kernel_mode; -}; - #define TARGET_CLONE_BACKWARDS #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 6405806eb0..e67b8a2e46 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -211,12 +211,9 @@ done_syscall: } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; + CPUArchState *env = cpu_env(cs); struct mode_req { bool single; @@ -245,12 +242,11 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) struct mode_req prog_req; struct mode_req interp_req; + target_ulong entry = info->entry; - for(i = 0; i < 32; i++) { - env->active_tc.gpr[i] = regs->regs[i]; - } - env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; - if (regs->cp0_epc & 1) { + env->active_tc.gpr[29] = info->start_stack; + env->active_tc.PC = entry & ~(target_ulong)1; + if (entry & 1) { env->hflags |= MIPS_HFLAG_M16; } diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c new file mode 100644 index 0000000000..e0c50f50ed --- /dev/null +++ b/linux-user/mips/elfload.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_MIPS64 + switch (eflags & EF_MIPS_MACH) { + case EF_MIPS_MACH_OCTEON: + case EF_MIPS_MACH_OCTEON2: + case EF_MIPS_MACH_OCTEON3: + return "Octeon68XX"; + case EF_MIPS_MACH_LS2E: + return "Loongson-2E"; + case EF_MIPS_MACH_LS2F: + return "Loongson-2F"; + case EF_MIPS_MACH_LS3A: + return "Loongson-3A1000"; + default: + break; + } + switch (eflags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_64R6: + return "I6400"; + case EF_MIPS_ARCH_64R2: + return "MIPS64R2-generic"; + default: + break; + } + return "5KEf"; +#else + if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return "mips32r6-generic"; + } + if (eflags & EF_MIPS_NAN2008) { + return "P5600"; + } + return "24Kf"; +#endif +} + +/* See arch/mips/include/uapi/asm/hwcap.h. */ +enum { + HWCAP_MIPS_R6 = (1 << 0), + HWCAP_MIPS_MSA = (1 << 1), + HWCAP_MIPS_CRC32 = (1 << 2), + HWCAP_MIPS_MIPS16 = (1 << 3), + HWCAP_MIPS_MDMX = (1 << 4), + HWCAP_MIPS_MIPS3D = (1 << 5), + HWCAP_MIPS_SMARTMIPS = (1 << 6), + HWCAP_MIPS_DSP = (1 << 7), + HWCAP_MIPS_DSP2 = (1 << 8), + HWCAP_MIPS_DSP3 = (1 << 9), + HWCAP_MIPS_MIPS16E2 = (1 << 10), + HWCAP_LOONGSON_MMI = (1 << 11), + HWCAP_LOONGSON_EXT = (1 << 12), + HWCAP_LOONGSON_EXT2 = (1 << 13), + HWCAP_LOONGSON_CPUCFG = (1 << 14), +}; + +#define GET_FEATURE_INSN(_flag, _hwcap) \ + do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ + do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ + do { \ + if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ + hwcaps |= _hwcap; \ + } \ + } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, + 2, HWCAP_MIPS_R6); + GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); + GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); + GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); + + return hwcaps; +} + +#undef GET_FEATURE_REG_EQU +#undef GET_FEATURE_REG_SET +#undef GET_FEATURE_INSN + +#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ + do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ + { return _base_platform; } } while (0) + +const char *get_elf_base_platform(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + + /* 64 bit ISAs goes first */ + MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); + MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); + MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); + MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); + MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); + MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); + MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); + + /* 32 bit ISAs */ + MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); + MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); + MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); + MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); + MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); + + /* Fallback */ + return "mips"; +} + +#undef MATCH_PLATFORM_INSN + +/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) +{ + for (int i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + r->pt.regs[i] = tswapl(env->active_tc.gpr[i]); + } + + r->pt.regs[26] = 0; + r->pt.regs[27] = 0; + r->pt.lo = tswapl(env->active_tc.LO[0]); + r->pt.hi = tswapl(env->active_tc.HI[0]); + r->pt.cp0_epc = tswapl(env->active_tc.PC); + r->pt.cp0_badvaddr = tswapl(env->CP0_BadVAddr); + r->pt.cp0_status = tswapl(env->CP0_Status); + r->pt.cp0_cause = tswapl(env->CP0_Cause); +} diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 71a32315a8..157306f7a0 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -7,14 +7,23 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return "mips32r6-generic"; - } - if (eflags & EF_MIPS_NAN2008) { - return "P5600"; - } - return "24Kf"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_MIPS +#define EXSTACK_DEFAULT true + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +typedef struct target_elf_gregset_t { + union { + abi_ulong reserved[45]; + struct target_pt_regs pt; + }; +} target_elf_gregset_t; + #endif diff --git a/linux-user/mips/target_ptrace.h b/linux-user/mips/target_ptrace.h new file mode 100644 index 0000000000..2f63b27ac4 --- /dev/null +++ b/linux-user/mips/target_ptrace.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS_TARGET_PTRACE_H +#define MIPS_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong pad0[6]; + abi_ulong regs[32]; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_epc; + abi_ulong cp0_badvaddr; + abi_ulong cp0_status; + abi_ulong cp0_cause; +}; + +#endif /* MIPS_TARGET_PTRACE_H */ diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 08ead67810..dfcdf320b7 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -1,25 +1,6 @@ #ifndef MIPS_TARGET_SYSCALL_H #define MIPS_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Pad bytes for argument save space on the stack. */ - abi_ulong pad0[6]; - - /* Saved main processor registers. */ - abi_ulong regs[32]; - - /* Saved special registers. */ - abi_ulong cp0_status; - abi_ulong lo; - abi_ulong hi; - abi_ulong cp0_badvaddr; - abi_ulong cp0_cause; - abi_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/mips64/elfload.c b/linux-user/mips64/elfload.c new file mode 100644 index 0000000000..b719555e65 --- /dev/null +++ b/linux-user/mips64/elfload.c @@ -0,0 +1 @@ +#include "../mips/elfload.c" diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 502af9d278..061471a0f1 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -7,30 +7,29 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - switch (eflags & EF_MIPS_MACH) { - case EF_MIPS_MACH_OCTEON: - case EF_MIPS_MACH_OCTEON2: - case EF_MIPS_MACH_OCTEON3: - return "Octeon68XX"; - case EF_MIPS_MACH_LS2E: - return "Loongson-2E"; - case EF_MIPS_MACH_LS2F: - return "Loongson-2F"; - case EF_MIPS_MACH_LS3A: - return "Loongson-3A1000"; - default: - break; - } - switch (eflags & EF_MIPS_ARCH) { - case EF_MIPS_ARCH_64R6: - return "I6400"; - case EF_MIPS_ARCH_64R2: - return "MIPS64R2-generic"; - default: - break; - } - return "5KEf"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS64 +#define ELF_MACHINE EM_MIPS +#define EXSTACK_DEFAULT true + +#ifdef TARGET_ABI_MIPSN32 +#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) +#else +#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) +#endif + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +typedef struct target_elf_gregset_t { + union { + target_ulong reserved[45]; + struct target_pt_regs pt; + }; +} target_elf_gregset_t; + #endif diff --git a/linux-user/mips64/target_ptrace.h b/linux-user/mips64/target_ptrace.h new file mode 100644 index 0000000000..41f0bf6c1c --- /dev/null +++ b/linux-user/mips64/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS64_TARGET_PTRACE_H +#define MIPS64_TARGET_PTRACE_H + +struct target_pt_regs { + target_ulong regs[32]; + target_ulong lo; + target_ulong hi; + target_ulong cp0_epc; + target_ulong cp0_badvaddr; + target_ulong cp0_status; + target_ulong cp0_cause; +}; + +#endif /* MIPS64_TARGET_PTRACE_H */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 358dc2d64c..9135bf5e8b 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -1,22 +1,6 @@ #ifndef MIPS64_TARGET_SYSCALL_H #define MIPS64_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - target_ulong cp0_status; - target_ulong lo; - target_ulong hi; - target_ulong cp0_badvaddr; - target_ulong cp0_cause; - target_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips64" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 306b4f8eb4..8c72347a99 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -83,13 +83,10 @@ void cpu_loop(CPUOpenRISCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for (i = 0; i < 32; i++) { - cpu_set_gpr(env, i, regs->gpr[i]); - } - env->pc = regs->pc; - cpu_set_sr(env, regs->sr); + env->pc = info->entry; + cpu_set_gpr(env, 1, info->start_stack); } diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c new file mode 100644 index 0000000000..6bf02bf58d --- /dev/null +++ b/linux-user/openrisc/elfload.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) +{ + for (int i = 0; i < 32; i++) { + r->pt.gpr[i] = tswapal(cpu_get_gpr(env, i)); + } + r->pt.pc = tswapal(env->pc); + r->pt.sr = tswapal(cpu_get_sr(env)); +} diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index cb74a9fe5e..40249095f2 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -21,9 +21,10 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" typedef struct target_sigcontext { - struct target_pt_regs regs; + struct target_user_regs_struct regs; abi_ulong oldmask; } target_sigcontext; diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index 265ecd3079..e8554f5339 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -7,8 +7,20 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + +#include "target_ptrace.h" + +#define ELF_MACHINE EM_OPENRISC +#define ELF_CLASS ELFCLASS32 + +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/openrisc/include/uapi/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_user_regs_struct pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/openrisc/target_ptrace.h b/linux-user/openrisc/target_ptrace.h new file mode 100644 index 0000000000..563c648525 --- /dev/null +++ b/linux-user/openrisc/target_ptrace.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENRISC_TARGET_PTRACE_H +#define OPENRISC_TARGET_PTRACE_H + +/* See arch/openrisc/include/uapi/asm/ptrace.h. */ +struct target_user_regs_struct { + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; +}; + +#endif /* OPENRISC_TARGET_PTRACE_H */ diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 7fe5b73d3b..c8394e9dcd 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,17 +1,6 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H -/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, - * this is called user_regs_struct. Given that this is what - * is used within struct sigcontext we need this definition. - * However, elfload.c wants this name. - */ -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong pc; - abi_ulong sr; -}; - #define UNAME_MACHINE "openrisc" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 2a0efaffcd..22885ffd90 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -378,21 +378,31 @@ void cpu_loop(CPUPPCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); + abi_ptr entry = info->entry; + + env->gpr[1] = info->start_stack; + +#ifdef TARGET_PPC64 + if (get_ppc64_abi(info) < 2) { + uint64_t val; + get_user_u64(val, entry + 8); + env->gpr[2] = val + info->load_bias; + get_user_u64(val, entry); + entry = val + info->load_bias; + } else { + env->gpr[12] = entry; /* r12 set to global entry address */ + } -#if defined(TARGET_PPC64) int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; #if defined(TARGET_ABI32) ppc_store_msr(env, env->msr & ~((target_ulong)1 << flag)); #else ppc_store_msr(env, env->msr | (target_ulong)1 << flag); #endif -#endif +#endif /* TARGET_PPC64 */ - env->nip = regs->nip; - for(i = 0; i < 32; i++) { - env->gpr[i] = regs->gpr[i]; - } + env->nip = entry; } diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c new file mode 100644 index 0000000000..0d54da9803 --- /dev/null +++ b/linux-user/ppc/elfload.c @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_PPC64 + return "POWER9"; +#else + return "750"; +#endif +} + +/* + * Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). + * See arch/powerpc/include/asm/cputable.h. + */ +enum { + QEMU_PPC_FEATURE_32 = 0x80000000, + QEMU_PPC_FEATURE_64 = 0x40000000, + QEMU_PPC_FEATURE_601_INSTR = 0x20000000, + QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, + QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, + QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, + QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, + QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, + QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, + QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, + QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, + QEMU_PPC_FEATURE_NO_TB = 0x00100000, + QEMU_PPC_FEATURE_POWER4 = 0x00080000, + QEMU_PPC_FEATURE_POWER5 = 0x00040000, + QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, + QEMU_PPC_FEATURE_CELL = 0x00010000, + QEMU_PPC_FEATURE_BOOKE = 0x00008000, + QEMU_PPC_FEATURE_SMT = 0x00004000, + QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, + QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, + QEMU_PPC_FEATURE_PA6T = 0x00000800, + QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, + QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, + QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, + QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, + QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, + + QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, + QEMU_PPC_FEATURE_PPC_LE = 0x00000001, + + /* Feature definitions in AT_HWCAP2. */ + QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ + QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ + QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ + QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ + QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ + QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ + QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, + QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, + QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ + QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ + QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ + QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ + QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ + QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ + QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + + /* + * We don't have to be terribly complete here; the high points are + * Altivec/FP/SPE support. Anything else is just a bonus. + */ +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flags, feature) \ + do { \ + if ((cpu->env.insns_flags2 & flags) == flags) { \ + features |= feature; \ + } \ + } while (0) + GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); + GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); + GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); + GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); + GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); + GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); + GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); + GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); + GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); + GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); + GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), + QEMU_PPC_FEATURE_ARCH_2_06); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flag, feature) \ + do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) + + GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); + GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); + GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | + QEMU_PPC_FEATURE2_VEC_CRYPTO); + GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | + QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); + GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | + QEMU_PPC_FEATURE2_MMA); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) +{ + for (int i = 0; i < ARRAY_SIZE(env->gpr); i++) { + r->pt.gpr[i] = tswapal(env->gpr[i]); + } + + r->pt.nip = tswapal(env->nip); + r->pt.msr = tswapal(env->msr); + r->pt.ctr = tswapal(env->ctr); + r->pt.link = tswapal(env->lr); + r->pt.xer = tswapal(cpu_read_xer(env)); + r->pt.ccr = tswapal(ppc_get_cr(env)); +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 0616618854..22854cf52f 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -7,12 +7,64 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ + +#include "target_ptrace.h" + +#define ELF_MACHINE PPC_ELF_MACHINE + #ifdef TARGET_PPC64 - return "POWER9"; +# define ELF_CLASS ELFCLASS64 +#else +# define ELF_CLASS ELFCLASS32 +# define EXSTACK_DEFAULT true +#endif + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * The size of 48 words is set in arch/powerpc/include/uapi/asm/elf.h. + * However PPC_ELF_CORE_COPY_REGS in arch/powerpc/include/asm/elf.h + * open-codes a memcpy from struct pt_regs, then zeros the rest. + */ +typedef struct target_elf_gregset_t { + union { + struct target_pt_regs pt; + abi_ulong reserved[48]; + }; +} target_elf_gregset_t; + +#ifndef TARGET_PPC64 +# define VDSO_HEADER "vdso-32.c.inc" +#elif TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-64.c.inc" #else - return "750"; +# define VDSO_HEADER "vdso-64le.c.inc" #endif -} + +/* + * The requirements here are: + * - keep the final alignment of sp (sp & 0xf) + * - make sure the 32-bit value at the first 16 byte aligned position of + * AUXV is greater than 16 for glibc compatibility. + * AT_IGNOREPPC is used for that. + * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, + * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. + */ +#define DLINFO_ARCH_ITEMS 5 +#define ARCH_DLINFO \ + do { \ + PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ + /* \ + * Handle glibc compatibility: these magic entries must \ + * be at the lowest addresses in the final auxv. \ + */ \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ + NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ + NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ + } while (0) + #endif diff --git a/linux-user/ppc/target_ptrace.h b/linux-user/ppc/target_ptrace.h new file mode 100644 index 0000000000..df77bfde73 --- /dev/null +++ b/linux-user/ppc/target_ptrace.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef PPC_TARGET_PTRACE_H +#define PPC_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong gpr[32]; + abi_ulong nip; + abi_ulong msr; + abi_ulong orig_gpr3; /* Used for restarting system calls */ + abi_ulong ctr; + abi_ulong link; + abi_ulong xer; + abi_ulong ccr; +#if defined(TARGET_PPC64) + abi_ulong softe; +#else + abi_ulong mq; /* 601 only (not used at present) */ +#endif + abi_ulong trap; /* Reason for being here */ + abi_ulong dar; /* Fault registers */ + abi_ulong dsisr; + abi_ulong result; /* Result of a system call */ +}; + +#endif /* PPC_TARGET_PTRACE_H */ diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 77b36d0b46..976b4bb7e9 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -20,34 +20,6 @@ #ifndef PPC_TARGET_SYSCALL_H #define PPC_TARGET_SYSCALL_H -/* XXX: ABSOLUTELY BUGGY: - * for now, this is quite just a cut-and-paste from i386 target... - */ - -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong nip; - abi_ulong msr; - abi_ulong orig_gpr3; /* Used for restarting system calls */ - abi_ulong ctr; - abi_ulong link; - abi_ulong xer; - abi_ulong ccr; -#if defined(TARGET_PPC64) - abi_ulong softe; -#else - abi_ulong mq; /* 601 only (not used at present) */ -#endif - /* Used on APUS to hold IPL value. */ - abi_ulong trap; /* Reason for being here */ - abi_ulong dar; /* Fault registers */ - abi_ulong dsisr; - abi_ulong result; /* Result of a system call */ -}; - /* ioctls */ struct target_revectored_struct { abi_ulong __map[8]; /* 256 bits */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0b19fa43e6..e4dca0c20f 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -122,12 +122,6 @@ struct TaskState { #ifdef TARGET_M68K abi_ulong tp_value; #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV) - /* Extra fields for semihosted binaries. */ - abi_ulong heap_base; - abi_ulong heap_limit; -#endif - abi_ulong stack_base; int used; /* non zero if used */ struct image_info *info; struct linux_binprm *bprm; @@ -365,4 +359,6 @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); +void init_main_thread(CPUState *cs, struct image_info *info); + #endif /* QEMU_H */ diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 3ac8bbfec1..b316281532 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -94,23 +94,16 @@ void cpu_loop(CPURISCVState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; + CPUArchState *env = cpu_env(cs); - env->pc = regs->sepc; - env->gpr[xSP] = regs->sp; + env->pc = info->entry; + env->gpr[xSP] = info->start_stack; env->elf_flags = info->elf_flags; if ((env->misa_ext & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { error_report("Incompatible ELF: RVE cpu requires RVE ABI binary"); exit(EXIT_FAILURE); } - - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c new file mode 100644 index 0000000000..2e7d622232 --- /dev/null +++ b/linux-user/riscv/elfload.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} + +abi_ulong get_elf_hwcap(CPUState *cs) +{ +#define MISA_BIT(EXT) (1 << (EXT - 'A')) + RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') + | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') + | MISA_BIT('V'); + + return cpu->env.misa_ext & mask; +#undef MISA_BIT +} diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index dedd5956f3..dbbfdf54d3 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -7,8 +7,17 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + +#define ELF_MACHINE EM_RISCV + +#ifdef TARGET_RISCV32 +#define ELF_CLASS ELFCLASS32 +#define VDSO_HEADER "vdso-32.c.inc" +#else +#define ELF_CLASS ELFCLASS64 +#define VDSO_HEADER "vdso-64.c.inc" +#endif + +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h index 7601f10c28..69a7b753eb 100644 --- a/linux-user/riscv/target_syscall.h +++ b/linux-user/riscv/target_syscall.h @@ -8,41 +8,6 @@ #ifndef LINUX_USER_RISCV_TARGET_SYSCALL_H #define LINUX_USER_RISCV_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long ra; - abi_long sp; - abi_long gp; - abi_long tp; - abi_long t0; - abi_long t1; - abi_long t2; - abi_long s0; - abi_long s1; - abi_long a0; - abi_long a1; - abi_long a2; - abi_long a3; - abi_long a4; - abi_long a5; - abi_long a6; - abi_long a7; - abi_long s2; - abi_long s3; - abi_long s4; - abi_long s5; - abi_long s6; - abi_long s7; - abi_long s8; - abi_long s9; - abi_long s10; - abi_long s11; - abi_long t3; - abi_long t4; - abi_long t5; - abi_long t6; -}; - #ifdef TARGET_RISCV32 #define UNAME_MACHINE "riscv32" #define UNAME_MINIMUM_RELEASE "5.4.0" diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index c9124444ed..49e44548f8 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -180,12 +180,13 @@ void cpu_loop(CPUS390XState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; i++) { - env->regs[i] = regs->gprs[i]; - } - env->psw.mask = regs->psw.mask; - env->psw.addr = regs->psw.addr; + CPUArchState *env = cpu_env(cs); + + env->psw.addr = info->entry; + env->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | + PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | + PSW_MASK_32; + env->regs[15] = info->start_stack; } diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c new file mode 100644 index 0000000000..27109279e2 --- /dev/null +++ b/linux-user/s390x/elfload.c @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "qemu"; +} + +#define GET_FEATURE(_feat, _hwcap) \ + do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* + * Let's assume we always have esan3 and zarch. + * 31-bit processes can use 64-bit registers (high gprs). + */ + uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; + + GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); + GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); + GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); + GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); + if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && + s390_has_feat(S390_FEAT_ETF3_ENH)) { + hwcap |= HWCAP_S390_ETF3EH; + } + GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); + GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); + GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); + + return hwcap; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [HWCAP_S390_NR_ESAN3] = "esan3", + [HWCAP_S390_NR_ZARCH] = "zarch", + [HWCAP_S390_NR_STFLE] = "stfle", + [HWCAP_S390_NR_MSA] = "msa", + [HWCAP_S390_NR_LDISP] = "ldisp", + [HWCAP_S390_NR_EIMM] = "eimm", + [HWCAP_S390_NR_DFP] = "dfp", + [HWCAP_S390_NR_HPAGE] = "edat", + [HWCAP_S390_NR_ETF3EH] = "etf3eh", + [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", + [HWCAP_S390_NR_TE] = "te", + [HWCAP_S390_NR_VXRS] = "vx", + [HWCAP_S390_NR_VXRS_BCD] = "vxd", + [HWCAP_S390_NR_VXRS_EXT] = "vxe", + [HWCAP_S390_NR_GS] = "gs", + [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", + [HWCAP_S390_NR_VXRS_PDE] = "vxp", + [HWCAP_S390_NR_SORT] = "sort", + [HWCAP_S390_NR_DFLT] = "dflt", + [HWCAP_S390_NR_NNPA] = "nnpa", + [HWCAP_S390_NR_PCI_MIO] = "pcimio", + [HWCAP_S390_NR_SIE] = "sie", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) +{ + r->pt.psw.mask = tswapal(env->psw.mask); + r->pt.psw.addr = tswapal(env->psw.addr); + for (int i = 0; i < 16; i++) { + r->pt.gprs[i] = tswapal(env->regs[i]); + } + for (int i = 0; i < 16; i++) { + r->pt.acrs[i] = tswap32(env->aregs[i]); + } + r->pt.orig_gpr2 = 0; +} diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index df49c24708..96d1c8d11c 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -22,6 +22,7 @@ #include "signal-common.h" #include "linux-user/trace.h" #include "vdso-asmoffset.h" +#include "target_ptrace.h" #define __NUM_GPRS 16 #define __NUM_FPRS 16 diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index 8114b59c1d..ef5edbd860 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -7,8 +7,22 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "qemu"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS64 +#define ELF_MACHINE EM_S390 +#define VDSO_HEADER "vdso.c.inc" + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/s390/include/asm/elf.h, where + * elf_gregset_t is typedef'd to struct s390_regs. + */ +typedef struct target_elf_gregset_t { + struct target_s390_regs pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/s390x/target_proc.h b/linux-user/s390x/target_proc.h index a4a4821ea5..60cc22d3b4 100644 --- a/linux-user/s390x/target_proc.h +++ b/linux-user/s390x/target_proc.h @@ -48,7 +48,7 @@ static void show_cpu_summary(CPUArchState *cpu_env, int fd) { S390CPUModel *model = env_archcpu(cpu_env)->model; int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - uint32_t elf_hwcap = get_elf_hwcap(); + uint32_t elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); const char *hwcap_str; int i; diff --git a/linux-user/s390x/target_ptrace.h b/linux-user/s390x/target_ptrace.h new file mode 100644 index 0000000000..a5ceb75a74 --- /dev/null +++ b/linux-user/s390x/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef S390X_TARGET_PTRACE_H +#define S390X_TARGET_PTRACE_H + +typedef struct { + abi_ulong mask; + abi_ulong addr; +} target_psw_t; + +struct target_s390_regs { + target_psw_t psw; + abi_ulong gprs[16]; + abi_uint acrs[16]; + abi_ulong orig_gpr2; +}; + +#endif /* S390X_TARGET_PTRACE_H */ diff --git a/linux-user/s390x/target_syscall.h b/linux-user/s390x/target_syscall.h index 4018988a25..f01f9a0baa 100644 --- a/linux-user/s390x/target_syscall.h +++ b/linux-user/s390x/target_syscall.h @@ -1,28 +1,6 @@ #ifndef S390X_TARGET_SYSCALL_H #define S390X_TARGET_SYSCALL_H -/* this typedef defines how a Program Status Word looks like */ -typedef struct { - abi_ulong mask; - abi_ulong addr; -} __attribute__ ((aligned(8))) target_psw_t; - -/* - * The pt_regs struct defines the way the registers are stored on - * the stack during a system call. - */ - -#define TARGET_NUM_GPRS 16 - -struct target_pt_regs { - abi_ulong args[1]; - target_psw_t psw; - abi_ulong gprs[TARGET_NUM_GPRS]; - abi_ulong orig_gpr2; - unsigned short ilen; - unsigned short trap; -}; - #define UNAME_MACHINE "s390x" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index ee9eff3428..259ea1cc8b 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -81,12 +81,10 @@ void cpu_loop(CPUSH4State *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 16; i++) { - env->gregs[i] = regs->regs[i]; - } - env->pc = regs->pc; + env->pc = info->entry; + env->gregs[15] = info->start_stack; } diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c new file mode 100644 index 0000000000..ddf2aaaed7 --- /dev/null +++ b/linux-user/sh4/elfload.c @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "sh7785"; +} + +enum { + SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ + SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ + SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ + SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ + SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ + SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ + SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ + SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ + SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ + SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + abi_ulong hwcap = 0; + + hwcap |= SH_CPU_HAS_FPU; + + if (cpu->env.features & SH_FEATURE_SH4A) { + hwcap |= SH_CPU_HAS_LLSC; + } + + return hwcap; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) +{ + for (int i = 0; i < 16; i++) { + r->pt.regs[i] = tswapal(env->gregs[i]); + } + + r->pt.pc = tswapal(env->pc); + r->pt.pr = tswapal(env->pr); + r->pt.sr = tswapal(env->sr); + r->pt.gbr = tswapal(env->gbr); + r->pt.mach = tswapal(env->mach); + r->pt.macl = tswapal(env->macl); +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index f485e0cef2..d9e253d425 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -7,8 +7,21 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "sh7785"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_SH + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/sh/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_pt_regs pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/sh4/target_ptrace.h b/linux-user/sh4/target_ptrace.h new file mode 100644 index 0000000000..b80218526b --- /dev/null +++ b/linux-user/sh4/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SH4_TARGET_PTRACE_H +#define SH4_TARGET_PTRACE_H + +/* See arch/sh/include/uapi/asm/ptrace_32.h. */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong pc; + abi_ulong pr; + abi_ulong sr; + abi_ulong gbr; + abi_ulong mach; + abi_ulong macl; + abi_long tra; +}; + +#endif /* SH4_TARGET_PTRACE_H */ diff --git a/linux-user/sh4/target_syscall.h b/linux-user/sh4/target_syscall.h index 148398855d..2f3557742d 100644 --- a/linux-user/sh4/target_syscall.h +++ b/linux-user/sh4/target_syscall.h @@ -1,17 +1,6 @@ #ifndef SH4_TARGET_SYSCALL_H #define SH4_TARGET_SYSCALL_H -struct target_pt_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - long tra; -}; - #define UNAME_MACHINE "sh4" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 68f1e8ecd4..7d30cd1ff2 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -357,14 +357,12 @@ void cpu_loop (CPUSPARCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - env->pc = regs->pc; - env->npc = regs->npc; - env->y = regs->y; - for(i = 0; i < 8; i++) - env->gregs[i] = regs->u_regs[i]; - for(i = 0; i < 8; i++) - env->regwptr[i] = regs->u_regs[i + 8]; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->npc = env->pc + 4; + env->regwptr[WREG_SP] = (info->start_stack - 16 * sizeof(abi_ulong) + - TARGET_STACK_BIAS); } diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c new file mode 100644 index 0000000000..32ca1b05b1 --- /dev/null +++ b/linux-user/sparc/elfload.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_SPARC64 + return "TI UltraSparc II"; +#else + return "Fujitsu MB86904"; +#endif +} + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* There are not many sparc32 hwcap bits -- we have all of them. */ + uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | + HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; + +#ifdef TARGET_SPARC64 + CPUSPARCState *env = cpu_env(cs); + uint32_t features = env->def.features; + + r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; + /* 32x32 multiply and divide are efficient. */ + r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; + /* We don't have an internal feature bit for this. */ + r |= HWCAP_SPARC_POPC; + r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; + r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; + r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; + r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; + r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; + r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; +#endif + + return r; +} diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 8181b8b92c..d339f89928 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -21,6 +21,8 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" + /* A Sparc register window */ struct target_reg_window { diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index a510ceb612..7827767bcb 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -7,12 +7,18 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ -#ifdef TARGET_SPARC64 - return "TI UltraSparc II"; + +#ifndef TARGET_SPARC64 +# define ELF_CLASS ELFCLASS32 +# define ELF_MACHINE EM_SPARC +#elif defined(TARGET_ABI32) +# define ELF_CLASS ELFCLASS32 +# define elf_check_machine(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else - return "Fujitsu MB86904"; +# define ELF_CLASS ELFCLASS64 +# define ELF_MACHINE EM_SPARCV9 #endif -} + +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/sparc/target_ptrace.h b/linux-user/sparc/target_ptrace.h new file mode 100644 index 0000000000..a4d5416c1f --- /dev/null +++ b/linux-user/sparc/target_ptrace.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SPARC_TARGET_PTRACE_H +#define SPARC_TARGET_PTRACE_H + +/* See arch/sparc/include/uapi/asm/ptrace.h. */ +struct target_pt_regs { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + uint32_t y; + uint32_t magic; +#else + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +#endif +}; + +#endif /* SPARC_TARGET_PTRACE_H */ diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index e421165357..a90ed2983a 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -1,25 +1,6 @@ #ifndef SPARC_TARGET_SYSCALL_H #define SPARC_TARGET_SYSCALL_H -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - uint32_t y; - uint32_t magic; -}; -#else -struct target_pt_regs { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; -}; -#endif - #ifdef TARGET_SPARC64 # define UNAME_MACHINE "sparc64" #else diff --git a/linux-user/strace.c b/linux-user/strace.c index 3b744ccd4a..1233ebceb0 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -499,113 +499,119 @@ print_socket_type(int type) static void print_socket_protocol(int domain, int type, int protocol) { - if (domain == AF_PACKET || - (domain == AF_INET && type == TARGET_SOCK_PACKET)) { + const char *name = NULL; + + switch (domain) { + case AF_PACKET: switch (protocol) { - case 0x0003: - qemu_log("ETH_P_ALL"); + case 3: + name = "ETH_P_ALL"; break; - default: - qemu_log("%d", protocol); } - return; - } + break; - if (domain == PF_NETLINK) { + case PF_NETLINK: switch (protocol) { case NETLINK_ROUTE: - qemu_log("NETLINK_ROUTE"); + name = "NETLINK_ROUTE"; break; case NETLINK_UNUSED: - qemu_log("NETLINK_UNUSED"); + name = "NETLINK_UNUSED"; break; case NETLINK_USERSOCK: - qemu_log("NETLINK_USERSOCK"); + name = "NETLINK_USERSOCK"; break; case NETLINK_FIREWALL: - qemu_log("NETLINK_FIREWALL"); + name = "NETLINK_FIREWALL"; break; case NETLINK_SOCK_DIAG: - qemu_log("NETLINK_SOCK_DIAG"); + name = "NETLINK_SOCK_DIAG"; break; case NETLINK_NFLOG: - qemu_log("NETLINK_NFLOG"); + name = "NETLINK_NFLOG"; break; case NETLINK_XFRM: - qemu_log("NETLINK_XFRM"); + name = "NETLINK_XFRM"; break; case NETLINK_SELINUX: - qemu_log("NETLINK_SELINUX"); + name = "NETLINK_SELINUX"; break; case NETLINK_ISCSI: - qemu_log("NETLINK_ISCSI"); + name = "NETLINK_ISCSI"; break; case NETLINK_AUDIT: - qemu_log("NETLINK_AUDIT"); + name = "NETLINK_AUDIT"; break; case NETLINK_FIB_LOOKUP: - qemu_log("NETLINK_FIB_LOOKUP"); + name = "NETLINK_FIB_LOOKUP"; break; case NETLINK_CONNECTOR: - qemu_log("NETLINK_CONNECTOR"); + name = "NETLINK_CONNECTOR"; break; case NETLINK_NETFILTER: - qemu_log("NETLINK_NETFILTER"); + name = "NETLINK_NETFILTER"; break; case NETLINK_IP6_FW: - qemu_log("NETLINK_IP6_FW"); + name = "NETLINK_IP6_FW"; break; case NETLINK_DNRTMSG: - qemu_log("NETLINK_DNRTMSG"); + name = "NETLINK_DNRTMSG"; break; case NETLINK_KOBJECT_UEVENT: - qemu_log("NETLINK_KOBJECT_UEVENT"); + name = "NETLINK_KOBJECT_UEVENT"; break; case NETLINK_GENERIC: - qemu_log("NETLINK_GENERIC"); + name = "NETLINK_GENERIC"; break; case NETLINK_SCSITRANSPORT: - qemu_log("NETLINK_SCSITRANSPORT"); + name = "NETLINK_SCSITRANSPORT"; break; case NETLINK_ECRYPTFS: - qemu_log("NETLINK_ECRYPTFS"); + name = "NETLINK_ECRYPTFS"; break; case NETLINK_RDMA: - qemu_log("NETLINK_RDMA"); + name = "NETLINK_RDMA"; break; case NETLINK_CRYPTO: - qemu_log("NETLINK_CRYPTO"); + name = "NETLINK_CRYPTO"; break; case NETLINK_SMC: - qemu_log("NETLINK_SMC"); + name = "NETLINK_SMC"; break; - default: - qemu_log("%d", protocol); + } + break; + + case AF_INET: + case AF_INET6: + switch (protocol) { + case 3: + if (domain == AF_INET && type == TARGET_SOCK_PACKET) { + name = "ETH_P_ALL"; + } + break; + case IPPROTO_IP: + name = "IPPROTO_IP"; + break; + case IPPROTO_TCP: + name = "IPPROTO_TCP"; + break; + case IPPROTO_UDP: + name = "IPPROTO_UDP"; + break; + case IPPROTO_RAW: + name = "IPPROTO_RAW"; break; } - return; + break; } - switch (protocol) { - case IPPROTO_IP: - qemu_log("IPPROTO_IP"); - break; - case IPPROTO_TCP: - qemu_log("IPPROTO_TCP"); - break; - case IPPROTO_UDP: - qemu_log("IPPROTO_UDP"); - break; - case IPPROTO_RAW: - qemu_log("IPPROTO_RAW"); - break; - default: + if (name) { + qemu_log("%s", name); + } else { qemu_log("%d", protocol); - break; } } - #ifdef TARGET_NR__newselect static void print_fdset(int n, abi_ulong target_fds_addr) diff --git a/linux-user/strace.list b/linux-user/strace.list index ab818352a9..51b5ead969 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1719,3 +1719,6 @@ #ifdef TARGET_NR_riscv_hwprobe { TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_rseq +{ TARGET_NR_rseq, "rseq" , "%s(%p,%u,%d,%#x)", NULL, NULL }, +#endif diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c new file mode 100644 index 0000000000..1e7000c6bc --- /dev/null +++ b/linux-user/x86_64/elfload.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} + +const char *get_elf_platform(CPUState *cs) +{ + return "x86_64"; +} + +bool init_guest_commpage(void) +{ + /* + * The vsyscall page is at a high negative address aka kernel space, + * which means that we cannot actually allocate it with target_mmap. + * We still should be able to use page_set_flags, unless the user + * has specified -R reserved_va, which would trigger an assert(). + */ + if (reserved_va != 0 && + TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { + error_report("Cannot allocate vsyscall page"); + exit(EXIT_FAILURE); + } + page_set_flags(TARGET_VSYSCALL_PAGE, + TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->pt.r15 = tswapal(env->regs[15]); + r->pt.r14 = tswapal(env->regs[14]); + r->pt.r13 = tswapal(env->regs[13]); + r->pt.r12 = tswapal(env->regs[12]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.r11 = tswapal(env->regs[11]); + r->pt.r10 = tswapal(env->regs[10]); + r->pt.r9 = tswapal(env->regs[9]); + r->pt.r8 = tswapal(env->regs[8]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); + r->pt.fs_base = tswapal(env->segs[R_FS].base); + r->pt.gs_base = tswapal(env->segs[R_GS].base); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 3f628f8d66..840bddf5ec 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -7,8 +7,24 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS64 +#define ELF_MACHINE EM_X86_64 +#define VDSO_HEADER "vdso.c.inc" + +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 +#define HAVE_GUEST_COMMPAGE 1 + +/* + * See linux kernel: arch/x86/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_user_regs_struct pt; +} target_elf_gregset_t; + #endif diff --git a/linux-user/x86_64/target_ptrace.h b/linux-user/x86_64/target_ptrace.h new file mode 100644 index 0000000000..33527127cb --- /dev/null +++ b/linux-user/x86_64/target_ptrace.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef X86_64_TARGET_PTRACE_H +#define X86_64_TARGET_PTRACE_H + +/* + * The struct pt_regs in arch/x86/include/uapi/asm/ptrace.h has missing + * register values and is not used. See arch/x86/include/asm/user_64.h. + */ +struct target_user_regs_struct { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong bp; + abi_ulong bx; + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong ax; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; + abi_ulong fs_base; + abi_ulong gs_base; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; +}; + +#endif /* X86_64_TARGET_PTRACE_H */ diff --git a/linux-user/x86_64/target_syscall.h b/linux-user/x86_64/target_syscall.h index fb558345d3..68f55f8e7b 100644 --- a/linux-user/x86_64/target_syscall.h +++ b/linux-user/x86_64/target_syscall.h @@ -4,34 +4,6 @@ #define __USER_CS (0x33) #define __USER_DS (0x2B) -struct target_pt_regs { - abi_ulong r15; - abi_ulong r14; - abi_ulong r13; - abi_ulong r12; - abi_ulong rbp; - abi_ulong rbx; -/* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; - abi_ulong r10; - abi_ulong r9; - abi_ulong r8; - abi_ulong rax; - abi_ulong rcx; - abi_ulong rdx; - abi_ulong rsi; - abi_ulong rdi; - abi_ulong orig_rax; -/* end of arguments */ -/* cpu exception frame or undefined */ - abi_ulong rip; - abi_ulong cs; - abi_ulong eflags; - abi_ulong rsp; - abi_ulong ss; -/* top of stack page */ -}; - /* Maximum number of LDT entries supported. */ #define TARGET_LDT_ENTRIES 8192 /* The size of each LDT entry. */ diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index c0fcf743e7..43a194fc4a 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -238,12 +238,22 @@ void cpu_loop(CPUXtensaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; ++i) { - env->regs[i] = regs->areg[i]; + CPUArchState *env = cpu_env(cs); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->regs[1] = info->start_stack; + env->pc = info->entry; + + if (info_is_fdpic(info)) { + env->regs[4] = info->loadmap_addr; + env->regs[5] = info->interpreter_loadmap_addr; + if (info->interpreter_loadmap_addr) { + env->regs[6] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[6] = info->pt_dynamic_addr; + } } - env->sregs[WINDOW_START] = regs->windowstart; - env->pc = regs->pc; } diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c new file mode 100644 index 0000000000..68aeed855f --- /dev/null +++ b/linux-user/xtensa/elfload.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "target_elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return XTENSA_DEFAULT_CPU_MODEL; +} + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) +{ + r->pt.pc = tswap32(env->pc); + r->pt.ps = tswap32(env->sregs[PS] & ~PS_EXCM); + r->pt.lbeg = tswap32(env->sregs[LBEG]); + r->pt.lend = tswap32(env->sregs[LEND]); + r->pt.lcount = tswap32(env->sregs[LCOUNT]); + r->pt.sar = tswap32(env->sregs[SAR]); + r->pt.windowstart = tswap32(env->sregs[WINDOW_START]); + r->pt.windowbase = tswap32(env->sregs[WINDOW_BASE]); + r->pt.threadptr = tswap32(env->uregs[THREADPTR]); + + xtensa_sync_phys_from_window((CPUXtensaState *)env); + + for (unsigned i = 0; i < env->config->nareg; ++i) { + r->pt.a[i] = tswap32(env->phys_regs[i]); + } +} diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index a9a3fabd89..1bf8f2a14a 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,9 +8,19 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return XTENSA_DEFAULT_CPU_MODEL; -} +#include "target_ptrace.h" + +#define ELF_CLASS ELFCLASS32 +#define ELF_MACHINE EM_XTENSA + +#define HAVE_ELF_CORE_DUMP 1 + +/* + * See linux kernel: arch/xtensa/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_pt_regs via typedef and sizeof. + */ +typedef struct target_elf_gregset_t { + struct target_user_pt_regs pt; +} target_elf_gregset_t; #endif diff --git a/linux-user/xtensa/target_ptrace.h b/linux-user/xtensa/target_ptrace.h new file mode 100644 index 0000000000..32443d0dee --- /dev/null +++ b/linux-user/xtensa/target_ptrace.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef XTENSA_TARGET_PTRACE_H +#define XTENSA_TARGET_PTRACE_H + +/* See arch/xtensa/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint32_t pc; + uint32_t ps; + uint32_t lbeg; + uint32_t lend; + uint32_t lcount; + uint32_t sar; + uint32_t windowstart; + uint32_t windowbase; + uint32_t threadptr; + uint32_t syscall; + uint32_t reserved[6 + 48]; + uint32_t a[64]; +}; + +#endif /* XTENSA_TARGET_PTRACE_H */ diff --git a/linux-user/xtensa/target_syscall.h b/linux-user/xtensa/target_syscall.h index afc86a153f..5d4352a4d1 100644 --- a/linux-user/xtensa/target_syscall.h +++ b/linux-user/xtensa/target_syscall.h @@ -8,41 +8,6 @@ #define MMAP_SHIFT TARGET_PAGE_BITS -typedef uint32_t xtensa_reg_t; -typedef struct { -} xtregs_opt_t; /* TODO */ - -struct target_pt_regs { - xtensa_reg_t pc; /* 4 */ - xtensa_reg_t ps; /* 8 */ - xtensa_reg_t depc; /* 12 */ - xtensa_reg_t exccause; /* 16 */ - xtensa_reg_t excvaddr; /* 20 */ - xtensa_reg_t debugcause; /* 24 */ - xtensa_reg_t wmask; /* 28 */ - xtensa_reg_t lbeg; /* 32 */ - xtensa_reg_t lend; /* 36 */ - xtensa_reg_t lcount; /* 40 */ - xtensa_reg_t sar; /* 44 */ - xtensa_reg_t windowbase; /* 48 */ - xtensa_reg_t windowstart; /* 52 */ - xtensa_reg_t syscall; /* 56 */ - xtensa_reg_t icountlevel; /* 60 */ - xtensa_reg_t scompare1; /* 64 */ - xtensa_reg_t threadptr; /* 68 */ - - /* Additional configurable registers that are used by the compiler. */ - xtregs_opt_t xtregs_opt; - - /* Make sure the areg field is 16 bytes aligned. */ - int align[0] __attribute__ ((aligned(16))); - - /* current register frame. - * Note: The ESF for kernel exceptions ends after 16 registers! - */ - xtensa_reg_t areg[16]; -}; - #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 #define TARGET_MCL_ONFAULT 4 diff --git a/meson.build b/meson.build index 50c774a195..0d42de61ae 100644 --- a/meson.build +++ b/meson.build @@ -4327,7 +4327,11 @@ foreach target : target_dirs ) if 'CONFIG_LINUX_USER' in config_target dir = base_dir / abi - arch_srcs += files(dir / 'signal.c', dir / 'cpu_loop.c') + arch_srcs += files( + dir / 'cpu_loop.c', + dir / 'elfload.c', + dir / 'signal.c', + ) if config_target.has_key('TARGET_SYSTBL_ABI') arch_srcs += \ syscall_nr_generators[abi].process(base_dir / abi / config_target['TARGET_SYSTBL'], diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index f102527c4d..9aed266df1 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -184,6 +184,10 @@ class Matcher: ) ) + def __str__(self) -> str: + """String representation delegated to the backend.""" + return str(self._m) + def __repr__(self) -> str: """Stable debug representation delegated to the backend.""" return repr(self._m) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b785c718f3..4baf6ba663 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0868e1b426..6f8884eb30 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -15,6 +15,11 @@ license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" rust-version = "1.77.0" +[workspace.dependencies] +anyhow = "~1.0" +foreign = "~0.3.1" +libc = "0.2.162" + [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', @@ -53,7 +58,6 @@ as_ptr_cast_mut = "deny" as_underscore = "deny" assertions_on_result_states = "deny" bool_to_int_with_if = "deny" -borrow_as_ptr = "deny" cast_lossless = "deny" dbg_macro = "deny" debug_assert_with_mut_call = "deny" diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index b525d89c09..959726efe6 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -85,7 +85,15 @@ fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream is_c_repr(&input, "#[derive(Object)]")?; let name = &input.ident; - let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; + let parent = &get_fields(&input, "#[derive(Object)]")? + .get(0) + .ok_or_else(|| { + Error::new( + input.ident.span(), + "#[derive(Object)] requires a parent field", + ) + })? + .ident; Ok(quote! { ::qemu_api::assert_field_type!(#name, #parent, @@ -115,23 +123,21 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; let typ = &field.ty; - // TODO: how to add "::qemu_api"? For now, this is only used in the - // qemu_api crate so it's not a problem. Ok(quote! { - unsafe impl crate::cell::Wrapper for #name { - type Wrapped = <#typ as crate::cell::Wrapper>::Wrapped; + unsafe impl ::qemu_api::cell::Wrapper for #name { + type Wrapped = <#typ as ::qemu_api::cell::Wrapper>::Wrapped; } impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut <Self as crate::cell::Wrapper>::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut <Self as crate::cell::Wrapper>::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const <Self as crate::cell::Wrapper>::Wrapped { + pub const fn as_ptr(&self) -> *const <Self as ::qemu_api::cell::Wrapper>::Wrapped { self.0.as_ptr() } @@ -139,7 +145,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream self.0.as_void_ptr() } - pub const fn raw_get(slot: *mut Self) -> *mut <Self as crate::cell::Wrapper>::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::qemu_api::cell::Wrapper>::Wrapped { slot.cast() } } diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index d6dcd62fcf..6028cdbc4c 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -7,9 +7,9 @@ use quote::quote; use super::*; macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ let input: proc_macro2::TokenStream = $input; - let error_msg: &str = $error_msg; + let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> = $derive_fn; @@ -18,7 +18,7 @@ macro_rules! derive_compile_fail { let err = result.unwrap_err().into_compile_error(); assert_eq!( err.to_string(), - quote! { ::core::compile_error! { #error_msg } }.to_string() + quote! { #(#error_msg)* }.to_string() ); }}; } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index db7000dee4..c07a17a28b 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,9 +15,9 @@ rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } -anyhow = "~1.0" -libc = "0.2.162" -foreign = "~0.3.1" +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } [features] default = ["debug_cell"] diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 86dcd8ef17..bcb51c7986 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -32,6 +32,10 @@ pub mod uninit; pub mod vmstate; pub mod zeroable; +// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this +// crate). +extern crate self as qemu_api; + use std::{ alloc::{GlobalAlloc, Layout}, ffi::c_void, diff --git a/scripts/kernel-doc b/scripts/kernel-doc deleted file mode 100755 index fec83f53ed..0000000000 --- a/scripts/kernel-doc +++ /dev/null @@ -1,2442 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-only - -use warnings; -use strict; - -## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## -## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ## -## Copyright (C) 2001 Simon Huggins ## -## Copyright (C) 2005-2012 Randy Dunlap ## -## Copyright (C) 2012 Dan Luedtke ## -## ## -## #define enhancements by Armin Kuster <akuster@mvista.com> ## -## Copyright (c) 2000 MontaVista Software, Inc. ## -## ## -## This software falls under the GNU General Public License. ## -## Please read the COPYING file for more information ## - -# 18/01/2001 - Cleanups -# Functions prototyped as foo(void) same as foo() -# Stop eval'ing where we don't need to. -# -- huggie@earth.li - -# 27/06/2001 - Allowed whitespace after initial "/**" and -# allowed comments before function declarations. -# -- Christian Kreibich <ck@whoop.org> - -# Still to do: -# - add perldoc documentation -# - Look more closely at some of the scarier bits :) - -# 26/05/2001 - Support for separate source and object trees. -# Return error code. -# Keith Owens <kaos@ocs.com.au> - -# 23/09/2001 - Added support for typedefs, structs, enums and unions -# Support for Context section; can be terminated using empty line -# Small fixes (like spaces vs. \s in regex) -# -- Tim Jansen <tim@tjansen.de> - -# 25/07/2012 - Added support for HTML5 -# -- Dan Luedtke <mail@danrl.de> - -sub usage { - my $message = <<"EOF"; -Usage: $0 [OPTION ...] FILE ... - -Read C language source or header FILEs, extract embedded documentation comments, -and print formatted documentation to standard output. - -The documentation comments are identified by "/**" opening comment mark. See -Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. - -Output format selection (mutually exclusive): - -man Output troff manual page format. This is the default. - -rst Output reStructuredText format. - -none Do not output documentation, only warnings. - -Output format selection modifier (affects only ReST output): - - -sphinx-version Use the ReST C domain dialect compatible with an - specific Sphinx Version. - If not specified, kernel-doc will auto-detect using - the sphinx-build version found on PATH. - -Output selection (mutually exclusive): - -export Only output documentation for symbols that have been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -internal Only output documentation for symbols that have NOT been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -function NAME Only output documentation for the given function(s) - or DOC: section title(s). All other functions and DOC: - sections are ignored. May be specified multiple times. - -nosymbol NAME Exclude the specified symbols from the output - documentation. May be specified multiple times. - -Output selection modifiers: - -no-doc-sections Do not output DOC: sections. - -enable-lineno Enable output of #define LINENO lines. Only works with - reStructuredText format. - -export-file FILE Specify an additional FILE in which to look for - EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with - -export or -internal. May be specified multiple times. - -Other parameters: - -v Verbose output, more warnings and other information. - -h Print this help. - -Werror Treat warnings as errors. - -EOF - print $message; - exit 1; -} - -# -# format of comments. -# In the following table, (...)? signifies optional structure. -# (...)* signifies 0 or more structure elements -# /** -# * function_name(:)? (- short description)? -# (* @parameterx: (description of parameter x)?)* -# (* a blank line)? -# * (Description:)? (Description of function)? -# * (section header: (section description)? )* -# (*)?*/ -# -# So .. the trivial example would be: -# -# /** -# * my_function -# */ -# -# If the Description: header tag is omitted, then there must be a blank line -# after the last parameter specification. -# e.g. -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * -# * Does my stuff explained. -# */ -# -# or, could also use: -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * Description: Does my stuff explained. -# */ -# etc. -# -# Besides functions you can also write documentation for structs, unions, -# enums and typedefs. Instead of the function name you must write the name -# of the declaration; the struct/union/enum/typedef must always precede -# the name. Nesting of declarations is not supported. -# Use the argument mechanism to document members or constants. -# e.g. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /* private: */ -# int c; -# }; -# -# All descriptions can be multiline, except the short function description. -# -# For really longs structs, you can also describe arguments inside the -# body of the struct. -# eg. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /** -# * @c: This is longer description of C -# * -# * You can use paragraphs to describe arguments -# * using this method. -# */ -# int c; -# }; -# -# This should be use only for struct/enum members. -# -# You can also add additional sections. When documenting kernel functions you -# should document the "Context:" of the function, e.g. whether the functions -# can be called form interrupts. Unlike other sections you can end it with an -# empty line. -# A non-void function should have a "Return:" section describing the return -# value(s). -# Example-sections should contain the string EXAMPLE so that they are marked -# appropriately in DocBook. -# -# Example: -# /** -# * user_function - function that can only be called in user context -# * @a: some argument -# * Context: !in_interrupt() -# * -# * Some description -# * Example: -# * user_function(22); -# */ -# ... -# -# -# All descriptive text is further processed, scanning for the following special -# patterns, which are highlighted appropriately. -# -# 'funcname()' - function -# '$ENVVAR' - environmental variable -# '&struct_name' - name of a structure (up to two words including 'struct') -# '&struct_name.member' - name of a structure member -# '@parameter' - name of a parameter -# '%CONST' - name of a constant. -# '``LITERAL``' - literal string without any spaces on it. - -## init lots of data - -my $errors = 0; -my $warnings = 0; -my $anon_struct_union = 0; - -# match expressions used to find embedded type information -my $type_constant = '\b``([^\`]+)``\b'; -my $type_constant2 = '\%([-_\w]+)'; -my $type_func = '(\w+)\(\)'; -my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params -my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params -my $type_env = '(\$\w+)'; -my $type_enum = '#(enum\s*([_\w]+))'; -my $type_struct = '#(struct\s*([_\w]+))'; -my $type_typedef = '#(([A-Z][_\w]*))'; -my $type_union = '#(union\s*([_\w]+))'; -my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; -my $type_fallback = '(?!)'; # this never matches -my $type_member_func = $type_member . '\(\)'; - -# Output conversion substitutions. -# One for each output format - -# these are pretty rough -my @highlights_man = ( - [$type_constant, "\$1"], - [$type_constant2, "\$1"], - [$type_func, "\\\\fB\$1\\\\fP"], - [$type_enum, "\\\\fI\$1\\\\fP"], - [$type_struct, "\\\\fI\$1\\\\fP"], - [$type_typedef, "\\\\fI\$1\\\\fP"], - [$type_union, "\\\\fI\$1\\\\fP"], - [$type_param, "\\\\fI\$1\\\\fP"], - [$type_param_ref, "\\\\fI\$1\$2\\\\fP"], - [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], - [$type_fallback, "\\\\fI\$1\\\\fP"] - ); -my $blankline_man = ""; - -# rst-mode -my @highlights_rst = ( - [$type_constant, "``\$1``"], - [$type_constant2, "``\$1``"], - # Note: need to escape () to avoid func matching later - [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], - [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], - [$type_fp_param, "**\$1\\\\(\\\\)**"], - [$type_fp_param2, "**\$1\\\\(\\\\)**"], - [$type_func, "\$1()"], - [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], - # in rst this can refer to any type - [$type_fallback, "\\:c\\:type\\:`\$1`"], - [$type_param_ref, "**\$1\$2**"] - ); -my $blankline_rst = "\n"; - -# read arguments -if ($#ARGV == -1) { - usage(); -} - -my $kernelversion; -my ($sphinx_major, $sphinx_minor, $sphinx_patch); - -my $dohighlight = ""; - -my $verbose = 0; -my $Werror = 0; -my $output_mode = "rst"; -my $output_preformatted = 0; -my $no_doc_sections = 0; -my $enable_lineno = 0; -my @highlights = @highlights_rst; -my $blankline = $blankline_rst; -my $modulename = "Kernel API"; - -use constant { - OUTPUT_ALL => 0, # output all symbols and doc sections - OUTPUT_INCLUDE => 1, # output only specified symbols - OUTPUT_EXPORTED => 2, # output exported symbols - OUTPUT_INTERNAL => 3, # output non-exported symbols -}; -my $output_selection = OUTPUT_ALL; -my $show_not_found = 0; # No longer used - -my @export_file_list; - -my @build_time; -if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && - (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { - @build_time = gmtime($seconds); -} else { - @build_time = localtime; -} - -my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', - 'November', 'December')[$build_time[4]] . - " " . ($build_time[5]+1900); - -# Essentially these are globals. -# They probably want to be tidied up, made more localised or something. -# CAVEAT EMPTOR! Some of the others I localised may not want to be, which -# could cause "use of undefined value" or other bugs. -my ($function, %function_table, %parametertypes, $declaration_purpose); -my %nosymbol_table = (); -my $declaration_start_line; -my ($type, $declaration_name, $return_type); -my ($newsection, $newcontents, $prototype, $brcount, %source_map); - -if (defined($ENV{'KBUILD_VERBOSE'})) { - $verbose = "$ENV{'KBUILD_VERBOSE'}"; -} - -if (defined($ENV{'KDOC_WERROR'})) { - $Werror = "$ENV{'KDOC_WERROR'}"; -} - -if (defined($ENV{'KCFLAGS'})) { - my $kcflags = "$ENV{'KCFLAGS'}"; - - if ($kcflags =~ /Werror/) { - $Werror = 1; - } -} - -# Generated docbook code is inserted in a template at a point where -# docbook v3.1 requires a non-zero sequence of RefEntry's; see: -# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html -# We keep track of number of generated entries and generate a dummy -# if needs be to ensure the expanded template can be postprocessed -# into html. -my $section_counter = 0; - -my $lineprefix=""; - -# Parser states -use constant { - STATE_NORMAL => 0, # normal code - STATE_NAME => 1, # looking for function name - STATE_BODY_MAYBE => 2, # body - or maybe more description - STATE_BODY => 3, # the body of the comment - STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line - STATE_PROTO => 5, # scanning prototype - STATE_DOCBLOCK => 6, # documentation block - STATE_INLINE => 7, # gathering doc outside main block -}; -my $state; -my $in_doc_sect; -my $leading_space; - -# Inline documentation state -use constant { - STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) - STATE_INLINE_NAME => 1, # looking for member name (@foo:) - STATE_INLINE_TEXT => 2, # looking for member documentation - STATE_INLINE_END => 3, # done - STATE_INLINE_ERROR => 4, # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. -}; -my $inline_doc_state; - -#declaration types: can be -# 'function', 'struct', 'union', 'enum', 'typedef' -my $decl_type; - -my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. -my $doc_end = '\*/'; -my $doc_com = '\s*\*\s*'; -my $doc_com_body = '\s*\* ?'; -my $doc_decl = $doc_com . '(\w+)'; -# @params and a strictly limited set of supported section names -my $doc_sect = $doc_com . - '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; -my $doc_content = $doc_com_body . '(.*)'; -my $doc_block = $doc_com . 'DOC:\s*(.*)?'; -my $doc_inline_start = '^\s*/\*\*\s*$'; -my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; -my $doc_inline_end = '^\s*\*/\s*$'; -my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; -my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; - -my %parameterdescs; -my %parameterdesc_start_lines; -my @parameterlist; -my %sections; -my @sectionlist; -my %section_start_lines; -my $sectcheck; -my $struct_actual; - -my $contents = ""; -my $new_start_line = 0; - -# the canonical section names. see also $doc_sect above. -my $section_default = "Description"; # default section -my $section_intro = "Introduction"; -my $section = $section_default; -my $section_context = "Context"; -my $section_return = "Return"; - -my $undescribed = "-- undescribed --"; - -reset_state(); - -while ($ARGV[0] =~ m/^--?(.*)/) { - my $cmd = $1; - shift @ARGV; - if ($cmd eq "man") { - $output_mode = "man"; - @highlights = @highlights_man; - $blankline = $blankline_man; - } elsif ($cmd eq "rst") { - $output_mode = "rst"; - @highlights = @highlights_rst; - $blankline = $blankline_rst; - } elsif ($cmd eq "none") { - $output_mode = "none"; - } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document - $modulename = shift @ARGV; - } elsif ($cmd eq "function") { # to only output specific functions - $output_selection = OUTPUT_INCLUDE; - $function = shift @ARGV; - $function_table{$function} = 1; - } elsif ($cmd eq "nosymbol") { # Exclude specific symbols - my $symbol = shift @ARGV; - $nosymbol_table{$symbol} = 1; - } elsif ($cmd eq "export") { # only exported symbols - $output_selection = OUTPUT_EXPORTED; - %function_table = (); - } elsif ($cmd eq "internal") { # only non-exported symbols - $output_selection = OUTPUT_INTERNAL; - %function_table = (); - } elsif ($cmd eq "export-file") { - my $file = shift @ARGV; - push(@export_file_list, $file); - } elsif ($cmd eq "v") { - $verbose = 1; - } elsif ($cmd eq "Werror") { - $Werror = 1; - } elsif (($cmd eq "h") || ($cmd eq "help")) { - usage(); - } elsif ($cmd eq 'no-doc-sections') { - $no_doc_sections = 1; - } elsif ($cmd eq 'enable-lineno') { - $enable_lineno = 1; - } elsif ($cmd eq 'show-not-found') { - $show_not_found = 1; # A no-op but don't fail - } elsif ($cmd eq "sphinx-version") { - my $ver_string = shift @ARGV; - if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) { - $sphinx_major = $1; - if (defined($2)) { - $sphinx_minor = substr($2,1); - } else { - $sphinx_minor = 0; - } - if (defined($3)) { - $sphinx_patch = substr($3,1) - } else { - $sphinx_patch = 0; - } - } else { - die "Sphinx version should either major.minor or major.minor.patch format\n"; - } - } else { - # Unknown argument - usage(); - } -} - -# continue execution near EOF; - -# The C domain dialect changed on Sphinx 3. So, we need to check the -# version in order to produce the right tags. -sub findprog($) -{ - foreach(split(/:/, $ENV{PATH})) { - return "$_/$_[0]" if(-x "$_/$_[0]"); - } -} - -sub get_sphinx_version() -{ - my $ver; - - my $cmd = "sphinx-build"; - if (!findprog($cmd)) { - my $cmd = "sphinx-build3"; - if (!findprog($cmd)) { - $sphinx_major = 1; - $sphinx_minor = 2; - $sphinx_patch = 0; - printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n", - $sphinx_major, $sphinx_minor, $sphinx_patch; - return; - } - } - - open IN, "$cmd --version 2>&1 |"; - while (<IN>) { - if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - # Sphinx 1.2.x uses a different format - if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - } - close IN; -} - -# get kernel version from env -sub get_kernel_version() { - my $version = 'unknown kernel version'; - - if (defined($ENV{'KERNELVERSION'})) { - $version = $ENV{'KERNELVERSION'}; - } - return $version; -} - -# -sub print_lineno { - my $lineno = shift; - if ($enable_lineno && defined($lineno)) { - print "#define LINENO " . $lineno . "\n"; - } -} -## -# dumps section contents to arrays/hashes intended for that purpose. -# -sub dump_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($name =~ m/$type_param/) { - $name = $1; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } elsif ($name eq "@\.\.\.") { - $name = "..."; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } else { - if (defined($sections{$name}) && ($sections{$name} ne "")) { - # Only warn on user specified duplicate section names. - if ($name ne $section_default) { - print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; - ++$warnings; - } - $sections{$name} .= $contents; - } else { - $sections{$name} = $contents; - push @sectionlist, $name; - $section_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } - } -} - -## -# dump DOC: section after checking that it should go out -# -sub dump_doc_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($no_doc_sections) { - return; - } - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE) && - defined($function_table{$name}))) - { - dump_section($file, $name, $contents); - output_blockhead({'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'module' => $modulename, - 'content-only' => ($output_selection != OUTPUT_ALL), }); - } -} - -## -# output function -# -# parameterdescs, a hash. -# function => "function name" -# parameterlist => @list of parameters -# parameterdescs => %parameter descriptions -# sectionlist => @list of sections -# sections => %section descriptions -# - -sub output_highlight { - my $contents = join "\n",@_; - my $line; - -# DEBUG -# if (!defined $contents) { -# use Carp; -# confess "output_highlight got called with no args?\n"; -# } - -# print STDERR "contents b4:$contents\n"; - eval $dohighlight; - die $@ if $@; -# print STDERR "contents af:$contents\n"; - - foreach $line (split "\n", $contents) { - if (! $output_preformatted) { - $line =~ s/^\s*//; - } - if ($line eq ""){ - if (! $output_preformatted) { - print $lineprefix, $blankline; - } - } else { - if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { - print "\\&$line"; - } else { - print $lineprefix, $line; - } - } - print "\n"; - } -} - -## -# output function in man -sub output_function_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - if ($args{'functiontype'} ne "") { - print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; - } else { - print ".B \"" . $args{'function'} . "\n"; - } - $count = 0; - my $parenth = "("; - my $post = ","; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count == $#{$args{'parameterlist'}}) { - $post = ");"; - } - $type = $args{'parametertypes'}{$parameter}; - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n"; - } else { - $type =~ s/([^\*])$/$1 /; - print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n"; - } - $count++; - $parenth = ""; - } - - print ".SH ARGUMENTS\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"", uc $section, "\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output enum in man -sub output_enum_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - print "enum " . $args{'enum'} . " {\n"; - $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - print ".br\n.BI \" $parameter\"\n"; - if ($count == $#{$args{'parameterlist'}}) { - print "\n};\n"; - last; - } - else { - print ", \n.br\n"; - } - $count++; - } - - print ".SH Constants\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output struct in man -sub output_struct_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; - - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - $declaration =~ s/\n/"\n.br\n.BI \"/g; - print ".SH SYNOPSIS\n"; - print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; - print ".BI \"$declaration\n};\n.br\n\n"; - - print ".SH Members\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output typedef in man -sub output_typedef_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -sub output_blockhead_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output in restructured text -# - -# -# This could use some work; it's used to output the DOC: sections, and -# starts by putting out the name of the doc section itself, but that tends -# to duplicate a header already in the template file. -# -sub output_blockhead_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - foreach $section (@{$args{'sectionlist'}}) { - next if (defined($nosymbol_table{$section})); - - if ($output_selection != OUTPUT_INCLUDE) { - print "**$section**\n\n"; - } - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } -} - -# -# Apply the RST highlights to a sub-block of text. -# -sub highlight_block($) { - # The dohighlight kludge requires the text be called $contents - my $contents = shift; - eval $dohighlight; - die $@ if $@; - return $contents; -} - -# -# Regexes used only here. -# -my $sphinx_literal = '^[^.].*::$'; -my $sphinx_cblock = '^\.\.\ +code-block::'; - -sub output_highlight_rst { - my $input = join "\n",@_; - my $output = ""; - my $line; - my $in_literal = 0; - my $litprefix; - my $block = ""; - - foreach $line (split "\n",$input) { - # - # If we're in a literal block, see if we should drop out - # of it. Otherwise pass the line straight through unmunged. - # - if ($in_literal) { - if (! ($line =~ /^\s*$/)) { - # - # If this is the first non-blank line in a literal - # block we need to figure out what the proper indent is. - # - if ($litprefix eq "") { - $line =~ /^(\s*)/; - $litprefix = '^' . $1; - $output .= $line . "\n"; - } elsif (! ($line =~ /$litprefix/)) { - $in_literal = 0; - } else { - $output .= $line . "\n"; - } - } else { - $output .= $line . "\n"; - } - } - # - # Not in a literal block (or just dropped out) - # - if (! $in_literal) { - $block .= $line . "\n"; - if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { - $in_literal = 1; - $litprefix = ""; - $output .= highlight_block($block); - $block = "" - } - } - } - - if ($block) { - $output .= highlight_block($block); - } - foreach $line (split "\n", $output) { - print $lineprefix . $line . "\n"; - } -} - -sub output_function_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $oldprefix = $lineprefix; - my $start = ""; - my $is_macro = 0; - - if ($sphinx_major < 3) { - if ($args{'typedef'}) { - print ".. c:type:: ". $args{'function'} . "\n\n"; - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - $is_macro = 1; - } else { - print ".. c:function:: "; - } - } else { - if ($args{'typedef'} || $args{'functiontype'} eq "") { - $is_macro = 1; - print ".. c:macro:: ". $args{'function'} . "\n\n"; - } else { - print ".. c:function:: "; - } - - if ($args{'typedef'}) { - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - } else { - print "``" if ($is_macro); - } - } - if ($args{'functiontype'} ne "") { - $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; - } else { - $start .= $args{'function'} . " ("; - } - print $start; - - my $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count ne 0) { - print ", "; - } - $count++; - $type = $args{'parametertypes'}{$parameter}; - - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print $1 . $parameter . ") (" . $2 . ")"; - } else { - print $type; - } - } - if ($is_macro) { - print ")``\n\n"; - } else { - print ")\n\n"; - } - if (!$args{'typedef'}) { - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - } - - print "**Parameters**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - $type = $args{'parametertypes'}{$parameter}; - - if ($type ne "") { - print "``$type``\n"; - } else { - print "``$parameter``\n"; - } - - print_lineno($parameterdesc_start_lines{$parameter_name}); - - if (defined($args{'parameterdescs'}{$parameter_name}) && - $args{'parameterdescs'}{$parameter_name} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_section_rst(%) { - my %args = %{$_[0]}; - my $section; - my $oldprefix = $lineprefix; - $lineprefix = ""; - - foreach $section (@{$args{'sectionlist'}}) { - print "**$section**\n\n"; - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } - print "\n"; - $lineprefix = $oldprefix; -} - -sub output_enum_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $count; - - if ($sphinx_major < 3) { - my $name = "enum " . $args{'enum'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'enum'}; - print "\n\n.. c:enum:: " . $name . "\n\n"; - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Constants**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - print "``$parameter``\n"; - if ($args{'parameterdescs'}{$parameter} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_typedef_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $name; - - if ($sphinx_major < 3) { - $name = "typedef " . $args{'typedef'}; - } else { - $name = $args{'typedef'}; - } - print "\n\n.. c:type:: " . $name . "\n\n"; - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_struct_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - - if ($sphinx_major < 3) { - my $name = $args{'type'} . " " . $args{'struct'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'struct'}; - if ($args{'type'} eq 'union') { - print "\n\n.. c:union:: " . $name . "\n\n"; - } else { - print "\n\n.. c:struct:: " . $name . "\n\n"; - } - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Definition**\n\n"; - print "::\n\n"; - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - print " " . $args{'type'} . " " . $args{'struct'} . " {\n$declaration };\n\n"; - - print "**Members**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - $type = $args{'parametertypes'}{$parameter}; - print_lineno($parameterdesc_start_lines{$parameter_name}); - print "``" . $parameter . "``\n"; - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - print "\n"; - } - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -## none mode output functions - -sub output_function_none(%) { -} - -sub output_enum_none(%) { -} - -sub output_typedef_none(%) { -} - -sub output_struct_none(%) { -} - -sub output_blockhead_none(%) { -} - -## -# generic output function for all types (function, struct/union, typedef, enum); -# calls the generated, variable output_ function name based on -# functype and output_mode -sub output_declaration { - no strict 'refs'; - my $name = shift; - my $functype = shift; - my $func = "output_${functype}_$output_mode"; - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE || - $output_selection == OUTPUT_EXPORTED) && - defined($function_table{$name})) || - ($output_selection == OUTPUT_INTERNAL && - !($functype eq "function" && defined($function_table{$name})))) - { - &$func(@_); - $section_counter++; - } -} - -## -# generic output function - calls the right one based on current output mode. -sub output_blockhead { - no strict 'refs'; - my $func = "output_blockhead_" . $output_mode; - &$func(@_); - $section_counter++; -} - -## -# takes a declaration (struct, union, enum, typedef) and -# invokes the right handler. NOT called for functions. -sub dump_declaration($$) { - no strict 'refs'; - my ($prototype, $file) = @_; - my $func = "dump_" . $decl_type; - &$func(@_); -} - -sub dump_union($$) { - dump_struct(@_); -} - -sub dump_struct($$) { - my $x = shift; - my $file = shift; - - if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) { - my $decl_type = $1; - $declaration_name = $2; - my $members = $3; - - # ignore members marked private: - $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; - $members =~ s/\/\*\s*private:.*//gosi; - # strip comments: - $members =~ s/\/\*.*?\*\///gos; - # strip attributes - $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)/ /gi; - $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos; - $members =~ s/\s*__packed\s*/ /gos; - $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos; - $members =~ s/\s*____cacheline_aligned_in_smp/ /gos; - $members =~ s/\s*____cacheline_aligned/ /gos; - - # replace DECLARE_BITMAP - $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos; - $members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; - # replace DECLARE_HASHTABLE - $members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos; - # replace DECLARE_KFIFO - $members =~ s/DECLARE_KFIFO\s*\(([^,)]+),\s*([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - # replace DECLARE_KFIFO_PTR - $members =~ s/DECLARE_KFIFO_PTR\s*\(([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - - my $declaration = $members; - - # Split nested struct/union elements as newer ones - while ($members =~ m/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/) { - my $newmember; - my $maintype = $1; - my $ids = $4; - my $content = $3; - foreach my $id(split /,/, $ids) { - $newmember .= "$maintype $id; "; - - $id =~ s/[:\[].*//; - $id =~ s/^\s*\**(\S+)\s*/$1/; - foreach my $arg (split /;/, $content) { - next if ($arg =~ m/^\s*$/); - if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { - # pointer-to-function - my $type = $1; - my $name = $2; - my $extra = $3; - next if (!$name); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type$name$extra; "; - } else { - $newmember .= "$type$id.$name$extra; "; - } - } else { - my $type; - my $names; - $arg =~ s/^\s+//; - $arg =~ s/\s+$//; - # Handle bitmaps - $arg =~ s/:\s*\d+\s*//g; - # Handle arrays - $arg =~ s/\[.*\]//g; - # The type may have multiple words, - # and multiple IDs can be defined, like: - # const struct foo, *bar, foobar - # So, we remove spaces when parsing the - # names, in order to match just names - # and commas for the names - $arg =~ s/\s*,\s*/,/g; - if ($arg =~ m/(.*)\s+([\S+,]+)/) { - $type = $1; - $names = $2; - } else { - $newmember .= "$arg; "; - next; - } - foreach my $name (split /,/, $names) { - $name =~ s/^\s*\**(\S+)\s*/$1/; - next if (($name =~ m/^\s*$/)); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type $name; "; - } else { - $newmember .= "$type $id.$name; "; - } - } - } - } - } - $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/; - } - - # Ignore other nested elements, like enums - $members =~ s/(\{[^\{\}]*\})//g; - - create_parameterlist($members, ';', $file, $declaration_name); - check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); - - # Adjust declaration for better display - $declaration =~ s/([\{;])/$1\n/g; - $declaration =~ s/\}\s+;/};/g; - # Better handle inlined enums - do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); - - my @def_args = split /\n/, $declaration; - my $level = 1; - $declaration = ""; - foreach my $clause (@def_args) { - $clause =~ s/^\s+//; - $clause =~ s/\s+$//; - $clause =~ s/\s+/ /; - next if (!$clause); - $level-- if ($clause =~ m/(\})/ && $level > 1); - if (!($clause =~ m/^\s*#/)) { - $declaration .= "\t" x $level; - } - $declaration .= "\t" . $clause . "\n"; - $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); - } - output_declaration($declaration_name, - 'struct', - {'struct' => $declaration_name, - 'module' => $modulename, - 'definition' => $declaration, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose, - 'type' => $decl_type - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; - ++$errors; - } -} - - -sub show_warnings($$) { - my $functype = shift; - my $name = shift; - - return 0 if (defined($nosymbol_table{$name})); - - return 1 if ($output_selection == OUTPUT_ALL); - - if ($output_selection == OUTPUT_EXPORTED) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INTERNAL) { - if (!($functype eq "function" && defined($function_table{$name}))) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INCLUDE) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - die("Please add the new output type at show_warnings()"); -} - -sub dump_enum($$) { - my $x = shift; - my $file = shift; - my $members; - - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - # strip #define macros inside enums - $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; - - if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) { - $declaration_name = $2; - $members = $1; - } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) { - $declaration_name = $1; - $members = $2; - } - - if ($declaration_name) { - my %_members; - - $members =~ s/\s+$//; - - foreach my $arg (split ',', $members) { - $arg =~ s/^\s*(\w+).*/$1/; - push @parameterlist, $arg; - if (!$parameterdescs{$arg}) { - $parameterdescs{$arg} = $undescribed; - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n"; - } - } - $_members{$arg} = 1; - } - - while (my ($k, $v) = each %parameterdescs) { - if (!exists($_members{$k})) { - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n"; - } - } - } - - output_declaration($declaration_name, - 'enum', - {'enum' => $declaration_name, - 'module' => $modulename, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - print STDERR "${file}:$.: error: Cannot parse enum!\n"; - ++$errors; - } -} - -my $typedef_type = qr { ((?:\s+[\w\*]+){1,8})\s* }x; -my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x; -my $typedef_args = qr { \s*\((.*)\); }x; - -my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x; -my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x; - -sub dump_typedef($$) { - my $x = shift; - my $file = shift; - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - - # Parse function typedef prototypes - if ($x =~ $typedef1 || $x =~ $typedef2) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - $return_type =~ s/^\s+//; - - create_parameterlist($args, ',', $file, $declaration_name); - - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - return; - } - - while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { - $x =~ s/\(*.\)\s*;$/;/; - $x =~ s/\[*.\]\s*;$/;/; - } - - if ($x =~ /typedef.*\s+(\w+)\s*;/) { - $declaration_name = $1; - - output_declaration($declaration_name, - 'typedef', - {'typedef' => $declaration_name, - 'module' => $modulename, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse typedef!\n"; - ++$errors; - } -} - -sub save_struct_actual($) { - my $actual = shift; - - # strip all spaces from the actual param so that it looks like one string item - $actual =~ s/\s*//g; - $struct_actual = $struct_actual . $actual . " "; -} - -sub create_parameterlist($$$$) { - my $args = shift; - my $splitter = shift; - my $file = shift; - my $declaration_name = shift; - my $type; - my $param; - - # temporarily replace commas inside function pointer definition - while ($args =~ /(\([^\),]+),/) { - $args =~ s/(\([^\),]+),/$1#/g; - } - - foreach my $arg (split($splitter, $args)) { - # strip comments - $arg =~ s/\/\*.*\*\///; - # strip leading/trailing spaces - $arg =~ s/^\s*//; - $arg =~ s/\s*$//; - $arg =~ s/\s+/ /; - - if ($arg =~ /^#/) { - # Treat preprocessor directive as a typeless variable just to fill - # corresponding data structures "correctly". Catch it later in - # output_* subs. - push_parameter($arg, "", "", $file); - } elsif ($arg =~ m/\(.+\)\s*\(/) { - # pointer-to-function - $arg =~ tr/#/,/; - $arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/; - $param = $1; - $type = $arg; - $type =~ s/([^\(]+\(\*?)\s*$param/$1/; - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } elsif ($arg) { - $arg =~ s/\s*:\s*/:/g; - $arg =~ s/\s*\[/\[/g; - - my @args = split('\s*,\s*', $arg); - if ($args[0] =~ m/\*/) { - $args[0] =~ s/(\*+)\s*/ $1/; - } - - my @first_arg; - if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { - shift @args; - push(@first_arg, split('\s+', $1)); - push(@first_arg, $2); - } else { - @first_arg = split('\s+', shift @args); - } - - unshift(@args, pop @first_arg); - $type = join " ", @first_arg; - - foreach $param (@args) { - if ($param =~ m/^(\*+)\s*(.*)/) { - save_struct_actual($2); - - push_parameter($2, "$type $1", $arg, $file, $declaration_name); - } - elsif ($param =~ m/(.*?):(\d+)/) { - if ($type ne "") { # skip unnamed bit-fields - save_struct_actual($1); - push_parameter($1, "$type:$2", $arg, $file, $declaration_name) - } - } - else { - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } - } - } - } -} - -sub push_parameter($$$$$) { - my $param = shift; - my $type = shift; - my $org_arg = shift; - my $file = shift; - my $declaration_name = shift; - - if (($anon_struct_union == 1) && ($type eq "") && - ($param eq "}")) { - return; # ignore the ending }; from anon. struct/union - } - - $anon_struct_union = 0; - $param =~ s/[\[\)].*//; - - if ($type eq "" && $param =~ /\.\.\.$/) - { - if (!$param =~ /\w\.\.\.$/) { - # handles unnamed variable parameters - $param = "..."; - } - elsif ($param =~ /\w\.\.\.$/) { - # for named variable parameters of the form `x...`, remove the dots - $param =~ s/\.\.\.$//; - } - if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { - $parameterdescs{$param} = "variable arguments"; - } - } - elsif ($type eq "" && ($param eq "" or $param eq "void")) - { - $param="void"; - $parameterdescs{void} = "no arguments"; - } - elsif ($type eq "" && ($param eq "struct" or $param eq "union")) - # handle unnamed (anonymous) union or struct: - { - $type = $param; - $param = "{unnamed_" . $param . "}"; - $parameterdescs{$param} = "anonymous\n"; - $anon_struct_union = 1; - } - - # warn if parameter has no description - # (but ignore ones starting with # as these are not parameters - # but inline preprocessor statements); - # Note: It will also ignore void params and unnamed structs/unions - if (!defined $parameterdescs{$param} && $param !~ /^#/) { - $parameterdescs{$param} = $undescribed; - - if (show_warnings($type, $declaration_name) && $param !~ /\./) { - print STDERR - "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; - ++$warnings; - } - } - - # strip spaces from $param so that it is one continuous string - # on @parameterlist; - # this fixes a problem where check_sections() cannot find - # a parameter like "addr[6 + 2]" because it actually appears - # as "addr[6", "+", "2]" on the parameter list; - # but it's better to maintain the param string unchanged for output, - # so just weaken the string compare in check_sections() to ignore - # "[blah" in a parameter string; - ###$param =~ s/\s*//g; - push @parameterlist, $param; - $org_arg =~ s/\s\s+/ /g; - $parametertypes{$param} = $org_arg; -} - -sub check_sections($$$$$) { - my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_; - my @sects = split ' ', $sectcheck; - my @prms = split ' ', $prmscheck; - my $err; - my ($px, $sx); - my $prm_clean; # strip trailing "[array size]" and/or beginning "*" - - foreach $sx (0 .. $#sects) { - $err = 1; - foreach $px (0 .. $#prms) { - $prm_clean = $prms[$px]; - $prm_clean =~ s/\[.*\]//; - $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - $prm_clean =~ s/\[.*//; - - ##$prm_clean =~ s/^\**//; - if ($prm_clean eq $sects[$sx]) { - $err = 0; - last; - } - } - if ($err) { - if ($decl_type eq "function") { - print STDERR "${file}:$.: warning: " . - "Excess function parameter " . - "'$sects[$sx]' " . - "description in '$decl_name'\n"; - ++$warnings; - } - } - } -} - -## -# Checks the section describing the return value of a function. -sub check_return_section { - my $file = shift; - my $declaration_name = shift; - my $return_type = shift; - - # Ignore an empty return type (It's a macro) - # Ignore functions with a "void" return type. (But don't ignore "void *") - if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { - return; - } - - if (!defined($sections{$section_return}) || - $sections{$section_return} eq "") { - print STDERR "${file}:$.: warning: " . - "No description found for return value of " . - "'$declaration_name'\n"; - ++$warnings; - } -} - -## -# takes a function prototype and the name of the current file being -# processed and spits out all the details stored in the global -# arrays/hashes. -sub dump_function($$) { - my $prototype = shift; - my $file = shift; - my $noret = 0; - - print_lineno($new_start_line); - - $prototype =~ s/^static +//; - $prototype =~ s/^extern +//; - $prototype =~ s/^asmlinkage +//; - $prototype =~ s/^inline +//; - $prototype =~ s/^__inline__ +//; - $prototype =~ s/^__inline +//; - $prototype =~ s/^__always_inline +//; - $prototype =~ s/^noinline +//; - $prototype =~ s/__init +//; - $prototype =~ s/__init_or_module +//; - $prototype =~ s/__meminit +//; - $prototype =~ s/__must_check +//; - $prototype =~ s/__weak +//; - $prototype =~ s/__sched +//; - $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; - my $define = $prototype =~ s/^#\s*define\s+//; #ak added - $prototype =~ s/__attribute__\s*\(\( - (?: - [\w\s]++ # attribute name - (?:\([^)]*+\))? # attribute arguments - \s*+,? # optional comma at the end - )+ - \)\)\s+//x; - - # Strip QEMU specific compiler annotations - $prototype =~ s/QEMU_[A-Z_]+ +//; - - # Yes, this truly is vile. We are looking for: - # 1. Return type (may be nothing if we're looking at a macro) - # 2. Function name - # 3. Function parameters. - # - # All the while we have to watch out for function pointer parameters - # (which IIRC is what the two sections are for), C types (these - # regexps don't even start to express all the possibilities), and - # so on. - # - # If you mess with these regexps, it's a good idea to check that - # the following functions' documentation still comes out right: - # - parport_register_device (function pointer parameters) - # - atomic_set (macro) - # - pci_match_device, __copy_to_user (long return type) - - if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { - # This is an object-like macro, it has no return type and no parameter - # list. - # Function-like macros are not allowed to have spaces between - # declaration_name and opening parenthesis (notice the \s+). - $return_type = $1; - $declaration_name = $2; - $noret = 1; - } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+\s*\w+\s*\*+\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - - create_parameterlist($args, ',', $file, $declaration_name); - } else { - print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; - return; - } - - my $prms = join " ", @parameterlist; - check_sections($file, $declaration_name, "function", $sectcheck, $prms); - - # This check emits a lot of warnings at the moment, because many - # functions don't have a 'Return' doc section. So until the number - # of warnings goes sufficiently down, the check is only performed in - # verbose mode. - # TODO: always perform the check. - if ($verbose && !$noret) { - check_return_section($file, $declaration_name, $return_type); - } - - # The function parser can be called with a typedef parameter. - # Handle it. - if ($return_type =~ /typedef/) { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } -} - -sub reset_state { - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $sectcheck = ""; - $struct_actual = ""; - $prototype = ""; - - $state = STATE_NORMAL; - $inline_doc_state = STATE_INLINE_NA; -} - -sub tracepoint_munge($) { - my $file = shift; - my $tracepointname = 0; - my $tracepointargs = 0; - - if ($prototype =~ m/TRACE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { - $tracepointname = $2; - } - $tracepointname =~ s/^\s+//; #strip leading whitespace - if ($prototype =~ m/TP_PROTO\((.*?)\)/) { - $tracepointargs = $1; - } - if (($tracepointname eq 0) || ($tracepointargs eq 0)) { - print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". - "$prototype\n"; - } else { - $prototype = "static inline void trace_$tracepointname($tracepointargs)"; - } -} - -sub syscall_munge() { - my $void = 0; - - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's -## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { - if ($prototype =~ m/SYSCALL_DEFINE0/) { - $void = 1; -## $prototype = "long sys_$1(void)"; - } - - $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name - if ($prototype =~ m/long (sys_.*?),/) { - $prototype =~ s/,/\(/; - } elsif ($void) { - $prototype =~ s/\)/\(void\)/; - } - - # now delete all of the odd-number commas in $prototype - # so that arg types & arg names don't have a comma between them - my $count = 0; - my $len = length($prototype); - if ($void) { - $len = 0; # skip the for-loop - } - for (my $ix = 0; $ix < $len; $ix++) { - if (substr($prototype, $ix, 1) eq ',') { - $count++; - if ($count % 2 == 1) { - substr($prototype, $ix, 1) = ' '; - } - } - } -} - -sub process_proto_function($$) { - my $x = shift; - my $file = shift; - - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { - # do nothing - } - elsif ($x =~ /([^\{]*)/) { - $prototype .= $1; - } - - if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { - $prototype =~ s@/\*.*?\*/@@gos; # strip comments. - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $prototype =~ s@^\s+@@gos; # strip leading spaces - - # Handle prototypes for function pointers like: - # int (*pcs_config)(struct foo) - $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos; - - if ($prototype =~ /SYSCALL_DEFINE/) { - syscall_munge(); - } - if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || - $prototype =~ /DEFINE_SINGLE_EVENT/) - { - tracepoint_munge($file); - } - dump_function($prototype, $file); - reset_state(); - } -} - -sub process_proto_type($$) { - my $x = shift; - my $file = shift; - - $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $x =~ s@^\s+@@gos; # strip leading spaces - $x =~ s@\s+$@@gos; # strip trailing spaces - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ /^#/) { - # To distinguish preprocessor directive from regular declaration later. - $x .= ";"; - } - - while (1) { - if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { - if( length $prototype ) { - $prototype .= " " - } - $prototype .= $1 . $2; - ($2 eq '{') && $brcount++; - ($2 eq '}') && $brcount--; - if (($2 eq ';') && ($brcount == 0)) { - dump_declaration($prototype, $file); - reset_state(); - last; - } - $x = $3; - } else { - $prototype .= $x; - last; - } - } -} - - -sub map_filename($) { - my $file; - my ($orig_file) = @_; - - if (defined($ENV{'SRCTREE'})) { - $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; - } else { - $file = $orig_file; - } - - if (defined($source_map{$file})) { - $file = $source_map{$file}; - } - - return $file; -} - -sub process_export_file($) { - my ($orig_file) = @_; - my $file = map_filename($orig_file); - - if (!open(IN,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - while (<IN>) { - if (/$export_symbol/) { - next if (defined($nosymbol_table{$2})); - $function_table{$2} = 1; - } - } - - close(IN); -} - -# -# Parsers for the various processing states. -# -# STATE_NORMAL: looking for the /** to begin everything. -# -sub process_normal() { - if (/$doc_start/o) { - $state = STATE_NAME; # next line is always the function name - $in_doc_sect = 0; - $declaration_start_line = $. + 1; - } -} - -# -# STATE_NAME: Looking for the "name - description" line -# -sub process_name($$) { - my $file = shift; - my $identifier; - my $descr; - - if (/$doc_block/o) { - $state = STATE_DOCBLOCK; - $contents = ""; - $new_start_line = $.; - - if ( $1 eq "" ) { - $section = $section_intro; - } else { - $section = $1; - } - } - elsif (/$doc_decl/o) { - $identifier = $1; - if (/\s*([\w\s]+?)(\s*-|:)/) { - $identifier = $1; - } - - $state = STATE_BODY; - # if there's no @param blocks need to set up default section - # here - $contents = ""; - $section = $section_default; - $new_start_line = $. + 1; - if (/[-:](.*)/) { - # strip leading/trailing/multiple spaces - $descr= $1; - $descr =~ s/^\s*//; - $descr =~ s/\s*$//; - $descr =~ s/\s+/ /g; - $declaration_purpose = $descr; - $state = STATE_BODY_MAYBE; - } else { - $declaration_purpose = ""; - } - - if (($declaration_purpose eq "") && $verbose) { - print STDERR "${file}:$.: warning: missing initial short description on line:\n"; - print STDERR $_; - ++$warnings; - } - - if ($identifier =~ m/^struct\b/) { - $decl_type = 'struct'; - } elsif ($identifier =~ m/^union\b/) { - $decl_type = 'union'; - } elsif ($identifier =~ m/^enum\b/) { - $decl_type = 'enum'; - } elsif ($identifier =~ m/^typedef\b/) { - $decl_type = 'typedef'; - } else { - $decl_type = 'function'; - } - - if ($verbose) { - print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; - } - } else { - print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", - " - I thought it was a doc line\n"; - ++$warnings; - $state = STATE_NORMAL; - } -} - - -# -# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. -# -sub process_body($$) { - my $file = shift; - - # Until all named variable macro parameters are - # documented using the bare name (`x`) rather than with - # dots (`x...`), strip the dots: - if ($section =~ /\w\.\.\.$/) { - $section =~ s/\.\.\.$//; - - if ($verbose) { - print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n"; - ++$warnings; - } - } - - if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) { - dump_section($file, $section, $contents); - $section = $section_default; - $new_start_line = $.; - $contents = ""; - } - - if (/$doc_sect/i) { # case insensitive for supported section names - $newsection = $1; - $newcontents = $2; - - # map the supported section names to the canonical names - if ($newsection =~ m/^description$/i) { - $newsection = $section_default; - } elsif ($newsection =~ m/^context$/i) { - $newsection = $section_context; - } elsif ($newsection =~ m/^returns?$/i) { - $newsection = $section_return; - } elsif ($newsection =~ m/^\@return$/) { - # special: @return is a section, not a param description - $newsection = $section_return; - } - - if (($contents ne "") && ($contents ne "\n")) { - if (!$in_doc_sect && $verbose) { - print STDERR "${file}:$.: warning: contents before sections\n"; - ++$warnings; - } - dump_section($file, $section, $contents); - $section = $section_default; - } - - $in_doc_sect = 1; - $state = STATE_BODY; - $contents = $newcontents; - $new_start_line = $.; - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - if ($contents ne "") { - $contents .= "\n"; - } - $section = $newsection; - $leading_space = undef; - } elsif (/$doc_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - # look for doc_com + <text> + doc_end: - if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { - print STDERR "${file}:$.: warning: suspicious ending line: $_"; - ++$warnings; - } - - $prototype = ""; - $state = STATE_PROTO; - $brcount = 0; - $new_start_line = $. + 1; - } elsif (/$doc_content/) { - if ($1 eq "") { - if ($section eq $section_context) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $new_start_line = $.; - $state = STATE_BODY; - } else { - if ($section ne $section_default) { - $state = STATE_BODY_WITH_BLANK_LINE; - } else { - $state = STATE_BODY; - } - $contents .= "\n"; - } - } elsif ($state == STATE_BODY_MAYBE) { - # Continued declaration purpose - chomp($declaration_purpose); - $declaration_purpose .= " " . $1; - $declaration_purpose =~ s/\s+/ /g; - } else { - my $cont = $1; - if ($section =~ m/^@/ || $section eq $section_context) { - if (!defined $leading_space) { - if ($cont =~ m/^(\s+)/) { - $leading_space = $1; - } else { - $leading_space = ""; - } - } - $cont =~ s/^$leading_space//; - } - $contents .= $cont . "\n"; - } - } else { - # i dont know - bad line? ignore. - print STDERR "${file}:$.: warning: bad line: $_"; - ++$warnings; - } -} - - -# -# STATE_PROTO: reading a function/whatever prototype. -# -sub process_proto($$) { - my $file = shift; - - if (/$doc_inline_oneline/) { - $section = $1; - $contents = $2; - if ($contents ne "") { - $contents .= "\n"; - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - } elsif (/$doc_inline_start/) { - $state = STATE_INLINE; - $inline_doc_state = STATE_INLINE_NAME; - } elsif ($decl_type eq 'function') { - process_proto_function($_, $file); - } else { - process_proto_type($_, $file); - } -} - -# -# STATE_DOCBLOCK: within a DOC: block. -# -sub process_docblock($$) { - my $file = shift; - - if (/$doc_end/) { - dump_doc_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $prototype = ""; - $state = STATE_NORMAL; - } elsif (/$doc_content/) { - if ( $1 eq "" ) { - $contents .= $blankline; - } else { - $contents .= $1 . "\n"; - } - } -} - -# -# STATE_INLINE: docbook comments within a prototype. -# -sub process_inline($$) { - my $file = shift; - - # First line (state 1) needs to be a @parameter - if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { - $section = $1; - $contents = $2; - $new_start_line = $.; - if ($contents ne "") { - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - $contents .= "\n"; - } - $inline_doc_state = STATE_INLINE_TEXT; - # Documentation block end */ - } elsif (/$doc_inline_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - $state = STATE_PROTO; - $inline_doc_state = STATE_INLINE_NA; - # Regular text - } elsif (/$doc_content/) { - if ($inline_doc_state == STATE_INLINE_TEXT) { - $contents .= $1 . "\n"; - # nuke leading blank lines - if ($contents =~ /^\s*$/) { - $contents = ""; - } - } elsif ($inline_doc_state == STATE_INLINE_NAME) { - $inline_doc_state = STATE_INLINE_ERROR; - print STDERR "${file}:$.: warning: "; - print STDERR "Incorrect use of kernel-doc format: $_"; - ++$warnings; - } - } -} - - -sub process_file($) { - my $file; - my $initial_section_counter = $section_counter; - my ($orig_file) = @_; - - $file = map_filename($orig_file); - - if (!open(IN_FILE,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - $. = 1; - - $section_counter = 0; - while (<IN_FILE>) { - while (s/\\\s*$//) { - $_ .= <IN_FILE>; - } - # Replace tabs by spaces - while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; - # Hand this line to the appropriate state handler - if ($state == STATE_NORMAL) { - process_normal(); - } elsif ($state == STATE_NAME) { - process_name($file, $_); - } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE || - $state == STATE_BODY_WITH_BLANK_LINE) { - process_body($file, $_); - } elsif ($state == STATE_INLINE) { # scanning for inline parameters - process_inline($file, $_); - } elsif ($state == STATE_PROTO) { - process_proto($file, $_); - } elsif ($state == STATE_DOCBLOCK) { - process_docblock($file, $_); - } - } - - # Make sure we got something interesting. - if ($initial_section_counter == $section_counter && $ - output_mode ne "none") { - if ($output_selection == OUTPUT_INCLUDE) { - print STDERR "${file}:1: warning: '$_' not found\n" - for keys %function_table; - } - else { - print STDERR "${file}:1: warning: no structured comments found\n"; - } - } - close IN_FILE; -} - - -if ($output_mode eq "rst") { - get_sphinx_version() if (!$sphinx_major); -} - -$kernelversion = get_kernel_version(); - -# generate a sequence of code that will splice in highlighting information -# using the s// operator. -for (my $k = 0; $k < @highlights; $k++) { - my $pattern = $highlights[$k][0]; - my $result = $highlights[$k][1]; -# print STDERR "scanning pattern:$pattern, highlight:($result)\n"; - $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; -} - -# Read the file that maps relative names to absolute names for -# separate source and object directories and for shadow trees. -if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { - my ($relname, $absname); - while(<SOURCE_MAP>) { - chop(); - ($relname, $absname) = (split())[0..1]; - $relname =~ s:^/+::; - $source_map{$relname} = $absname; - } - close(SOURCE_MAP); -} - -if ($output_selection == OUTPUT_EXPORTED || - $output_selection == OUTPUT_INTERNAL) { - - push(@export_file_list, @ARGV); - - foreach (@export_file_list) { - chomp; - process_export_file($_); - } -} - -foreach (@ARGV) { - chomp; - process_file($_); -} -if ($verbose && $errors) { - print STDERR "$errors errors\n"; -} -if ($verbose && $warnings) { - print STDERR "$warnings warnings\n"; -} - -if ($Werror && $warnings) { - print STDERR "$warnings warnings as Errors\n"; - exit($warnings); -} else { - exit($output_mode eq "none" ? 0 : $errors) -} diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py new file mode 100755 index 0000000000..fc3d46ef51 --- /dev/null +++ b/scripts/kernel-doc.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. +# +# pylint: disable=C0103,R0915 +# +# Converted from the kernel-doc script originally written in Perl +# under GPLv2, copyrighted since 1998 by the following authors: +# +# Aditya Srivastava <yashsri421@gmail.com> +# Akira Yokosawa <akiyks@gmail.com> +# Alexander A. Klimov <grandmaster@al2klimov.de> +# Alexander Lobakin <aleksander.lobakin@intel.com> +# André Almeida <andrealmeid@igalia.com> +# Andy Shevchenko <andriy.shevchenko@linux.intel.com> +# Anna-Maria Behnsen <anna-maria@linutronix.de> +# Armin Kuster <akuster@mvista.com> +# Bart Van Assche <bart.vanassche@sandisk.com> +# Ben Hutchings <ben@decadent.org.uk> +# Borislav Petkov <bbpetkov@yahoo.de> +# Chen-Yu Tsai <wenst@chromium.org> +# Coco Li <lixiaoyan@google.com> +# Conchúr Navid <conchur@web.de> +# Daniel Santos <daniel.santos@pobox.com> +# Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk> +# Dan Luedtke <mail@danrl.de> +# Donald Hunter <donald.hunter@gmail.com> +# Gabriel Krisman Bertazi <krisman@collabora.co.uk> +# Greg Kroah-Hartman <gregkh@linuxfoundation.org> +# Harvey Harrison <harvey.harrison@gmail.com> +# Horia Geanta <horia.geanta@freescale.com> +# Ilya Dryomov <idryomov@gmail.com> +# Jakub Kicinski <kuba@kernel.org> +# Jani Nikula <jani.nikula@intel.com> +# Jason Baron <jbaron@redhat.com> +# Jason Gunthorpe <jgg@nvidia.com> +# Jérémy Bobbio <lunar@debian.org> +# Johannes Berg <johannes.berg@intel.com> +# Johannes Weiner <hannes@cmpxchg.org> +# Jonathan Cameron <Jonathan.Cameron@huawei.com> +# Jonathan Corbet <corbet@lwn.net> +# Jonathan Neuschäfer <j.neuschaefer@gmx.net> +# Kamil Rytarowski <n54@gmx.com> +# Kees Cook <kees@kernel.org> +# Laurent Pinchart <laurent.pinchart@ideasonboard.com> +# Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com> +# Linus Torvalds <torvalds@linux-foundation.org> +# Lucas De Marchi <lucas.demarchi@profusion.mobi> +# Mark Rutland <mark.rutland@arm.com> +# Markus Heiser <markus.heiser@darmarit.de> +# Martin Waitz <tali@admingilde.org> +# Masahiro Yamada <masahiroy@kernel.org> +# Matthew Wilcox <willy@infradead.org> +# Mauro Carvalho Chehab <mchehab+huawei@kernel.org> +# Michal Wajdeczko <michal.wajdeczko@intel.com> +# Michael Zucchi +# Mike Rapoport <rppt@linux.ibm.com> +# Niklas Söderlund <niklas.soderlund@corigine.com> +# Nishanth Menon <nm@ti.com> +# Paolo Bonzini <pbonzini@redhat.com> +# Pavan Kumar Linga <pavan.kumar.linga@intel.com> +# Pavel Pisa <pisa@cmp.felk.cvut.cz> +# Peter Maydell <peter.maydell@linaro.org> +# Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> +# Randy Dunlap <rdunlap@infradead.org> +# Richard Kennedy <richard@rsk.demon.co.uk> +# Rich Walker <rw@shadow.org.uk> +# Rolf Eike Beer <eike-kernel@sf-tec.de> +# Sakari Ailus <sakari.ailus@linux.intel.com> +# Silvio Fricke <silvio.fricke@gmail.com> +# Simon Huggins +# Tim Waugh <twaugh@redhat.com> +# Tomasz Warniełło <tomasz.warniello@gmail.com> +# Utkarsh Tripathi <utripathi2002@gmail.com> +# valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu> +# Vegard Nossum <vegard.nossum@oracle.com> +# Will Deacon <will.deacon@arm.com> +# Yacine Belkadi <yacine.belkadi.1@gmail.com> +# Yujie Liu <yujie.liu@intel.com> + +""" +kernel_doc +========== + +Print formatted kernel documentation to stdout + +Read C language source or header FILEs, extract embedded +documentation comments, and print formatted documentation +to standard output. + +The documentation comments are identified by the "/**" +opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the +documentation comment syntax. +""" + +import argparse +import logging +import os +import sys + +# Import Python modules + +LIB_DIR = "lib/kdoc" +SRC_DIR = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) + +from kdoc_files import KernelFiles # pylint: disable=C0413 +from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 + +DESC = """ +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by the "/**" opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. +""" + +EXPORT_FILE_DESC = """ +Specify an additional FILE in which to look for EXPORT_SYMBOL information. + +May be used multiple times. +""" + +EXPORT_DESC = """ +Only output documentation for the symbols that have been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +INTERNAL_DESC = """ +Only output documentation for the symbols that have NOT been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +FUNCTION_DESC = """ +Only output documentation for the given function or DOC: section +title. All other functions and DOC: sections are ignored. + +May be used multiple times. +""" + +NOSYMBOL_DESC = """ +Exclude the specified symbol from the output documentation. + +May be used multiple times. +""" + +FILES_DESC = """ +Header and C source files to be parsed. +""" + +WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ +Warns if there are contents before sections (deprecated). + +This option is kept just for backward-compatibility, but it does nothing, +neither here nor at the original Perl script. +""" + + +class MsgFormatter(logging.Formatter): + """Helper class to format warnings on a similar way to kernel-doc.pl""" + + def format(self, record): + record.levelname = record.levelname.capitalize() + return logging.Formatter.format(self, record) + +def main(): + """Main program""" + + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, + description=DESC) + + # Normal arguments + + parser.add_argument("-v", "-verbose", "--verbose", action="store_true", + help="Verbose output, more warnings and other information.") + + parser.add_argument("-d", "-debug", "--debug", action="store_true", + help="Enable debug messages") + + parser.add_argument("-M", "-modulename", "--modulename", + default="Kernel API", + help="Allow setting a module name at the output.") + + parser.add_argument("-l", "-enable-lineno", "--enable_lineno", + action="store_true", + help="Enable line number output (only in ReST mode)") + + # Arguments to control the warning behavior + + parser.add_argument("-Wreturn", "--wreturn", action="store_true", + help="Warns about the lack of a return markup on functions.") + + parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", + action="store_true", + help="Warns if initial short description is missing") + + parser.add_argument("-Wcontents-before-sections", + "--wcontents-before-sections", action="store_true", + help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) + + parser.add_argument("-Wall", "--wall", action="store_true", + help="Enable all types of warnings") + + parser.add_argument("-Werror", "--werror", action="store_true", + help="Treat warnings as errors.") + + parser.add_argument("-export-file", "--export-file", action='append', + help=EXPORT_FILE_DESC) + + # Output format mutually-exclusive group + + out_group = parser.add_argument_group("Output format selection (mutually exclusive)") + + out_fmt = out_group.add_mutually_exclusive_group() + + out_fmt.add_argument("-m", "-man", "--man", action="store_true", + help="Output troff manual page format.") + out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", + help="Output reStructuredText format (default).") + out_fmt.add_argument("-N", "-none", "--none", action="store_true", + help="Do not output documentation, only warnings.") + + # Output selection mutually-exclusive group + + sel_group = parser.add_argument_group("Output selection (mutually exclusive)") + sel_mut = sel_group.add_mutually_exclusive_group() + + sel_mut.add_argument("-e", "-export", "--export", action='store_true', + help=EXPORT_DESC) + + sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', + help=INTERNAL_DESC) + + sel_mut.add_argument("-s", "-function", "--symbol", action='append', + help=FUNCTION_DESC) + + # Those are valid for all 3 types of filter + parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', + help=NOSYMBOL_DESC) + + parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", + action='store_true', help="Don't outputt DOC sections") + + parser.add_argument("files", metavar="FILE", + nargs="+", help=FILES_DESC) + + args = parser.parse_args() + + if args.wall: + args.wreturn = True + args.wshort_desc = True + args.wcontents_before_sections = True + + logger = logging.getLogger() + + if not args.debug: + logger.setLevel(logging.INFO) + else: + logger.setLevel(logging.DEBUG) + + formatter = MsgFormatter('%(levelname)s: %(message)s') + + handler = logging.StreamHandler() + handler.setFormatter(formatter) + + logger.addHandler(handler) + + python_ver = sys.version_info[:2] + if python_ver < (3,6): + logger.warning("Python 3.6 or later is required by kernel-doc") + + # Return 0 here to avoid breaking compilation + sys.exit(0) + + if python_ver < (3,7): + logger.warning("Python 3.7 or later is required for correct results") + + if args.man: + out_style = ManFormat(modulename=args.modulename) + elif args.none: + out_style = None + else: + out_style = RestFormat() + + kfiles = KernelFiles(verbose=args.verbose, + out_style=out_style, werror=args.werror, + wreturn=args.wreturn, wshort_desc=args.wshort_desc, + wcontents_before_sections=args.wcontents_before_sections) + + kfiles.parse(args.files, export_file=args.export_file) + + for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, + internal=args.internal, symbol=args.symbol, + nosymbol=args.nosymbol, export_file=args.export_file, + no_doc_sections=args.no_doc_sections): + msg = t[1] + if msg: + print(msg) + + error_count = kfiles.errors + if not error_count: + sys.exit(0) + + if args.werror: + print(f"{error_count} warnings as errors") + sys.exit(error_count) + + if args.verbose: + print(f"{error_count} errors") + + if args.none: + sys.exit(0) + + sys.exit(error_count) + + +# Call main method +if __name__ == "__main__": + main() diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py new file mode 100644 index 0000000000..9e09b45b02 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_files.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. +# +# pylint: disable=R0903,R0913,R0914,R0917 + +""" +Parse lernel-doc tags on multiple kernel source files. +""" + +import argparse +import logging +import os +import re + +from kdoc_parser import KernelDoc +from kdoc_output import OutputFormat + + +class GlobSourceFiles: + """ + Parse C source code file names and directories via an Interactor. + """ + + def __init__(self, srctree=None, valid_extensions=None): + """ + Initialize valid extensions with a tuple. + + If not defined, assume default C extensions (.c and .h) + + It would be possible to use python's glob function, but it is + very slow, and it is not interactive. So, it would wait to read all + directories before actually do something. + + So, let's use our own implementation. + """ + + if not valid_extensions: + self.extensions = (".c", ".h") + else: + self.extensions = valid_extensions + + self.srctree = srctree + + def _parse_dir(self, dirname): + """Internal function to parse files recursively""" + + with os.scandir(dirname) as obj: + for entry in obj: + name = os.path.join(dirname, entry.name) + + if entry.is_dir(): + yield from self._parse_dir(name) + + if not entry.is_file(): + continue + + basename = os.path.basename(name) + + if not basename.endswith(self.extensions): + continue + + yield name + + def parse_files(self, file_list, file_not_found_cb): + """ + Define an interator to parse all source files from file_list, + handling directories if any + """ + + if not file_list: + return + + for fname in file_list: + if self.srctree: + f = os.path.join(self.srctree, fname) + else: + f = fname + + if os.path.isdir(f): + yield from self._parse_dir(f) + elif os.path.isfile(f): + yield f + elif file_not_found_cb: + file_not_found_cb(fname) + + +class KernelFiles(): + """ + Parse kernel-doc tags on multiple kernel source files. + + There are two type of parsers defined here: + - self.parse_file(): parses both kernel-doc markups and + EXPORT_SYMBOL* macros; + - self.process_export_file(): parses only EXPORT_SYMBOL* macros. + """ + + def warning(self, msg): + """Ancillary routine to output a warning and increment error count""" + + self.config.log.warning(msg) + self.errors += 1 + + def error(self, msg): + """Ancillary routine to output an error and increment error count""" + + self.config.log.error(msg) + self.errors += 1 + + def parse_file(self, fname): + """ + Parse a single Kernel source. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.files: + return + + doc = KernelDoc(self.config, fname) + export_table, entries = doc.parse_kdoc() + + self.export_table[fname] = export_table + + self.files.add(fname) + self.export_files.add(fname) # parse_kdoc() already check exports + + self.results[fname] = entries + + def process_export_file(self, fname): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.export_files: + return + + doc = KernelDoc(self.config, fname) + export_table = doc.parse_export() + + if not export_table: + self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}") + export_table = set() + + self.export_table[fname] = export_table + self.export_files.add(fname) + + def file_not_found_cb(self, fname): + """ + Callback to warn if a file was not found. + """ + + self.error(f"Cannot find file {fname}") + + def __init__(self, verbose=False, out_style=None, + werror=False, wreturn=False, wshort_desc=False, + wcontents_before_sections=False, + logger=None): + """ + Initialize startup variables and parse all files + """ + + if not verbose: + verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) + + if out_style is None: + out_style = OutputFormat() + + if not werror: + kcflags = os.environ.get("KCFLAGS", None) + if kcflags: + match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) + if match: + werror = True + + # reading this variable is for backwards compat just in case + # someone was calling it with the variable from outside the + # kernel's build system + kdoc_werror = os.environ.get("KDOC_WERROR", None) + if kdoc_werror: + werror = kdoc_werror + + # Some variables are global to the parser logic as a whole as they are + # used to send control configuration to KernelDoc class. As such, + # those variables are read-only inside the KernelDoc. + self.config = argparse.Namespace + + self.config.verbose = verbose + self.config.werror = werror + self.config.wreturn = wreturn + self.config.wshort_desc = wshort_desc + self.config.wcontents_before_sections = wcontents_before_sections + + if not logger: + self.config.log = logging.getLogger("kernel-doc") + else: + self.config.log = logger + + self.config.warning = self.warning + + self.config.src_tree = os.environ.get("SRCTREE", None) + + # Initialize variables that are internal to KernelFiles + + self.out_style = out_style + + self.errors = 0 + self.results = {} + + self.files = set() + self.export_files = set() + self.export_table = {} + + def parse(self, file_list, export_file=None): + """ + Parse all files + """ + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in glob.parse_files(file_list, self.file_not_found_cb): + self.parse_file(fname) + + for fname in glob.parse_files(export_file, self.file_not_found_cb): + self.process_export_file(fname) + + def out_msg(self, fname, name, arg): + """ + Return output messages from a file name using the output style + filtering. + + If output type was not handled by the syler, return None. + """ + + # NOTE: we can add rules here to filter out unwanted parts, + # although OutputFormat.msg already does that. + + return self.out_style.msg(fname, name, arg) + + def msg(self, enable_lineno=False, export=False, internal=False, + symbol=None, nosymbol=None, no_doc_sections=False, + filenames=None, export_file=None): + """ + Interacts over the kernel-doc results and output messages, + returning kernel-doc markups on each interaction + """ + + self.out_style.set_config(self.config) + + if not filenames: + filenames = sorted(self.results.keys()) + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in filenames: + function_table = set() + + if internal or export: + if not export_file: + export_file = [fname] + + for f in glob.parse_files(export_file, self.file_not_found_cb): + function_table |= self.export_table[f] + + if symbol: + for s in symbol: + function_table.add(s) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno, + no_doc_sections) + + msg = "" + if fname not in self.results: + self.config.log.warning("No kernel-doc for file %s", fname) + continue + + for arg in self.results[fname]: + m = self.out_msg(fname, arg.name, arg) + + if m is None: + ln = arg.get("ln", 0) + dtype = arg.get('type', "") + + self.config.log.warning("%s:%d Can't handle %s", + fname, ln, dtype) + else: + msg += m + + if msg: + yield fname, msg diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py new file mode 100644 index 0000000000..b3b2257645 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_item.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# A class that will, eventually, encapsulate all of the parsed data that we +# then pass into the output modules. +# + +class KdocItem: + def __init__(self, name, type, start_line, **other_stuff): + self.name = name + self.type = type + self.declaration_start_line = start_line + self.sections = {} + self.sections_start_lines = {} + self.parameterlist = [] + self.parameterdesc_start_lines = [] + self.parameterdescs = {} + self.parametertypes = {} + # + # Just save everything else into our own dict so that the output + # side can grab it directly as before. As we move things into more + # structured data, this will, hopefully, fade away. + # + self.other_stuff = other_stuff + + def get(self, key, default = None): + return self.other_stuff.get(key, default) + + def __getitem__(self, key): + return self.get(key) + + # + # Tracking of section and parameter information. + # + def set_sections(self, sections, start_lines): + self.sections = sections + self.section_start_lines = start_lines + + def set_params(self, names, descs, types, starts): + self.parameterlist = names + self.parameterdescs = descs + self.parametertypes = types + self.parameterdesc_start_lines = starts diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py new file mode 100644 index 0000000000..39fa872dfc --- /dev/null +++ b/scripts/lib/kdoc/kdoc_output.py @@ -0,0 +1,749 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. +# +# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917 + +""" +Implement output filters to print kernel-doc documentation. + +The implementation uses a virtual base class (OutputFormat) which +contains a dispatches to virtual methods, and some code to filter +out output messages. + +The actual implementation is done on one separate class per each type +of output. Currently, there are output classes for ReST and man/troff. +""" + +import os +import re +from datetime import datetime + +from kdoc_parser import KernelDoc, type_param +from kdoc_re import KernRe + + +function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) + +# match expressions used to find embedded type information +type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False) +type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False) +type_func = KernRe(r"(\w+)\(\)", cache=False) +type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# Special RST handling for func ptr params +type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False) + +# Special RST handling for structs with func ptr params +type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) + +type_env = KernRe(r"(\$\w+)", cache=False) +type_enum = KernRe(r"#(enum\s*([_\w]+))", cache=False) +type_struct = KernRe(r"#(struct\s*([_\w]+))", cache=False) +type_typedef = KernRe(r"#(([A-Z][_\w]*))", cache=False) +type_union = KernRe(r"#(union\s*([_\w]+))", cache=False) +type_member = KernRe(r"#([_\w]+)(\.|->)([_\w]+)", cache=False) +type_fallback = KernRe(r"((?!))", cache=False) # this never matches +type_member_func = type_member + KernRe(r"\(\)", cache=False) + + +class OutputFormat: + """ + Base class for OutputFormat. If used as-is, it means that only + warnings will be displayed. + """ + + # output mode. + OUTPUT_ALL = 0 # output all symbols and doc sections + OUTPUT_INCLUDE = 1 # output only specified symbols + OUTPUT_EXPORTED = 2 # output exported symbols + OUTPUT_INTERNAL = 3 # output non-exported symbols + + # Virtual member to be overriden at the inherited classes + highlights = [] + + def __init__(self): + """Declare internal vars and set mode to OUTPUT_ALL""" + + self.out_mode = self.OUTPUT_ALL + self.enable_lineno = None + self.nosymbol = {} + self.symbol = None + self.function_table = None + self.config = None + self.no_doc_sections = False + + self.data = "" + + def set_config(self, config): + """ + Setup global config variables used by both parser and output. + """ + + self.config = config + + def set_filter(self, export, internal, symbol, nosymbol, function_table, + enable_lineno, no_doc_sections): + """ + Initialize filter variables according with the requested mode. + + Only one choice is valid between export, internal and symbol. + + The nosymbol filter can be used on all modes. + """ + + self.enable_lineno = enable_lineno + self.no_doc_sections = no_doc_sections + self.function_table = function_table + + if symbol: + self.out_mode = self.OUTPUT_INCLUDE + elif export: + self.out_mode = self.OUTPUT_EXPORTED + elif internal: + self.out_mode = self.OUTPUT_INTERNAL + else: + self.out_mode = self.OUTPUT_ALL + + if nosymbol: + self.nosymbol = set(nosymbol) + + + def highlight_block(self, block): + """ + Apply the RST highlights to a sub-block of text. + """ + + for r, sub in self.highlights: + block = r.sub(sub, block) + + return block + + def out_warnings(self, args): + """ + Output warnings for identifiers that will be displayed. + """ + + for log_msg in args.warnings: + self.config.warning(log_msg) + + def check_doc(self, name, args): + """Check if DOC should be output""" + + if self.no_doc_sections: + return False + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode == self.OUTPUT_INCLUDE: + if name in self.function_table: + self.out_warnings(args) + return True + + return False + + def check_declaration(self, dtype, name, args): + """ + Checks if a declaration should be output or not based on the + filtering criteria. + """ + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: + if name in self.function_table: + return True + + if self.out_mode == self.OUTPUT_INTERNAL: + if dtype != "function": + self.out_warnings(args) + return True + + if name not in self.function_table: + self.out_warnings(args) + return True + + return False + + def msg(self, fname, name, args): + """ + Handles a single entry from kernel-doc parser + """ + + self.data = "" + + dtype = args.type + + if dtype == "doc": + self.out_doc(fname, name, args) + return self.data + + if not self.check_declaration(dtype, name, args): + return self.data + + if dtype == "function": + self.out_function(fname, name, args) + return self.data + + if dtype == "enum": + self.out_enum(fname, name, args) + return self.data + + if dtype == "typedef": + self.out_typedef(fname, name, args) + return self.data + + if dtype in ["struct", "union"]: + self.out_struct(fname, name, args) + return self.data + + # Warn if some type requires an output logic + self.config.log.warning("doesn't now how to output '%s' block", + dtype) + + return None + + # Virtual methods to be overridden by inherited classes + # At the base class, those do nothing. + def out_doc(self, fname, name, args): + """Outputs a DOC block""" + + def out_function(self, fname, name, args): + """Outputs a function""" + + def out_enum(self, fname, name, args): + """Outputs an enum""" + + def out_typedef(self, fname, name, args): + """Outputs a typedef""" + + def out_struct(self, fname, name, args): + """Outputs a struct""" + + +class RestFormat(OutputFormat): + """Consts and functions used by ReST output""" + + highlights = [ + (type_constant, r"``\1``"), + (type_constant2, r"``\1``"), + + # Note: need to escape () to avoid func matching later + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), + (type_member, r":c:type:`\1\2\3 <\1>`"), + (type_fp_param, r"**\1\\(\\)**"), + (type_fp_param2, r"**\1\\(\\)**"), + (type_func, r"\1()"), + (type_enum, r":c:type:`\1 <\2>`"), + (type_struct, r":c:type:`\1 <\2>`"), + (type_typedef, r":c:type:`\1 <\2>`"), + (type_union, r":c:type:`\1 <\2>`"), + + # in rst this can refer to any type + (type_fallback, r":c:type:`\1`"), + (type_param_ref, r"**\1\2**") + ] + blankline = "\n" + + sphinx_literal = KernRe(r'^[^.].*::$', cache=False) + sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False) + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.lineprefix = "" + + def print_lineno(self, ln): + """Outputs a line number""" + + if self.enable_lineno and ln is not None: + ln += 1 + self.data += f".. LINENO {ln}\n" + + def output_highlight(self, args): + """ + Outputs a C symbol that may require being converted to ReST using + the self.highlights variable + """ + + input_text = args + output = "" + in_literal = False + litprefix = "" + block = "" + + for line in input_text.strip("\n").split("\n"): + + # If we're in a literal block, see if we should drop out of it. + # Otherwise, pass the line straight through unmunged. + if in_literal: + if line.strip(): # If the line is not blank + # If this is the first non-blank line in a literal block, + # figure out the proper indent. + if not litprefix: + r = KernRe(r'^(\s*)') + if r.match(line): + litprefix = '^' + r.group(1) + else: + litprefix = "" + + output += line + "\n" + elif not KernRe(litprefix).match(line): + in_literal = False + else: + output += line + "\n" + else: + output += line + "\n" + + # Not in a literal block (or just dropped out) + if not in_literal: + block += line + "\n" + if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line): + in_literal = True + litprefix = "" + output += self.highlight_block(block) + block = "" + + # Handle any remaining block + if block: + output += self.highlight_block(block) + + # Print the output with the line prefix + for line in output.strip("\n").split("\n"): + self.data += self.lineprefix + line + "\n" + + def out_section(self, args, out_docblock=False): + """ + Outputs a block section. + + This could use some work; it's used to output the DOC: sections, and + starts by putting out the name of the doc section itself, but that + tends to duplicate a header already in the template file. + """ + for section, text in args.sections.items(): + # Skip sections that are in the nosymbol_table + if section in self.nosymbol: + continue + + if out_docblock: + if not self.out_mode == self.OUTPUT_INCLUDE: + self.data += f".. _{section}:\n\n" + self.data += f'{self.lineprefix}**{section}**\n\n' + else: + self.data += f'{self.lineprefix}**{section}**\n\n' + + self.print_lineno(args.section_start_lines.get(section, 0)) + self.output_highlight(text) + self.data += "\n" + self.data += "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + self.out_section(args, out_docblock=True) + + def out_function(self, fname, name, args): + + oldprefix = self.lineprefix + signature = "" + + func_macro = args.get('func_macro', False) + if func_macro: + signature = name + else: + if args.get('functiontype'): + signature = args['functiontype'] + " " + signature += name + " (" + + ln = args.declaration_start_line + count = 0 + for parameter in args.parameterlist: + if count != 0: + signature += ", " + count += 1 + dtype = args.parametertypes.get(parameter, "") + + if function_pointer.search(dtype): + signature += function_pointer.group(1) + parameter + function_pointer.group(3) + else: + signature += dtype + + if not func_macro: + signature += ")" + + self.print_lineno(ln) + if args.get('typedef') or not args.get('functiontype'): + self.data += f".. c:macro:: {name}\n\n" + + if args.get('typedef'): + self.data += " **Typedef**: " + self.lineprefix = "" + self.output_highlight(args.get('purpose', "")) + self.data += "\n\n**Syntax**\n\n" + self.data += f" ``{signature}``\n\n" + else: + self.data += f"``{signature}``\n\n" + else: + self.data += f".. c:function:: {signature}\n\n" + + if not args.get('typedef'): + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', "")) + self.data += "\n" + + # Put descriptive text into a container (HTML <div>) to help set + # function prototypes apart + self.lineprefix = " " + + if args.parameterlist: + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Parameters**\n\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + dtype = args.parametertypes.get(parameter, "") + + if dtype: + self.data += f"{self.lineprefix}``{dtype}``\n" + else: + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.lineprefix = " " + if parameter_name in args.parameterdescs and \ + args.parameterdescs[parameter_name] != KernelDoc.undescribed: + + self.output_highlight(args.parameterdescs[parameter_name]) + self.data += "\n" + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.lineprefix = " " + + self.out_section(args) + self.lineprefix = oldprefix + + def out_enum(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:enum:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', '')) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + outer = self.lineprefix + " " + self.lineprefix = outer + " " + self.data += f"{outer}**Constants**\n\n" + + for parameter in args.parameterlist: + self.data += f"{outer}``{parameter}``\n" + + if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: + self.output_highlight(args.parameterdescs[parameter]) + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_typedef(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:type:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + + self.output_highlight(args.get('purpose', '')) + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_struct(self, fname, name, args): + + purpose = args.get('purpose', "") + declaration = args.get('definition', "") + dtype = args.type + ln = args.declaration_start_line + + self.data += f"\n\n.. c:{dtype}:: {name}\n\n" + + self.print_lineno(ln) + + oldprefix = self.lineprefix + self.lineprefix += " " + + self.output_highlight(purpose) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Definition**::\n\n" + + self.lineprefix = self.lineprefix + " " + + declaration = declaration.replace("\t", self.lineprefix) + + self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" + self.data += f"{declaration}{self.lineprefix}" + "};\n\n" + + self.lineprefix = " " + self.data += f"{self.lineprefix}**Members**\n\n" + for parameter in args.parameterlist: + if not parameter or parameter.startswith("#"): + continue + + parameter_name = parameter.split("[", maxsplit=1)[0] + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.lineprefix = " " + self.output_highlight(args.parameterdescs[parameter_name]) + self.lineprefix = " " + + self.data += "\n" + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + +class ManFormat(OutputFormat): + """Consts and functions used by man pages output""" + + highlights = ( + (type_constant, r"\1"), + (type_constant2, r"\1"), + (type_func, r"\\fB\1\\fP"), + (type_enum, r"\\fI\1\\fP"), + (type_struct, r"\\fI\1\\fP"), + (type_typedef, r"\\fI\1\\fP"), + (type_union, r"\\fI\1\\fP"), + (type_param, r"\\fI\1\\fP"), + (type_param_ref, r"\\fI\1\2\\fP"), + (type_member, r"\\fI\1\2\3\\fP"), + (type_fallback, r"\\fI\1\\fP") + ) + blankline = "" + + date_formats = [ + "%a %b %d %H:%M:%S %Z %Y", + "%a %b %d %H:%M:%S %Y", + "%Y-%m-%d", + "%b %d %Y", + "%B %d %Y", + "%m %d %Y", + ] + + def __init__(self, modulename): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.modulename = modulename + + dt = None + tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") + if tstamp: + for fmt in self.date_formats: + try: + dt = datetime.strptime(tstamp, fmt) + break + except ValueError: + pass + + if not dt: + dt = datetime.now() + + self.man_date = dt.strftime("%B %Y") + + def output_highlight(self, block): + """ + Outputs a C symbol that may require being highlighted with + self.highlights variable using troff syntax + """ + + contents = self.highlight_block(block) + + if isinstance(contents, list): + contents = "\n".join(contents) + + for line in contents.strip("\n").split("\n"): + line = KernRe(r"^\s*").sub("", line) + if not line: + continue + + if line[0] == ".": + self.data += "\\&" + line + "\n" + else: + self.data += line + "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + + self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_function(self, fname, name, args): + """output function in man""" + + self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + if args.get('functiontype', ''): + self.data += f'.B "{args["functiontype"]}" {name}' + "\n" + else: + self.data += f'.B "{name}' + "\n" + + count = 0 + parenth = "(" + post = "," + + for parameter in args.parameterlist: + if count == len(args.parameterlist) - 1: + post = ");" + + dtype = args.parametertypes.get(parameter, "") + if function_pointer.match(dtype): + # Pointer-to-function + self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" + else: + dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype) + + self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n" + count += 1 + parenth = "" + + if args.parameterlist: + self.data += ".SH ARGUMENTS\n" + + for parameter in args.parameterlist: + parameter_name = re.sub(r'\[.*', '', parameter) + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section.upper()}"' + "\n" + self.output_highlight(text) + + def out_enum(self, fname, name, args): + self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"enum {name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + self.data += f"enum {name}" + " {\n" + + count = 0 + for parameter in args.parameterlist: + self.data += f'.br\n.BI " {parameter}"' + "\n" + if count == len(args.parameterlist) - 1: + self.data += "\n};\n" + else: + self.data += ", \n.br\n" + + count += 1 + + self.data += ".SH Constants\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_typedef(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + + self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"typedef {name} \\- {purpose}\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_struct(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + definition = args.get('definition') + + self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{args.type} {name} \\- {purpose}\n" + + # Replace tabs with two spaces and handle newlines + declaration = definition.replace("\t", " ") + declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) + + self.data += ".SH SYNOPSIS\n" + self.data += f"{args.type} {name} " + "{" + "\n.br\n" + self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" + + self.data += ".SH Members\n" + for parameter in args.parameterlist: + if parameter.startswith("#"): + continue + + parameter_name = re.sub(r"\[.*", "", parameter) + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name)) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py new file mode 100644 index 0000000000..32b4356292 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -0,0 +1,1670 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. +# +# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702 + +""" +kdoc_parser +=========== + +Read a C language source or header FILE and extract embedded +documentation comments +""" + +import sys +import re +from pprint import pformat + +from kdoc_re import NestedMatch, KernRe +from kdoc_item import KdocItem + +# +# Regular expressions used to parse kernel-doc markups at KernelDoc class. +# +# Let's declare them in lowercase outside any class to make easier to +# convert from the python script. +# +# As those are evaluated at the beginning, no need to cache them +# + +# Allow whitespace at end of comment start. +doc_start = KernRe(r'^/\*\*\s*$', cache=False) + +doc_end = KernRe(r'\*/', cache=False) +doc_com = KernRe(r'\s*\*\s*', cache=False) +doc_com_body = KernRe(r'\s*\* ?', cache=False) +doc_decl = doc_com + KernRe(r'(\w+)', cache=False) + +# @params and a strictly limited set of supported section names +# Specifically: +# Match @word: +# @...: +# @{section-name}: +# while trying to not match literal block starts like "example::" +# +known_section_names = 'description|context|returns?|notes?|examples?' +known_sections = KernRe(known_section_names, flags = re.I) +doc_sect = doc_com + \ + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + flags=re.I, cache=False) + +doc_content = doc_com_body + KernRe(r'(.*)', cache=False) +doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) +doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) +doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) +doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) +attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=re.I | re.S, cache=False) + +export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) +export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) + +type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# +# Tests for the beginning of a kerneldoc block in its various forms. +# +doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) +doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False) +doc_begin_func = KernRe(str(doc_com) + # initial " * ' + r"(?:\w+\s*\*\s*)?" + # type (not captured) + r'(?:define\s+)?' + # possible "define" (not captured) + r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)" + r'(?:[-:].*)?$', # description (not captured) + cache = False) + +# +# A little helper to get rid of excess white space +# +multi_space = KernRe(r'\s\s+') +def trim_whitespace(s): + return multi_space.sub(' ', s.strip()) + +class state: + """ + State machine enums + """ + + # Parser states + NORMAL = 0 # normal code + NAME = 1 # looking for function name + DECLARATION = 2 # We have seen a declaration which might not be done + BODY = 3 # the body of the comment + SPECIAL_SECTION = 4 # doc section ending with a blank line + PROTO = 5 # scanning prototype + DOCBLOCK = 6 # documentation block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs + + name = [ + "NORMAL", + "NAME", + "DECLARATION", + "BODY", + "SPECIAL_SECTION", + "PROTO", + "DOCBLOCK", + "INLINE_NAME", + "INLINE_TEXT", + ] + + +SECTION_DEFAULT = "Description" # default section + +class KernelEntry: + + def __init__(self, config, ln): + self.config = config + + self._contents = [] + self.prototype = "" + + self.warnings = [] + + self.parameterlist = [] + self.parameterdescs = {} + self.parametertypes = {} + self.parameterdesc_start_lines = {} + + self.section_start_lines = {} + self.sections = {} + + self.anon_struct_union = False + + self.leading_space = None + + # State flags + self.brcount = 0 + self.declaration_start_line = ln + 1 + + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + + # TODO: rename to emit_message after removal of kernel-doc.pl + def emit_msg(self, log_msg, warning=True): + """Emit a message""" + + if not warning: + self.config.log.info(log_msg) + return + + # Delegate warning output to output logic, as this way it + # will report warnings/info only for symbols that are output + + self.warnings.append(log_msg) + return + + # + # Begin a new section. + # + def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False): + if dump: + self.dump_section(start_new = True) + self.section = title + self.new_start_line = line_no + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return + name = self.section + contents = self.contents() + + if type_param.match(name): + name = type_param.group(1) + + self.parameterdescs[name] = contents + self.parameterdesc_start_lines[name] = self.new_start_line + + self.new_start_line = 0 + + else: + if name in self.sections and self.sections[name] != "": + # Only warn on user-specified duplicate section names + if name != SECTION_DEFAULT: + self.emit_msg(self.new_start_line, + f"duplicate section name '{name}'\n") + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents + else: + self.sections[name] = contents + self.section_start_lines[name] = self.new_start_line + self.new_start_line = 0 + +# self.config.log.debug("Section: %s : %s", name, pformat(vars(self))) + + if start_new: + self.section = SECTION_DEFAULT + self._contents = [] + + +class KernelDoc: + """ + Read a C language source or header FILE and extract embedded + documentation comments. + """ + + # Section names + + section_context = "Context" + section_return = "Return" + + undescribed = "-- undescribed --" + + def __init__(self, config, fname): + """Initialize internal variables""" + + self.fname = fname + self.config = config + + # Initial state for the state machines + self.state = state.NORMAL + + # Store entry currently being processed + self.entry = None + + # Place all potential outputs into an array + self.entries = [] + + # + # We need Python 3.7 for its "dicts remember the insertion + # order" guarantee + # + if sys.version_info.major == 3 and sys.version_info.minor < 7: + self.emit_msg(0, + 'Python 3.7 or later is required for correct results') + + def emit_msg(self, ln, msg, warning=True): + """Emit a message""" + + log_msg = f"{self.fname}:{ln} {msg}" + + if self.entry: + self.entry.emit_msg(log_msg, warning) + return + + if warning: + self.config.log.warning(log_msg) + else: + self.config.log.info(log_msg) + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + + if self.entry: + self.entry.dump_section(start_new) + + # TODO: rename it to store_declaration after removal of kernel-doc.pl + def output_declaration(self, dtype, name, **args): + """ + Stores the entry into an entry array. + + The actual output and output filters will be handled elsewhere + """ + + item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) + item.warnings = self.entry.warnings + + # Drop empty sections + # TODO: improve empty sections logic to emit warnings + sections = self.entry.sections + for section in ["Description", "Return"]: + if section in sections and not sections[section].rstrip(): + del sections[section] + item.set_sections(sections, self.entry.section_start_lines) + item.set_params(self.entry.parameterlist, self.entry.parameterdescs, + self.entry.parametertypes, + self.entry.parameterdesc_start_lines) + self.entries.append(item) + + self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) + + def reset_state(self, ln): + """ + Ancillary routine to create a new entry. It initializes all + variables used by the state machine. + """ + + self.entry = KernelEntry(self.config, ln) + + # State flags + self.state = state.NORMAL + + def push_parameter(self, ln, decl_type, param, dtype, + org_arg, declaration_name): + """ + Store parameters and their descriptions at self.entry. + """ + + if self.entry.anon_struct_union and dtype == "" and param == "}": + return # Ignore the ending }; from anonymous struct/union + + self.entry.anon_struct_union = False + + param = KernRe(r'[\[\)].*').sub('', param, count=1) + + if dtype == "" and param.endswith("..."): + if KernRe(r'\w\.\.\.$').search(param): + # For named variable parameters of the form `x...`, + # remove the dots + param = param[:-3] + else: + # Handles unnamed variable parameters + param = "..." + + if param not in self.entry.parameterdescs or \ + not self.entry.parameterdescs[param]: + + self.entry.parameterdescs[param] = "variable arguments" + + elif dtype == "" and (not param or param == "void"): + param = "void" + self.entry.parameterdescs[param] = "no arguments" + + elif dtype == "" and param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype = param + param = "{unnamed_" + param + "}" + self.entry.parameterdescs[param] = "anonymous\n" + self.entry.anon_struct_union = True + + # Handle cache group enforcing variables: they do not need + # to be described in header files + elif "__cacheline_group" in param: + # Ignore __cacheline_group_begin and __cacheline_group_end + return + + # Warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements) + if param not in self.entry.parameterdescs and not param.startswith("#"): + self.entry.parameterdescs[param] = self.undescribed + + if "." not in param: + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + + self.emit_msg(ln, + f"{dname} '{param}' not described in '{declaration_name}'") + + # Strip spaces from param so that it is one continuous string on + # parameterlist. This fixes a problem where check_sections() + # cannot find a parameter like "addr[6 + 2]" because it actually + # appears as "addr[6", "+", "2]" on the parameter list. + # However, it's better to maintain the param string unchanged for + # output, so just weaken the string compare in check_sections() + # to ignore "[blah" in a parameter string. + + self.entry.parameterlist.append(param) + org_arg = KernRe(r'\s\s+').sub(' ', org_arg) + self.entry.parametertypes[param] = org_arg + + + def create_parameter_list(self, ln, decl_type, args, + splitter, declaration_name): + """ + Creates a list of parameters, storing them at self.entry. + """ + + # temporarily replace all commas inside function pointer definition + arg_expr = KernRe(r'(\([^\),]+),') + while arg_expr.search(args): + args = arg_expr.sub(r"\1#", args) + + for arg in args.split(splitter): + # Strip comments + arg = KernRe(r'\/\*.*\*\/').sub('', arg) + + # Ignore argument attributes + arg = KernRe(r'\sPOS0?\s').sub(' ', arg) + + # Strip leading/trailing spaces + arg = arg.strip() + arg = KernRe(r'\s+').sub(' ', arg, count=1) + + if arg.startswith('#'): + # Treat preprocessor directive as a typeless variable just to fill + # corresponding data structures "correctly". Catch it later in + # output_* subs. + + # Treat preprocessor directive as a typeless variable + self.push_parameter(ln, decl_type, arg, "", + "", declaration_name) + + elif KernRe(r'\(.+\)\s*\(').search(arg): + # Pointer-to-function + + arg = arg.replace('#', ',') + + r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif KernRe(r'\(.+\)\s*\[').search(arg): + # Array-of-pointers + + arg = arg.replace('#', ',') + r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif arg: + arg = KernRe(r'\s*:\s*').sub(":", arg) + arg = KernRe(r'\s*\[').sub('[', arg) + + args = KernRe(r'\s*,\s*').split(arg) + if args[0] and '*' in args[0]: + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) + + first_arg = [] + r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') + if args[0] and r.match(args[0]): + args.pop(0) + first_arg.extend(r.group(1)) + first_arg.append(r.group(2)) + else: + first_arg = KernRe(r'\s+').split(args.pop(0)) + + args.insert(0, first_arg.pop()) + dtype = ' '.join(first_arg) + + for param in args: + if KernRe(r'^(\*+)\s*(.*)').match(param): + r = KernRe(r'^(\*+)\s*(.*)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + param = r.group(1) + + self.push_parameter(ln, decl_type, r.group(2), + f"{dtype} {r.group(1)}", + arg, declaration_name) + + elif KernRe(r'(.*?):(\w+)').search(param): + r = KernRe(r'(.*?):(\w+)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + if dtype != "": # Skip unnamed bit-fields + self.push_parameter(ln, decl_type, r.group(1), + f"{dtype}:{r.group(2)}", + arg, declaration_name) + else: + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + def check_sections(self, ln, decl_name, decl_type): + """ + Check for errors inside sections, emitting warnings if not found + parameters are described. + """ + for section in self.entry.sections: + if section not in self.entry.parameterlist and \ + not known_sections.search(section): + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + self.emit_msg(ln, + f"Excess {dname} '{section}' description in '{decl_name}'") + + def check_return_section(self, ln, declaration_name, return_type): + """ + If the function doesn't return void, warns about the lack of a + return description. + """ + + if not self.config.wreturn: + return + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type (but not "void *") + if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): + return + + if not self.entry.sections.get("Return", None): + self.emit_msg(ln, + f"No description found for return value of '{declaration_name}'") + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + + type_pattern = r'(struct|union)' + + qualifiers = [ + "__attribute__", + "__packed", + "__aligned", + "____cacheline_aligned_in_smp", + "____cacheline_aligned", + ] + + definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" + struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') + + # Extract struct/union definition + members = None + declaration_name = None + decl_type = None + + r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(2) + members = r.group(3) + else: + r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') + + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(3) + members = r.group(2) + + if not members: + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") + return + + args_pattern = r'([^,)]+)' + + sub_prefixes = [ + (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), + (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), + + # Strip comments + (KernRe(r'\/\*.*?\*\/', re.S), ''), + + # Strip attributes + (attribute, ' '), + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__packed\s*', re.S), ' '), + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), + + # Unwrap struct_group macros based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cases. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are properly + # matched. So, the implementation to drop STRUCT_GROUP() will be + # handled in separate. + + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), + + # Replace macros + # + # TODO: use NestedMatch for FOO($1, $2, ...) matches + # + # it is better to also move those to the NestedMatch logic, + # to ensure that parenthesis will be properly matched. + + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), + (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), + ] + + # Regexes here are guaranteed to have the end limiter matching + # the start delimiter. Yet, right now, only one replace group + # is allowed. + + sub_nested_prefixes = [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), + ] + + for search, sub in sub_prefixes: + members = search.sub(sub, members) + + nested = NestedMatch() + + for search, sub in sub_nested_prefixes: + members = nested.sub(search, sub, members) + + # Keeps the original declaration as-is + declaration = members + + # Split nested struct/union elements + # + # This loop was simpler at the original kernel-doc perl version, as + # while ($members =~ m/$struct_members/) { ... } + # reads 'members' string on each interaction. + # + # Python behavior is different: it parses 'members' only once, + # creating a list of tuples from the first interaction. + # + # On other words, this won't get nested structs. + # + # So, we need to have an extra loop on Python to override such + # re limitation. + + while True: + tuples = struct_members.findall(members) + if not tuples: + break + + for t in tuples: + newmember = "" + maintype = t[0] + s_ids = t[5] + content = t[3] + + oldmember = "".join(t) + + for s_id in s_ids.split(','): + s_id = s_id.strip() + + newmember += f"{maintype} {s_id}; " + s_id = KernRe(r'[:\[].*').sub('', s_id) + s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) + + for arg in content.split(';'): + arg = arg.strip() + + if not arg: + continue + + r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + if r.match(arg): + # Pointer-to-function + dtype = r.group(1) + name = r.group(2) + extra = r.group(3) + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype}{name}{extra}; " + else: + newmember += f"{dtype}{s_id}.{name}{extra}; " + + else: + arg = arg.strip() + # Handle bitmaps + arg = KernRe(r':\s*\d+\s*').sub('', arg) + + # Handle arrays + arg = KernRe(r'\[.*\]').sub('', arg) + + # Handle multiple IDs + arg = KernRe(r'\s*,\s*').sub(',', arg) + + r = KernRe(r'(.*)\s+([\S+,]+)') + + if r.search(arg): + dtype = r.group(1) + names = r.group(2) + else: + newmember += f"{arg}; " + continue + + for name in names.split(','): + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype} {name}; " + else: + newmember += f"{dtype} {s_id}.{name}; " + + members = members.replace(oldmember, newmember) + + # Ignore other nested elements, like enums + members = re.sub(r'(\{[^\{\}]*\})', '', members) + + self.create_parameter_list(ln, decl_type, members, ';', + declaration_name) + self.check_sections(ln, declaration_name, decl_type) + + # Adjust declaration for better display + declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) + declaration = KernRe(r'\}\s+;').sub('};', declaration) + + # Better handle inlined enums + while True: + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') + if not r.search(declaration): + break + + declaration = r.sub(r'\1,\n\2', declaration) + + def_args = declaration.split('\n') + level = 1 + declaration = "" + for clause in def_args: + + clause = clause.strip() + clause = KernRe(r'\s+').sub(' ', clause, count=1) + + if not clause: + continue + + if '}' in clause and level > 1: + level -= 1 + + if not KernRe(r'^\s*#').match(clause): + declaration += "\t" * level + + declaration += "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level += 1 + + self.output_declaration(decl_type, declaration_name, + definition=declaration, + purpose=self.entry.declaration_purpose) + + def dump_enum(self, ln, proto): + """ + Stores an enum inside self.entries array. + """ + + # Ignore members marked private + proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) + proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) + + # Strip comments + proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) + + # Strip #define macros inside enums + proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) + + # + # Parse out the name and members of the enum. Typedef form first. + # + r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') + if r.search(proto): + declaration_name = r.group(2) + members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # + else: + r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') + if r.match(proto): + declaration_name = r.group(1) + members = r.group(2).rstrip() + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # + if self.entry.identifier != declaration_name: + if self.entry.identifier == "": + self.emit_msg(ln, + f"{proto}: wrong kernel-doc identifier on prototype") + else: + self.emit_msg(ln, + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") + return + + if not declaration_name: + declaration_name = "(anonymous)" + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # + member_set = set() + members = KernRe(r'\([^;)]*\)').sub('', members) + for arg in members.split(','): + if not arg: + continue + arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) + self.entry.parameterlist.append(arg) + if arg not in self.entry.parameterdescs: + self.entry.parameterdescs[arg] = self.undescribed + self.emit_msg(ln, + f"Enum value '{arg}' not described in enum '{declaration_name}'") + member_set.add(arg) + # + # Ensure that every described member actually exists in the enum. + # + for k in self.entry.parameterdescs: + if k not in member_set: + self.emit_msg(ln, + f"Excess enum value '%{k}' description in '{declaration_name}'") + + self.output_declaration('enum', declaration_name, + purpose=self.entry.declaration_purpose) + + def dump_declaration(self, ln, prototype): + """ + Stores a data declaration inside self.entries array. + """ + + if self.entry.decl_type == "enum": + self.dump_enum(ln, prototype) + elif self.entry.decl_type == "typedef": + self.dump_typedef(ln, prototype) + elif self.entry.decl_type in ["union", "struct"]: + self.dump_struct(ln, prototype) + else: + # This would be a bug + self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') + + def dump_function(self, ln, prototype): + """ + Stores a function of function macro inside self.entries array. + """ + + func_macro = False + return_type = '' + decl_type = 'function' + + # Prefixes that would be removed + sub_prefixes = [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"QEMU_[A-Z_]+ +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), + (r"__attribute_const__ +", "", 0), + + # It seems that Python support for re.X is broken: + # At least for me (Python 3.13), this didn't work +# (r""" +# __attribute__\s*\(\( +# (?: +# [\w\s]+ # attribute name +# (?:\([^)]*\))? # attribute arguments +# \s*,? # optional comma at the end +# )+ +# \)\)\s+ +# """, "", re.X), + + # So, remove whitespaces and comments from it + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), + ] + + for search, sub, flags in sub_prefixes: + prototype = KernRe(search, flags).sub(sub, prototype) + + # Macros are a special case, as they change the prototype format + new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) + if new_proto != prototype: + is_define_proto = True + prototype = new_proto + else: + is_define_proto = False + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer parameters + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + name = r'[a-zA-Z0-9_~:]+' + prototype_end1 = r'[^\(]*' + prototype_end2 = r'[^\{]*' + prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' + + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ + + type1 = r'(?:[\w\s]+)?' + type2 = r'(?:[\w\s]+\*+)+' + + found = False + + if is_define_proto: + r = KernRe(r'^()(' + name + r')\s+') + + if r.search(prototype): + return_type = '' + declaration_name = r.group(2) + func_macro = True + + found = True + + if not found: + patterns = [ + rf'^()({name})\s*{prototype_end}', + rf'^({type1})\s+({name})\s*{prototype_end}', + rf'^({type2})\s*({name})\s*{prototype_end}', + ] + + for p in patterns: + r = KernRe(p) + + if r.match(prototype): + + return_type = r.group(1) + declaration_name = r.group(2) + args = r.group(3) + + self.create_parameter_list(ln, decl_type, args, ',', + declaration_name) + + found = True + break + if not found: + self.emit_msg(ln, + f"cannot understand function prototype: '{prototype}'") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") + return + + self.check_sections(ln, declaration_name, "function") + + self.check_return_section(ln, declaration_name, return_type) + + if 'typedef' in return_type: + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + else: + self.output_declaration(decl_type, declaration_name, + typedef=False, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + + def dump_typedef(self, ln, proto): + """ + Stores a typedef inside self.entries array. + """ + + typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' + typedef_ident = r'\*?\s*(\w\S+)\s*' + typedef_args = r'\s*\((.*)\);' + + typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) + typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) + + # Strip comments + proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) + + # Parse function typedef prototypes + for r in [typedef1, typedef2]: + if not r.match(proto): + continue + + return_type = r.group(1).strip() + declaration_name = r.group(2) + args = r.group(3) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + decl_type = 'function' + self.create_parameter_list(ln, decl_type, args, ',', declaration_name) + + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose) + return + + # Handle nested parentheses or brackets + r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') + while r.search(proto): + proto = r.sub('', proto) + + # Parse simple typedefs + r = KernRe(r'typedef.*\s+(\w+)\s*;') + if r.match(proto): + declaration_name = r.group(1) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + self.output_declaration('typedef', declaration_name, + purpose=self.entry.declaration_purpose) + return + + self.emit_msg(ln, "error: Cannot parse typedef!") + + @staticmethod + def process_export(function_set, line): + """ + process EXPORT_SYMBOL* tags + + This method doesn't use any variable from the class, so declare it + with a staticmethod decorator. + """ + + # We support documenting some exported symbols with different + # names. A horrible hack. + suffixes = [ '_noprof' ] + + # Note: it accepts only one EXPORT_SYMBOL* per line, as having + # multiple export lines would violate Kernel coding style. + + if export_symbol.search(line): + symbol = export_symbol.group(2) + elif export_symbol_ns.search(line): + symbol = export_symbol_ns.group(2) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + # Be backward compatible with Python < 3.9 + if symbol.endswith(suffix): + symbol = symbol[:-len(suffix)] + function_set.add(symbol) + return True + + def process_normal(self, ln, line): + """ + STATE_NORMAL: looking for the /** to begin everything. + """ + + if not doc_start.match(line): + return + + # start a new entry + self.reset_state(ln) + + # next line is always the function name + self.state = state.NAME + + def process_name(self, ln, line): + """ + STATE_NAME: Looking for the "name - description" line + """ + # + # Check for a DOC: block and handle them specially. + # + if doc_block.search(line): + + if not doc_block.group(1): + self.entry.begin_section(ln, "Introduction") + else: + self.entry.begin_section(ln, doc_block.group(1)) + + self.entry.identifier = self.entry.section + self.state = state.DOCBLOCK + # + # Otherwise we're looking for a normal kerneldoc declaration line. + # + elif doc_decl.search(line): + self.entry.identifier = doc_decl.group(1) + + # Test for data declaration + if doc_begin_data.search(line): + self.entry.decl_type = doc_begin_data.group(1) + self.entry.identifier = doc_begin_data.group(2) + # + # Look for a function description + # + elif doc_begin_func.search(line): + self.entry.identifier = doc_begin_func.group(1) + self.entry.decl_type = "function" + # + # We struck out. + # + else: + self.emit_msg(ln, + f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") + self.state = state.NORMAL + return + # + # OK, set up for a new kerneldoc entry. + # + self.state = state.BODY + self.entry.identifier = self.entry.identifier.strip(" ") + # if there's no @param blocks need to set up default section here + self.entry.begin_section(ln + 1) + # + # Find the description portion, which *should* be there but + # isn't always. + # (We should be able to capture this from the previous parsing - someday) + # + r = KernRe("[-:](.*)") + if r.search(line): + self.entry.declaration_purpose = trim_whitespace(r.group(1)) + self.state = state.DECLARATION + else: + self.entry.declaration_purpose = "" + + if not self.entry.declaration_purpose and self.config.wshort_desc: + self.emit_msg(ln, + f"missing initial short description on line:\n{line}") + + if not self.entry.identifier and self.entry.decl_type != "enum": + self.emit_msg(ln, + f"wrong kernel-doc identifier on line:\n{line}") + self.state = state.NORMAL + + if self.config.verbose: + self.emit_msg(ln, + f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", + warning=False) + # + # Failed to find an identifier. Emit a warning + # + else: + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + + # + # Helper function to determine if a new section is being started. + # + def is_new_section(self, ln, line): + if doc_sect.search(line): + self.state = state.BODY + # + # Pick out the name of our new section, tweaking it if need be. + # + newsection = doc_sect.group(1) + if newsection.lower() == 'description': + newsection = 'Description' + elif newsection.lower() == 'context': + newsection = 'Context' + self.state = state.SPECIAL_SECTION + elif newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection = "Return" + self.state = state.SPECIAL_SECTION + elif newsection[0] == '@': + self.state = state.SPECIAL_SECTION + # + # Initialize the contents, and get the new section going. + # + newcontents = doc_sect.group(2) + if not newcontents: + newcontents = "" + self.dump_section() + self.entry.begin_section(ln, newsection) + self.entry.leading_space = None + + self.entry.add_text(newcontents.lstrip()) + return True + return False + + # + # Helper function to detect (and effect) the end of a kerneldoc comment. + # + def is_comment_end(self, ln, line): + if doc_end.search(line): + self.dump_section() + + # Look for doc_com + <text> + doc_end: + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_msg(ln, f"suspicious ending line: {line}") + + self.entry.prototype = "" + self.entry.new_start_line = ln + 1 + + self.state = state.PROTO + return True + return False + + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # Look for anything with the " * " line beginning. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # A blank line means that we have moved out of the declaration + # part of the comment (without any "special section" parameter + # descriptions). + # + if cont == "": + self.state = state.BODY + # + # Otherwise we have more of the declaration section to soak up. + # + else: + self.entry.declaration_purpose = \ + trim_whitespace(self.entry.declaration_purpose + ' ' + cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + + def process_special(self, ln, line): + """ + STATE_SPECIAL_SECTION: a section ending with a blank line + """ + # + # If we have hit a blank line (only the " * " marker), then this + # section is done. + # + if KernRe(r"\s*\*\s*$").match(line): + self.entry.begin_section(ln, dump = True) + self.state = state.BODY + return + # + # Not a blank line, look for the other ways to end the section. + # + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # OK, we should have a continuation of the text for this section. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # If the lines of text after the first in a special section have + # leading white space, we need to trim it out or Sphinx will get + # confused. For the second line (the None case), see what we + # find there and remember it. + # + if self.entry.leading_space is None: + r = KernRe(r'^(\s+)') + if r.match(cont): + self.entry.leading_space = len(r.group(1)) + else: + self.entry.leading_space = 0 + # + # Otherwise, before trimming any leading chars, be *sure* + # that they are white space. We should maybe warn if this + # isn't the case. + # + for i in range(0, self.entry.leading_space): + if cont[i] != " ": + self.entry.leading_space = i + break + # + # Add the trimmed result to the section and we're done. + # + self.entry.add_text(cont[self.entry.leading_space:]) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_body(self, ln, line): + """ + STATE_BODY: the bulk of a kerneldoc comment. + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + + if doc_content.search(line): + cont = doc_content.group(1) + self.entry.add_text(cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" + + if doc_inline_sect.search(line): + self.entry.begin_section(ln, doc_inline_sect.group(1)) + self.entry.add_text(doc_inline_sect.group(2).lstrip()) + self.state = state.INLINE_TEXT + elif doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? + + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" + + if doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? + + def syscall_munge(self, ln, proto): # pylint: disable=W0613 + """ + Handle syscall definitions + """ + + is_void = False + + # Strip newlines/CR's + proto = re.sub(r'[\r\n]+', ' ', proto) + + # Check if it's a SYSCALL_DEFINE0 + if 'SYSCALL_DEFINE0' in proto: + is_void = True + + # Replace SYSCALL_DEFINE with correct return type & function name + proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) + + r = KernRe(r'long\s+(sys_.*?),') + if r.search(proto): + proto = KernRe(',').sub('(', proto, count=1) + elif is_void: + proto = KernRe(r'\)').sub('(void)', proto, count=1) + + # Now delete all of the odd-numbered commas in the proto + # so that argument types & names don't have a comma between them + count = 0 + length = len(proto) + + if is_void: + length = 0 # skip the loop if is_void + + for ix in range(length): + if proto[ix] == ',': + count += 1 + if count % 2 == 1: + proto = proto[:ix] + ' ' + proto[ix + 1:] + + return proto + + def tracepoint_munge(self, ln, proto): + """ + Handle tracepoint definitions + """ + + tracepointname = None + tracepointargs = None + + # Match tracepoint name based on different patterns + r = KernRe(r'TRACE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),') + if r.search(proto): + tracepointname = r.group(2) + + if tracepointname: + tracepointname = tracepointname.lstrip() + + r = KernRe(r'TP_PROTO\((.*?)\)') + if r.search(proto): + tracepointargs = r.group(1) + + if not tracepointname or not tracepointargs: + self.emit_msg(ln, + f"Unrecognized tracepoint format:\n{proto}\n") + else: + proto = f"static inline void trace_{tracepointname}({tracepointargs})" + self.entry.identifier = f"trace_{self.entry.identifier}" + + return proto + + def process_proto_function(self, ln, line): + """Ancillary routine to process a function prototype""" + + # strip C99-style comments to end of line + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # + if KernRe(r'\s*#\s*define').match(line): + self.entry.prototype = line + elif not line.startswith('#'): # skip other preprocessor stuff + r = KernRe(r'([^\{]*)') + if r.match(line): + self.entry.prototype += r.group(1) + " " + # + # If we now have the whole prototype, clean it up and declare victory. + # + if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): + # strip comments and surrounding spaces + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # + # Handle self.entry.prototypes for function pointers like: + # int (*pcs_config)(struct foo) + # by turning it into + # int pcs_config(struct foo) + # + r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') + self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) + # + # Handle special declaration syntaxes + # + if 'SYSCALL_DEFINE' in self.entry.prototype: + self.entry.prototype = self.syscall_munge(ln, + self.entry.prototype) + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # + self.dump_function(ln, self.entry.prototype) + self.reset_state(ln) + + def process_proto_type(self, ln, line): + """Ancillary routine to process a type""" + + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here + + # To distinguish preprocessor directive from regular declaration later. + if line.startswith('#'): + line += ";" + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: + self.dump_declaration(ln, self.entry.prototype) + self.reset_state(ln) + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' + + def process_proto(self, ln, line): + """STATE_PROTO: reading a function/whatever prototype.""" + + if doc_inline_oneline.search(line): + self.entry.begin_section(ln, doc_inline_oneline.group(1)) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() + + elif doc_inline_start.search(line): + self.state = state.INLINE_NAME + + elif self.entry.decl_type == 'function': + self.process_proto_function(ln, line) + + else: + self.process_proto_type(ln, line) + + def process_docblock(self, ln, line): + """STATE_DOCBLOCK: within a DOC: block.""" + + if doc_end.search(line): + self.dump_section() + self.output_declaration("doc", self.entry.identifier) + self.reset_state(ln) + + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + + def parse_export(self): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + + for line in fp: + self.process_export(export_table, line) + + except IOError: + return None + + return export_table + + # + # The state/action table telling us which function to invoke in + # each state. + # + state_actions = { + state.NORMAL: process_normal, + state.NAME: process_name, + state.BODY: process_body, + state.DECLARATION: process_decl, + state.SPECIAL_SECTION: process_special, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, + state.PROTO: process_proto, + state.DOCBLOCK: process_docblock, + } + + def parse_kdoc(self): + """ + Open and process each line of a C source file. + The parsing is controlled via a state machine, and the line is passed + to a different process function depending on the state. The process + function may update the state as needed. + + Besides parsing kernel-doc tags, it also parses export symbols. + """ + + prev = "" + prev_ln = None + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + for ln, line in enumerate(fp): + + line = line.expandtabs().strip("\n") + + # Group continuation lines on prototypes + if self.state == state.PROTO: + if line.endswith("\\"): + prev += line.rstrip("\\") + if not prev_ln: + prev_ln = ln + continue + + if prev: + ln = prev_ln + line = prev + line + prev = "" + prev_ln = None + + self.config.log.debug("%d %s: %s", + ln, state.name[self.state], + line) + + # This is an optimization over the original script. + # There, when export_file was used for the same file, + # it was read twice. Here, we use the already-existing + # loop to parse exported symbols as well. + # + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) + + except OSError: + self.config.log.error(f"Error: Cannot open file {self.fname}") + + return export_table, self.entries diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py new file mode 100644 index 0000000000..612223e1e7 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_re.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. + +""" +Regular expression ancillary classes. + +Those help caching regular expressions and do matching for kernel-doc. +""" + +import re + +# Local cache for regular expressions +re_cache = {} + + +class KernRe: + """ + Helper class to simplify regex declaration and usage, + + It calls re.compile for a given pattern. It also allows adding + regular expressions and define sub at class init time. + + Regular expressions can be cached via an argument, helping to speedup + searches. + """ + + def _add_regex(self, string, flags): + """ + Adds a new regex or re-use it from the cache. + """ + self.regex = re_cache.get(string, None) + if not self.regex: + self.regex = re.compile(string, flags=flags) + if self.cache: + re_cache[string] = self.regex + + def __init__(self, string, cache=True, flags=0): + """ + Compile a regular expression and initialize internal vars. + """ + + self.cache = cache + self.last_match = None + + self._add_regex(string, flags) + + def __str__(self): + """ + Return the regular expression pattern. + """ + return self.regex.pattern + + def __add__(self, other): + """ + Allows adding two regular expressions into one. + """ + + return KernRe(str(self) + str(other), cache=self.cache or other.cache, + flags=self.regex.flags | other.regex.flags) + + def match(self, string): + """ + Handles a re.match storing its results + """ + + self.last_match = self.regex.match(string) + return self.last_match + + def search(self, string): + """ + Handles a re.search storing its results + """ + + self.last_match = self.regex.search(string) + return self.last_match + + def findall(self, string): + """ + Alias to re.findall + """ + + return self.regex.findall(string) + + def split(self, string): + """ + Alias to re.split + """ + + return self.regex.split(string) + + def sub(self, sub, string, count=0): + """ + Alias to re.sub + """ + + return self.regex.sub(sub, string, count=count) + + def group(self, num): + """ + Returns the group results of the last match + """ + + return self.last_match.group(num) + + +class NestedMatch: + """ + Finding nested delimiters is hard with regular expressions. It is + even harder on Python with its normal re module, as there are several + advanced regular expressions that are missing. + + This is the case of this pattern: + + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' + + which is used to properly match open/close parenthesis of the + string search STRUCT_GROUP(), + + Add a class that counts pairs of delimiters, using it to match and + replace nested expressions. + + The original approach was suggested by: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + + Although I re-implemented it to make it more generic and match 3 types + of delimiters. The logic checks if delimiters are paired. If not, it + will ignore the search string. + """ + + # TODO: make NestedMatch handle multiple match groups + # + # Right now, regular expressions to match it are defined only up to + # the start delimiter, e.g.: + # + # \bSTRUCT_GROUP\( + # + # is similar to: STRUCT_GROUP\((.*)\) + # except that the content inside the match group is delimiter's aligned. + # + # The content inside parenthesis are converted into a single replace + # group (e.g. r`\1'). + # + # It would be nice to change such definition to support multiple + # match groups, allowing a regex equivalent to. + # + # FOO\((.*), (.*), (.*)\) + # + # it is probably easier to define it not as a regular expression, but + # with some lexical definition like: + # + # FOO(arg1, arg2, arg3) + + DELIMITER_PAIRS = { + '{': '}', + '(': ')', + '[': ']', + } + + RE_DELIM = re.compile(r'[\{\}\[\]\(\)]') + + def _search(self, regex, line): + """ + Finds paired blocks for a regex that ends with a delimiter. + + The suggestion of using finditer to match pairs came from: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + but I ended using a different implementation to align all three types + of delimiters and seek for an initial regular expression. + + The algorithm seeks for open/close paired delimiters and place them + into a stack, yielding a start/stop position of each match when the + stack is zeroed. + + The algorithm shoud work fine for properly paired lines, but will + silently ignore end delimiters that preceeds an start delimiter. + This should be OK for kernel-doc parser, as unaligned delimiters + would cause compilation errors. So, we don't need to rise exceptions + to cover such issues. + """ + + stack = [] + + for match_re in regex.finditer(line): + start = match_re.start() + offset = match_re.end() + + d = line[offset - 1] + if d not in self.DELIMITER_PAIRS: + continue + + end = self.DELIMITER_PAIRS[d] + stack.append(end) + + for match in self.RE_DELIM.finditer(line[offset:]): + pos = match.start() + offset + + d = line[pos] + + if d in self.DELIMITER_PAIRS: + end = self.DELIMITER_PAIRS[d] + + stack.append(end) + continue + + # Does the end delimiter match what it is expected? + if stack and d == stack[-1]: + stack.pop() + + if not stack: + yield start, offset, pos + 1 + break + + def search(self, regex, line): + """ + This is similar to re.search: + + It matches a regex that it is followed by a delimiter, + returning occurrences only if all delimiters are paired. + """ + + for t in self._search(regex, line): + + yield line[t[0]:t[2]] + + def sub(self, regex, sub, line, count=0): + """ + This is similar to re.sub: + + It matches a regex that it is followed by a delimiter, + replacing occurrences only if all delimiters are paired. + + if r'\1' is used, it works just like re: it places there the + matched paired data with the delimiter stripped. + + If count is different than zero, it will replace at most count + items. + """ + out = "" + + cur_pos = 0 + n = 0 + + for start, end, pos in self._search(regex, line): + out += line[cur_pos:start] + + # Value, ignoring start/end delimiters + value = line[end:pos - 1] + + # replaces \1 at the sub string, if \1 is used there + new_sub = sub + new_sub = new_sub.replace(r'\1', value) + + out += new_sub + + # Drop end ';' if any + if line[pos] == ';': + pos += 1 + + cur_pos = pos + n += 1 + + if count and count >= n: + break + + # Append the remaining string + l = len(line) + out += line[cur_pos:l] + + return out diff --git a/scripts/minikconf.py b/scripts/minikconf.py index 6f7f43b291..4de5aeed11 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -340,7 +340,7 @@ class KconfigParser: @classmethod def parse(self, fp, mode=None): - data = KconfigData(mode or KconfigParser.defconfig) + data = KconfigData(mode or defconfig) parser = KconfigParser(data) parser.parse_file(fp) return data @@ -363,7 +363,9 @@ class KconfigParser: def do_assignment(self, var, val): if not var.startswith("CONFIG_"): - raise Error('assigned variable should start with CONFIG_') + raise KconfigParserError( + self, "assigned variable should start with CONFIG_" + ) var = self.data.do_var(var[7:]) self.data.do_assignment(var, val) diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 6b86f01b01..aea751fb04 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -105,20 +105,15 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, .u.q_unix.path = path }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; - uint32_t flags; int r; assert(!pr_mgr->ioc); qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); + r = qio_channel_socket_connect_sync(sioc, &saddr, errp); g_free(path); - if (local_err) { + if (r < 0) { object_unref(OBJECT(sioc)); - error_propagate(errp, local_err); return -ENOTCONN; } diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 86e5260e50..bcd13cd6df 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -666,7 +666,7 @@ void do_common_semihosting(CPUState *cs) int i; #ifdef CONFIG_USER_ONLY TaskState *ts = get_task_state(cs); - target_ulong limit; + static abi_ulong heapbase, heaplimit; #else LayoutInfo info = common_semi_find_bases(cs); #endif @@ -678,25 +678,25 @@ void do_common_semihosting(CPUState *cs) * Some C libraries assume the heap immediately follows .bss, so * allocate it using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - - ts->heap_base = do_brk(0); - limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; + if (!heaplimit) { + heapbase = do_brk(0); /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(limit); + for (abi_ulong size = COMMON_SEMI_HEAP_SIZE; ; size >>= 1) { + abi_ulong limit = heapbase + size; + abi_ulong ret = do_brk(limit); if (ret >= limit) { + heaplimit = limit; break; } - limit = (ts->heap_base >> 1) + (limit >> 1); } - ts->heap_limit = limit; } - - retvals[0] = ts->heap_base; - retvals[1] = ts->heap_limit; - retvals[2] = ts->stack_base; + retvals[0] = heapbase; + retvals[1] = heaplimit; + /* + * Note that semihosting is *not* thread aware. + * Always return the stack base of the main thread. + */ + retvals[2] = ts->info->start_stack; retvals[3] = 0; /* Stack limit. */ #else retvals[0] = info.heapbase; /* Heap Base */ diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build index 5759df3ecc..ba7de07029 100644 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -1,6 +1,6 @@ project('proc-macro2-1-rs', 'rust', meson_version: '>=1.5.0', - version: '1.0.84', + version: '1.0.95', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index a009417408..3e6dc318a9 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -1,6 +1,6 @@ project('syn-2-rs', 'rust', meson_version: '>=1.5.0', - version: '2.0.66', + version: '2.0.104', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 6c9369f0df..0f06cd8e11 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = proc-macro2-1.0.84 -source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.84/download -source_filename = proc-macro2-1.0.84.0.tar.gz -source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 +directory = proc-macro2-1.0.95 +source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.95/download +source_filename = proc-macro2-1.0.95.0.tar.gz +source_hash = 02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778 #method = cargo patch_directory = proc-macro2-1-rs diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index d79cf750fb..1e5e9d9fb6 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = syn-2.0.66 -source_url = https://crates.io/api/v1/crates/syn/2.0.66/download -source_filename = syn-2.0.66.0.tar.gz -source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 +directory = syn-2.0.104 +source_url = https://crates.io/api/v1/crates/syn/2.0.104/download +source_filename = syn-2.0.104.0.tar.gz +source_hash = 17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40 #method = cargo patch_directory = syn-2-rs diff --git a/system/cpus.c b/system/cpus.c index 256723558d..437848b5eb 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -254,9 +254,16 @@ int64_t cpus_get_elapsed_ticks(void) return cpu_get_ticks(); } +void cpu_set_interrupt(CPUState *cpu, int mask) +{ + /* Pairs with cpu_test_interrupt(). */ + qatomic_store_release(&cpu->interrupt_request, + cpu->interrupt_request | mask); +} + void generic_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); diff --git a/system/memory.c b/system/memory.c index 5646547940..44701c465c 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2546,6 +2546,21 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr) } } +void memory_region_enable_lockless_io(MemoryRegion *mr) +{ + mr->lockless_io = true; + /* + * reentrancy_guard has per device scope, that when enabled + * will effectively prevent concurrent access to device's IO + * MemoryRegion(s) by not calling accessor callback. + * + * Turn it off for lock-less IO enabled devices, to allow + * concurrent IO. + * TODO: remove this when reentrancy_guard becomes per transaction. + */ + mr->disable_reentrancy_guard = true; +} + void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, diff --git a/system/physmem.c b/system/physmem.c index e5dd760e0b..f498572fc8 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2900,7 +2900,7 @@ bool prepare_mmio_access(MemoryRegion *mr) { bool release_lock = false; - if (!bql_locked()) { + if (!bql_locked() && !mr->lockless_io) { bql_lock(); release_lock = true; } diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index bf1787a69d..932cddac05 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -86,10 +86,10 @@ static bool alpha_cpu_has_work(CPUState *cs) assume that if a CPU really wants to stay asleep, it will mask interrupts at the chipset level, which will prevent these bits from being set in the first place. */ - return cs->interrupt_request & (CPU_INTERRUPT_HARD - | CPU_INTERRUPT_TIMER - | CPU_INTERRUPT_SMP - | CPU_INTERRUPT_MCHK); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD + | CPU_INTERRUPT_TIMER + | CPU_INTERRUPT_SMP + | CPU_INTERRUPT_MCHK); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c index 9c4431c18b..31c01eddc8 100644 --- a/target/arm/cpregs-pmu.c +++ b/target/arm/cpregs-pmu.c @@ -228,22 +228,27 @@ static bool event_supported(uint16_t number) return supported_event_map[number] != UNSUPPORTED_EVENT; } -static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult do_pmreg_access(CPUARMState *env, bool is_pmcr) { /* * Performance monitor registers user accessibility is controlled - * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable + * by PMUSERENR. MDCR_EL2.TPM/TPMCR and MDCR_EL3.TPM allow configurable * trapping to EL2 or EL3 for other accesses. */ int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { return CP_ACCESS_TRAP_EL1; } - if (el < 2 && (mdcr_el2 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL2; + if (el < 2) { + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + + if (mdcr_el2 & MDCR_TPM) { + return CP_ACCESS_TRAP_EL2; + } + if (is_pmcr && (mdcr_el2 & MDCR_TPMCR)) { + return CP_ACCESS_TRAP_EL2; + } } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { return CP_ACCESS_TRAP_EL3; @@ -252,6 +257,19 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, false); +} + +static CPAccessResult pmreg_access_pmcr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, true); +} + static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -1187,14 +1205,14 @@ void define_pm_cpregs(ARMCPU *cpu) .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, + .accessfn = pmreg_access_pmcr, .readfn = pmcr_read, .raw_readfn = raw_read, .writefn = pmcr_write, .raw_writefn = raw_write, }; const ARMCPRegInfo pmcr64 = { .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access, + .access = PL0_RW, .accessfn = pmreg_access_pmcr, .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 5876162428..e49e0ae3af 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -406,9 +406,14 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, CRC32) != 0; } -static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +static inline bool isar_feature_aa64_lse(const ARMISARegisters *id) { - return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; +} + +static inline bool isar_feature_aa64_lse128(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 3; } static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) @@ -604,6 +609,11 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES); } +static inline bool isar_feature_aa64_cssc(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, CSSC) != 0; +} + static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); @@ -904,6 +914,16 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } +static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, TCRX) != 0; +} + +static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e2b2337399..d0f6fcdfce 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -142,11 +142,11 @@ static bool arm_cpu_has_work(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); return (cpu->power_state != PSCI_OFF) - && cs->interrupt_request & - (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD - | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI - | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR - | CPU_INTERRUPT_EXITTB); + && cpu_test_interrupt(cs, + CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI + | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR + | CPU_INTERRUPT_EXITTB); } #endif /* !CONFIG_USER_ONLY */ @@ -644,6 +644,12 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + env->cp15.scr_el3 |= SCR_TCR2EN; + } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + env->cp15.scr_el3 |= SCR_SCTLR2EN; + } } if (target_el == 2) { @@ -958,7 +964,7 @@ void arm_cpu_update_virq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); } else { @@ -980,7 +986,7 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); } else { @@ -1002,7 +1008,7 @@ void arm_cpu_update_vinmi(ARMCPU *cpu) (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VINMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VINMI); } else { @@ -1022,7 +1028,7 @@ void arm_cpu_update_vfnmi(ARMCPU *cpu) bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && (arm_hcrx_el2_eff(env) & HCRX_VFNMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); } else { @@ -1041,7 +1047,7 @@ void arm_cpu_update_vserr(ARMCPU *cpu) bool new_state = env->cp15.hcr_el2 & HCR_VSE; - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VSERR) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VSERR); } else { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index dc9b6dce4c..c15d79a106 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -337,6 +337,7 @@ typedef struct CPUArchState { }; uint64_t sctlr_el[4]; }; + uint64_t sctlr2_el[4]; /* Extension to System control register. */ uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ @@ -365,6 +366,7 @@ typedef struct CPUArchState { uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */ /* MMU translation table base control. */ uint64_t tcr_el[4]; + uint64_t tcr2_el[3]; uint64_t vtcr_el2; /* Virtualization Translation Control. */ uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ @@ -1420,6 +1422,19 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ #define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ +#define SCTLR2_EMEC (1ULL << 1) /* FEAT_MEC */ +#define SCTLR2_NMEA (1ULL << 2) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENADERR (1ULL << 3) /* FEAT_ADERR */ +#define SCTLR2_ENANERR (1ULL << 4) /* FEAT_ANERR */ +#define SCTLR2_EASE (1ULL << 5) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENIDCP128 (1ULL << 6) /* FEAT_SYSREG128 */ +#define SCTLR2_ENPACM (1ULL << 7) /* FEAT_PAuth_LR */ +#define SCTLR2_ENPACM0 (1ULL << 8) /* FEAT_PAuth_LR */ +#define SCTLR2_CPTA (1ULL << 9) /* FEAT_CPA2 */ +#define SCTLR2_CPTA0 (1ULL << 10) /* FEAT_CPA2 */ +#define SCTLR2_CPTM (1ULL << 11) /* FEAT_CPA2 */ +#define SCTLR2_CPTM0 (1ULL << 12) /* FEAT_CAP2 */ + #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1712,6 +1727,8 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_HXEN (1ULL << 38) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) +#define SCR_TCR2EN (1ULL << 43) +#define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0c1299ff84..19637e7301 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -741,6 +741,12 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= SCR_TCR2EN; + } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= SCR_SCTLR2EN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -833,40 +839,40 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t ret = 0; if (hcr_el2 & HCR_IMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { ret |= ISR_IS; ret |= CPSR_I; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { ret |= ISR_IS; ret |= CPSR_I; } } if (hcr_el2 & HCR_FMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { ret |= CPSR_F; } - if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { ret |= ISR_FS; ret |= CPSR_F; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_FIQ)) { ret |= CPSR_F; } } if (hcr_el2 & HCR_AMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VSERR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { ret |= CPSR_A; } } @@ -3907,23 +3913,24 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; - /* FEAT_MOPS adds MSCEn and MCE2 */ if (cpu_isar_feature(aa64_mops, cpu)) { valid_mask |= HCRX_MSCEN | HCRX_MCE2; } - - /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ if (cpu_isar_feature(aa64_nmi, cpu)) { valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; } - /* FEAT_CMOW adds CMOW */ if (cpu_isar_feature(aa64_cmow, cpu)) { valid_mask |= HCRX_CMOW; } - /* FEAT_XS adds FGTnXS, FnXS */ if (cpu_isar_feature(aa64_xs, cpu)) { valid_mask |= HCRX_FGTNXS | HCRX_FNXS; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= HCRX_TCR2EN; + } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= HCRX_SCTLR2EN; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -3981,11 +3988,19 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) * This may need to be revisited for future bits. */ if (!arm_is_el2_enabled(env)) { + ARMCPU *cpu = env_archcpu(env); uint64_t hcrx = 0; - if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { - /* MSCEn behaves as 1 if EL2 is not enabled */ + + /* Bits which whose effective value is 1 if el2 not enabled. */ + if (cpu_isar_feature(aa64_mops, cpu)) { hcrx |= HCRX_MSCEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + hcrx |= HCRX_TCR2EN; + } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + hcrx |= HCRX_SCTLR2EN; + } return hcrx; } if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { @@ -4513,6 +4528,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), "SCTLR", "SCTLR_EL2", "SCTLR_EL12" }, + { K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3), + "SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 }, { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), "CPACR", "CPTR_EL2", "CPACR_EL12" }, { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), @@ -4521,6 +4538,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) "TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" }, { K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2), "TCR_EL1", "TCR_EL2", "TCR_EL12" }, + { K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3), + "TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 }, { K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0), "SPSR_EL1", "SPSR_EL2", "SPSR_EL12" }, { K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1), @@ -5994,6 +6013,133 @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = { .resetvalue = 0 }, }; +static CPAccessResult sctlr2_el2_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult sctlr2_el1_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return sctlr2_el2_access(env, ri, isread); +} + +static void sctlr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo sctlr2_reginfo[] = { + { .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL1_RW, .accessfn = sctlr2_el1_access, + .writefn = sctlr2_el1_write, .fgt = FGT_SCTLR_EL1, + .nv2_redirect_offset = 0x278 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) }, + { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL2_RW, .accessfn = sctlr2_el2_access, + .writefn = sctlr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) }, + { .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL3_RW, .writefn = sctlr2_el3_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, +}; + +static CPAccessResult tcr2_el2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_TCR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_TCR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return tcr2_el2_access(env, ri, isread); +} + +static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo tcr2_reginfo[] = { + { .name = "TCR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL1_RW, .accessfn = tcr2_el1_access, + .writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1, + .nv2_redirect_offset = 0x270 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) }, + { .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL2_RW, .accessfn = tcr2_el2_access, + .writefn = tcr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7223,6 +7369,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, nmi_reginfo); } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + define_arm_cp_regs(cpu, sctlr2_reginfo); + } + + if (cpu_isar_feature(aa64_tcr2, cpu)) { + define_arm_cp_regs(cpu, tcr2_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } @@ -9147,7 +9301,7 @@ void arm_cpu_do_interrupt(CPUState *cs) arm_call_el_change_hook(cpu); if (!kvm_enabled()) { - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB); } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 47b0cd3a35..b77db99079 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1782,13 +1782,13 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) static int hvf_inject_interrupts(CPUState *cpu) { - if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_FIQ)) { trace_hvf_inject_fiq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_FIQ, true); } - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { trace_hvf_inject_irq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_IRQ, true); @@ -1840,7 +1840,7 @@ static void hvf_wfi(CPUState *cpu) uint64_t nanos; uint32_t cntfrq; - if (cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { /* Interrupt pending, no need to wait */ return; } diff --git a/target/arm/internals.h b/target/arm/internals.h index 1b3d0244fd..f5a1e75db3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -113,11 +113,6 @@ FIELD(DBGWCR, WT, 20, 1) FIELD(DBGWCR, MASK, 24, 5) FIELD(DBGWCR, SSCE, 29, 1) -#define VTCR_NSW (1u << 29) -#define VTCR_NSA (1u << 30) -#define VSTCR_SW VTCR_NSW -#define VSTCR_SA VTCR_NSA - /* Bit definitions for CPACR (AArch32 only) */ FIELD(CPACR, CP10, 20, 2) FIELD(CPACR, CP11, 22, 2) @@ -201,6 +196,24 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define TTBCR_SH1 (1U << 28) #define TTBCR_EAE (1U << 31) +#define TCR2_PNCH (1ULL << 0) +#define TCR2_PIE (1ULL << 1) +#define TCR2_E0POE (1ULL << 2) +#define TCR2_POE (1ULL << 3) +#define TCR2_AIE (1ULL << 4) +#define TCR2_D128 (1ULL << 5) +#define TCR2_PTTWI (1ULL << 10) +#define TCR2_HAFT (1ULL << 11) +#define TCR2_AMEC0 (1ULL << 12) +#define TCR2_AMEC1 (1ULL << 13) +#define TCR2_DISCH0 (1ULL << 14) +#define TCR2_DISCH1 (1ULL << 15) +#define TCR2_A2 (1ULL << 16) +#define TCR2_FNG0 (1ULL << 17) +#define TCR2_FNG1 (1ULL << 18) +#define TCR2_FNGNA0 (1ULL << 20) +#define TCR2_FNGNA1 (1ULL << 21) + FIELD(VTCR, T0SZ, 0, 6) FIELD(VTCR, SL0, 6, 2) FIELD(VTCR, IRGN0, 8, 2) @@ -220,6 +233,9 @@ FIELD(VTCR, NSA, 30, 1) FIELD(VTCR, DS, 32, 1) FIELD(VTCR, SL2, 33, 1) +FIELD(VSTCR, SW, 29, 1) +FIELD(VSTCR, SA, 30, 1) + #define HCRX_ENAS0 (1ULL << 0) #define HCRX_ENALS (1ULL << 1) #define HCRX_ENASR (1ULL << 2) @@ -232,6 +248,8 @@ FIELD(VTCR, SL2, 33, 1) #define HCRX_CMOW (1ULL << 9) #define HCRX_MCE2 (1ULL << 10) #define HCRX_MSCEN (1ULL << 11) +#define HCRX_TCR2EN (1ULL << 14) +#define HCRX_SCTLR2EN (1ULL << 15) #define HPFAR_NS (1ULL << 63) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 561bf2678e..ed5c728eab 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -193,9 +193,9 @@ static ARMMMUIdx ptw_idx_for_stage_2(CPUARMState *env, ARMMMUIdx stage2idx) return ARMMMUIdx_Phys_Realm; case ARMSS_Secure: if (stage2idx == ARMMMUIdx_Stage2_S) { - s2walk_secure = !(env->cp15.vstcr_el2 & VSTCR_SW); + s2walk_secure = !(env->cp15.vstcr_el2 & R_VSTCR_SW_MASK); } else { - s2walk_secure = !(env->cp15.vtcr_el2 & VTCR_NSW); + s2walk_secure = !(env->cp15.vtcr_el2 & R_VTCR_NSW_MASK); } return s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; default: @@ -3372,9 +3372,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ if (in_space == ARMSS_Secure) { result->f.attrs.secure = - !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)) + !(env->cp15.vstcr_el2 & (R_VSTCR_SA_MASK | R_VSTCR_SW_MASK)) && (ipa_secure - || !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW))); + || !(env->cp15.vtcr_el2 & (R_VTCR_NSA_MASK | R_VTCR_NSW_MASK))); result->f.attrs.space = arm_secure_to_space(result->f.attrs.secure); } diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8c798cde2b..55ff6c504f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -156,6 +156,16 @@ MOVZ . 10 100101 .. ................ ..... @movw_32 MOVK . 11 100101 .. ................ ..... @movw_64 MOVK . 11 100101 .. ................ ..... @movw_32 +# Min/Max (immediate) + +@minmaxi_s sf:1 .. ........... imm:s8 rn:5 rd:5 &rri_sf +@minmaxi_u sf:1 .. ........... imm:8 rn:5 rd:5 &rri_sf + +SMAX_i . 00 1000111 0000 ........ ..... ..... @minmaxi_s +SMIN_i . 00 1000111 0010 ........ ..... ..... @minmaxi_s +UMAX_i . 00 1000111 0001 ........ ..... ..... @minmaxi_u +UMIN_i . 00 1000111 0011 ........ ..... ..... @minmaxi_u + # Bitfield &bitfield rd rn sf immr imms @@ -536,6 +546,13 @@ SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 +# Atomic 128-bit memory operations +&atomic128 rn rt rt2 a r +@atomic128 ........ a:1 r:1 . rt2:5 ...... rn:5 rt:5 &atomic128 +LDCLRP 00011001 . . 1 ..... 000100 ..... ..... @atomic128 +LDSETP 00011001 . . 1 ..... 001100 ..... ..... @atomic128 +SWPP 00011001 . . 1 ..... 100000 ..... ..... @atomic128 + # Load/store register (pointer authentication) # LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous @@ -698,6 +715,11 @@ GMI 1 00 11010110 ..... 000101 ..... ..... @rrr PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr +SMAX . 00 11010110 ..... 011000 ..... ..... @rrr_sf +SMIN . 00 11010110 ..... 011010 ..... ..... @rrr_sf +UMAX . 00 11010110 ..... 011001 ..... ..... @rrr_sf +UMIN . 00 11010110 ..... 011011 ..... ..... @rrr_sf + # Data Processing (1-source) @rr . .......... ..... ...... rn:5 rd:5 &rr @@ -711,6 +733,10 @@ REV64 1 10 11010110 00000 000011 ..... ..... @rr CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf CLS . 10 11010110 00000 000101 ..... ..... @rr_sf +CTZ . 10 11010110 00000 000110 ..... ..... @rr_sf +CNT . 10 11010110 00000 000111 ..... ..... @rr_sf +ABS . 10 11010110 00000 001000 ..... ..... @rr_sf + &pacaut rd rn z @pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 35cddbafa4..b8b1981e70 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1145,7 +1145,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */ - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* FEAT_LSE, FEAT_LSE128 */ t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ @@ -1178,6 +1178,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ + t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64PFR0); @@ -1247,7 +1248,11 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ SET_IDREG(isar, ID_AA64MMFR2, t); - FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + t = GET_IDREG(isar, ID_AA64MMFR3); + t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64ZFR0); t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index dbf47595db..37bedc3780 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3237,7 +3237,7 @@ static bool trans_LDXP(DisasContext *s, arg_stxr *a) static bool trans_CASP(DisasContext *s, arg_CASP *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } if (((a->rt | a->rs) & 1) != 0) { @@ -3250,7 +3250,7 @@ static bool trans_CASP(DisasContext *s, arg_CASP *a) static bool trans_CAS(DisasContext *s, arg_CAS *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz); @@ -3743,15 +3743,64 @@ static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn, return true; } -TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) -TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) -TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) -TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) -TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) -TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) -TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) -TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) -TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) +TRANS_FEAT(LDADD, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) +TRANS_FEAT(LDCLR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) +TRANS_FEAT(LDEOR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) +TRANS_FEAT(LDSET, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) +TRANS_FEAT(LDSMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) +TRANS_FEAT(LDSMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) +TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) +TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) +TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) + +typedef void Atomic128ThreeOpFn(TCGv_i128, TCGv_i64, TCGv_i128, TCGArg, MemOp); + +static bool do_atomic128_ld(DisasContext *s, arg_atomic128 *a, + Atomic128ThreeOpFn *fn, bool invert) +{ + MemOp mop; + int rlo, rhi; + TCGv_i64 clean_addr, tlo, thi; + TCGv_i128 t16; + + if (a->rt == 31 || a->rt2 == 31 || a->rt == a->rt2) { + return false; + } + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = check_atomic_align(s, a->rn, MO_128); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + + rlo = (s->be_data == MO_LE ? a->rt : a->rt2); + rhi = (s->be_data == MO_LE ? a->rt2 : a->rt); + + tlo = read_cpu_reg(s, rlo, true); + thi = read_cpu_reg(s, rhi, true); + if (invert) { + tcg_gen_not_i64(tlo, tlo); + tcg_gen_not_i64(thi, thi); + } + /* + * The tcg atomic primitives are all full barriers. Therefore we + * can ignore the Acquire and Release bits of this instruction. + */ + t16 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(t16, tlo, thi); + + fn(t16, clean_addr, t16, get_mem_index(s), mop); + + tcg_gen_extr_i128_i64(cpu_reg(s, rlo), cpu_reg(s, rhi), t16); + return true; +} + +TRANS_FEAT(LDCLRP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_and_i128, true) +TRANS_FEAT(LDSETP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_or_i128, false) +TRANS_FEAT(SWPP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_xchg_i128, false) static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { @@ -3759,7 +3808,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) TCGv_i64 clean_addr; MemOp mop; - if (!dc_isar_feature(aa64_atomics, s) || + if (!dc_isar_feature(aa64_lse, s) || !dc_isar_feature(aa64_rcpc_8_3, s)) { return false; } @@ -4553,6 +4602,50 @@ TRANS(ADDS_i, gen_rri, a, 0, 1, a->sf ? gen_add64_CC : gen_add32_CC) TRANS(SUBS_i, gen_rri, a, 0, 1, a->sf ? gen_sub64_CC : gen_sub32_CC) /* + * Min/Max (immediate) + */ + +static void gen_wrap3_i32(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, NeonGenTwoOpFn fn) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t1, n); + tcg_gen_extrl_i64_i32(t2, m); + fn(t1, t1, t2); + tcg_gen_extu_i32_i64(d, t1); +} + +static void gen_smax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smax_i32); +} + +static void gen_smin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smin_i32); +} + +static void gen_umax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umax_i32); +} + +static void gen_umin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umin_i32); +} + +TRANS_FEAT(SMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + +/* * Add/subtract (immediate, with tags) */ @@ -8157,6 +8250,28 @@ static bool trans_PACGA(DisasContext *s, arg_rrr *a) return false; } +static bool gen_rrr(DisasContext *s, arg_rrr_sf *a, ArithTwoOp fn) +{ + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + + fn(tcg_rd, tcg_rn, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +TRANS_FEAT(SMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + typedef void ArithOneOp(TCGv_i64, TCGv_i64); static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) @@ -8165,13 +8280,22 @@ static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) return true; } -static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +/* + * Perform 32-bit operation fn on the low half of n; + * the high half of the output is zeroed. + */ +static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn) { - TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t32, tcg_rn); - gen_helper_rbit(t32, t32); - tcg_gen_extu_i32_i64(tcg_rd, t32); + tcg_gen_extrl_i64_i32(t, n); + fn(t, t); + tcg_gen_extu_i32_i64(d, t); +} + +static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit); } static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) @@ -8227,15 +8351,42 @@ static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_clrsb_i32); +} + +TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) +TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) + +static void gen_ctz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ TCGv_i32 t32 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t32, tcg_rn); - tcg_gen_clrsb_i32(t32, t32); + tcg_gen_ctzi_i32(t32, t32, 32); tcg_gen_extu_i32_i64(tcg_rd, t32); } -TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) -TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) +static void gen_ctz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_ctzi_i64(tcg_rd, tcg_rn, 64); +} + +static void gen_cnt32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_ctpop_i32); +} + +static void gen_abs32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_abs_i32); +} + +TRANS_FEAT(CTZ, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? gen_ctz64 : gen_ctz32) +TRANS_FEAT(CNT, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_ctpop_i64 : gen_cnt32) +TRANS_FEAT(ABS, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_abs_i64 : gen_abs32) static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn) { diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 6995de6a12..a6df71d020 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -45,7 +45,7 @@ static vaddr avr_cpu_get_pc(CPUState *cs) static bool avr_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET)) + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET) && cpu_interrupts_enabled(cpu_env(cs)); } diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 24777727e6..0ca79ee5e2 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -135,7 +135,7 @@ static void hppa_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool hppa_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 818b50419f..8445cadece 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -773,9 +773,9 @@ int hvf_vcpu_exec(CPUState *cpu) switch (exit_reason) { case EXIT_REASON_HLT: { macvm_set_rip(cpu, rip + ins_len); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) - && !(cpu->interrupt_request & CPU_INTERRUPT_NMI) && + && !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI) && !(idtvec_info & VMCS_IDT_VEC_VALID)) { cpu->halted = 1; ret = EXCP_HLT; diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 17fce1d3cd..9e05e0e576 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -395,7 +395,7 @@ bool hvf_inject_interrupts(CPUState *cs) }; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { cs->interrupt_request &= ~CPU_INTERRUPT_NMI; info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; @@ -406,7 +406,7 @@ bool hvf_inject_interrupts(CPUState *cs) } if (!(env->hflags & HF_INHIBIT_IRQ_MASK) && - (cs->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { int line = cpu_get_pic_interrupt(env); cs->interrupt_request &= ~CPU_INTERRUPT_HARD; @@ -415,11 +415,10 @@ bool hvf_inject_interrupts(CPUState *cs) VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); } } - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { vmx_set_int_window_exiting(cs); } - return (cs->interrupt_request - & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)); + return cpu_test_interrupt(cs, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR); } int hvf_process_events(CPUState *cs) @@ -432,25 +431,25 @@ int hvf_process_events(CPUState *cs) env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } - if (cs->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT)) { cpu_synchronize_state(cs); do_cpu_init(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 369626f8c8..306430a052 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5453,8 +5453,8 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) int ret; /* Inject NMI */ - if (cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; bql_unlock(); @@ -5465,7 +5465,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; bql_unlock(); @@ -5478,31 +5478,30 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } - if (!kvm_pic_in_kernel()) { - bql_lock(); - } /* Force the VCPU out of its inner loop to process any INIT requests * or (for userspace APIC, but it is cheap to combine the checks here) * pending TPR access reports. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, 1); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { - cpu->exit_request = 1; + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { + qatomic_set(&cpu->exit_request, 1); } } if (!kvm_pic_in_kernel()) { /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && - (cpu->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) { int irq; + bql_lock(); + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -5517,13 +5516,14 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } + bql_unlock(); } /* If we have an interrupt but the guest is not ready to receive an * interrupt, request an interrupt window exit. This will * cause a return to userspace as soon as the guest is ready to * receive interrupts. */ - if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { run->request_interrupt_window = 1; } else { run->request_interrupt_window = 0; @@ -5531,8 +5531,6 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) DPRINTF("setting tpr\n"); run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state); - - bql_unlock(); } } @@ -5595,7 +5593,7 @@ int kvm_arch_process_async_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (cs->interrupt_request & CPU_INTERRUPT_MCE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_MCE)) { /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); @@ -5618,7 +5616,7 @@ int kvm_arch_process_async_events(CPUState *cs) } } - if ((cs->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { kvm_cpu_synchronize_state(cs); do_cpu_init(cpu); @@ -5628,20 +5626,20 @@ int kvm_arch_process_async_events(CPUState *cs) return 0; } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { kvm_cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; kvm_cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, @@ -5656,9 +5654,9 @@ static int kvm_handle_halt(X86CPU *cpu) CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - if (!((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) && - !(cs->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 1; return EXCP_HLT; } diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index 58ce3df53a..890322ae37 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -57,13 +57,9 @@ QIOChannelSocket *vmsr_open_socket(const char *path) }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); - if (local_err) { + if (qio_channel_socket_connect_sync(sioc, &saddr, NULL) < 0) { /* Close socket. */ qio_channel_close(QIO_CHANNEL(sioc), NULL); object_unref(OBJECT(sioc)); diff --git a/target/i386/machine.c b/target/i386/machine.c index dd2dac1d44..45b7cea80a 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -462,6 +462,24 @@ static const VMStateDescription vmstate_exception_info = { } }; +static bool cpu_errcode_needed(void *opaque) +{ + X86CPU *cpu = opaque; + + return cpu->env.has_error_code != 0; +} + +static const VMStateDescription vmstate_error_code = { + .name = "cpu/error_code", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_errcode_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(env.error_code, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + /* Poll control MSR enabled by default */ static bool poll_control_msr_needed(void *opaque) { @@ -1746,6 +1764,7 @@ const VMStateDescription vmstate_x86_cpu = { }, .subsections = (const VMStateDescription * const []) { &vmstate_exception_info, + &vmstate_error_code, &vmstate_async_pf_msr, &vmstate_async_pf_int_msr, &vmstate_pv_eoi_msr, diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 92e3b8b2f4..c1ac74c4f0 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -413,11 +413,11 @@ nvmm_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests * or commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { if (nvmm_can_take_nmi(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; event->type = NVMM_VCPU_EVENT_INTR; @@ -426,7 +426,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { if (nvmm_can_take_int(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; event->type = NVMM_VCPU_EVENT_INTR; @@ -436,7 +436,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } /* Don't want SMIs. */ - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } @@ -651,9 +651,9 @@ nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -691,25 +691,25 @@ nvmm_vcpu_loop(CPUState *cpu) * Some asynchronous events must be handled outside of the inner * VCPU loop. They are handled here. */ - if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT)) { nvmm_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); /* set int/nmi windows back to the reset state */ } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { nvmm_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; nvmm_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index d4ea890c12..794a23ddfc 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -133,7 +133,7 @@ bool x86_cpu_exec_halt(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { bql_lock(); apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index b27049b9ed..3569196bdd 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -49,7 +49,7 @@ static void svm_save_seg(CPUX86State *env, int mmu_idx, hwaddr addr, static inline void svm_canonicalization(CPUX86State *env, target_ulong *seg_base) { uint16_t shift_amt = 64 - cpu_x86_virtual_addr_width(env); - *seg_base = ((((long) *seg_base) << shift_amt) >> shift_amt); + *seg_base = (((int64_t) *seg_base) << shift_amt) >> shift_amt; } static void svm_load_seg(CPUX86State *env, int mmu_idx, hwaddr addr, @@ -403,7 +403,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (ctl_has_irq(env)) { - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; + cpu_set_interrupt(cs, CPU_INTERRUPT_VIRQ); } if (virtual_gif_set(env)) { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index b72dcff3c8..878cdd1668 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1436,9 +1436,9 @@ static int whpx_handle_halt(CPUState *cpu) int ret = 0; bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -1469,15 +1469,15 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Inject NMI */ if (!vcpu->interruption_pending && - cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; vcpu->interruptable = false; new_int.InterruptionType = WHvX64PendingNmi; new_int.InterruptionPending = 1; new_int.InterruptionVector = 2; } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } } @@ -1486,12 +1486,12 @@ static void whpx_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests or * commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { cpu->exit_request = 1; } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } } @@ -1501,7 +1501,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (!vcpu->interruption_pending && vcpu->interruptable && (env->eflags & IF_MASK)) { assert(!new_int.InterruptionPending); - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1519,7 +1519,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) reg_count += 1; } } else if (vcpu->ready_for_pic_interrupt && - (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1546,7 +1546,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Update the state of the interrupt delivery notification */ if (!vcpu->window_registered && - cpu->interrupt_request & CPU_INTERRUPT_HARD) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { reg_values[reg_count].DeliverabilityNotifications = (WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) { .InterruptNotification = 1 @@ -1599,30 +1599,30 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) CPUX86State *env = &x86_cpu->env; AccelCPUState *vcpu = cpu->accel; - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { whpx_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); vcpu->interruptable = true; } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { whpx_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h new file mode 100644 index 0000000000..0068d22efc --- /dev/null +++ b/target/loongarch/cpu-mmu.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_MMU_H +#define LOONGARCH_CPU_MMU_H + +typedef enum TLBRet { + TLBRET_MATCH, + TLBRET_BADADDR, + TLBRET_NOMATCH, + TLBRET_INVALID, + TLBRET_DIRTY, + TLBRET_RI, + TLBRET_XI, + TLBRET_PE, +} TLBRet; + +typedef struct MMUContext { + vaddr addr; + uint64_t pte; + hwaddr physical; + int ps; /* page size shift */ + int prot; +} MMUContext; + +bool check_ps(CPULoongArchState *ent, uint8_t ps); +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx); +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx, + int is_debug); +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level); +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +#endif /* LOONGARCH_CPU_MMU_H */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index abad84c054..55ee317bf2 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -17,6 +17,7 @@ #include "hw/qdev-properties.h" #include "exec/translation-block.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" #include "csr.h" @@ -376,7 +377,7 @@ static bool loongarch_cpu_has_work(CPUState *cs) { bool has_work = false; - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) { has_work = true; } @@ -422,6 +423,96 @@ static void loongarch_la464_init_csr(Object *obj) #endif } +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (cpu->lsx == ON_OFF_AUTO_OFF) { + cpu->lasx = ON_OFF_AUTO_OFF; + if (cpu->lasx == ON_OFF_AUTO_ON) { + error_setg(errp, "Failed to disable LSX since LASX is enabled"); + return; + } + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LSX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { + error_setg(errp, "Failed to enable LSX in TCG mode"); + return; + } + } else { + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); + val = cpu->env.cpucfg[2]; + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { + error_setg(errp, "Failed to enable LASX since lSX is disabled"); + return; + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LASX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { + error_setg(errp, "Failed to enable LASX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); +} + +static 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, + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + /* lbt is enabled only in kvm mode, not supported in tcg mode */ + if (kvm_enabled()) { + kvm_loongarch_cpu_post_init(cpu); + } +} + static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -683,96 +774,6 @@ static void loongarch_cpu_unrealizefn(DeviceState *dev) lacc->parent_unrealize(dev); } -static bool loongarch_get_lsx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lsx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if (cpu->lsx == ON_OFF_AUTO_OFF) { - cpu->lasx = ON_OFF_AUTO_OFF; - if (cpu->lasx == ON_OFF_AUTO_ON) { - error_setg(errp, "Failed to disable LSX since LASX is enabled"); - return; - } - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LSX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lsx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { - error_setg(errp, "Failed to enable LSX in TCG mode"); - return; - } - } else { - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); - val = cpu->env.cpucfg[2]; - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); -} - -static bool loongarch_get_lasx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lasx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { - error_setg(errp, "Failed to enable LASX since lSX is disabled"); - return; - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LASX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lasx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { - error_setg(errp, "Failed to enable LASX in TCG mode"); - return; - } - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); -} - -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, - loongarch_set_lsx); - object_property_add_bool(obj, "lasx", loongarch_get_lasx, - loongarch_set_lasx); - /* lbt is enabled only in kvm mode, not supported in tcg mode */ - if (kvm_enabled()) { - kvm_loongarch_cpu_post_init(cpu); - } -} - static void loongarch_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9538e8d61d..7731f6acdc 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -494,15 +494,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU -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 -void kvm_loongarch_init_irq_routing(void); - #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index b5f732f15b..4a9db3ea4c 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -13,6 +13,7 @@ #include "exec/target_page.h" #include "internals.h" #include "cpu-csr.h" +#include "cpu-mmu.h" #include "tcg/tcg_loongarch.h" void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, @@ -43,15 +44,79 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, } } -static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address) +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx) +{ + uint64_t plv = mmu_idx; + uint64_t tlb_entry, tlb_ppn; + uint8_t tlb_ps, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + + tlb_entry = context->pte; + tlb_ps = context->ps; + tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (context->addr & MAKE_64BIT_MASK(0, tlb_ps)); + context->prot = PAGE_READ; + if (tlb_d) { + context->prot |= PAGE_WRITE; + } + if (!tlb_nx) { + context->prot |= PAGE_EXEC; + } + return TLBRET_MATCH; +} + +static TLBRet loongarch_page_table_walker(CPULoongArchState *env, + MMUContext *context, + int access_type, int mmu_idx) { CPUState *cs = env_cpu(env); target_ulong index, phys; uint64_t dir_base, dir_width; uint64_t base; int level; + vaddr address; + address = context->addr; if ((address >> 63) & 0x1) { base = env->CSR_PGDH; } else { @@ -93,41 +158,20 @@ static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, base = ldq_phys(cs->as, phys); } - /* TODO: check plv and other bits? */ - - /* base is pte, in normal pte format */ - if (!FIELD_EX64(base, TLBENTRY, V)) { - return TLBRET_NOMATCH; - } - - if (!FIELD_EX64(base, TLBENTRY, D)) { - *prot = PAGE_READ; - } else { - *prot = PAGE_READ | PAGE_WRITE; - } - - /* get TARGET_PAGE_SIZE aligned physical address */ - base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); - /* mask RPLV, NX, NR bits */ - base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); - base = FIELD_DP64(base, TLBENTRY_64, NX, 0); - base = FIELD_DP64(base, TLBENTRY_64, NR, 0); - /* mask other attribute bits */ - *physical = base & TARGET_PAGE_MASK; - - return 0; + context->ps = dir_base; + context->pte = base; + return loongarch_check_pte(env, context, access_type, mmu_idx); } -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, - int is_debug) +static TLBRet loongarch_map_address(CPULoongArchState *env, + MMUContext *context, + MMUAccessType access_type, int mmu_idx, + int is_debug) { - int ret; + TLBRet ret; if (tcg_enabled()) { - ret = loongarch_get_addr_from_tlb(env, physical, prot, address, - access_type, mmu_idx); + ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx); if (ret != TLBRET_NOMATCH) { return ret; } @@ -139,14 +183,13 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, * legal mapping, even if the mapping is not yet in TLB. return 0 if * there is a valid map, else none zero. */ - return loongarch_page_table_walker(env, physical, prot, address); + return loongarch_page_table_walker(env, context, access_type, mmu_idx); } return TLBRET_NOMATCH; } -static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, - target_ulong dmw) +static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw) { if (is_la64(env)) { return va & TARGET_VIRT_MASK; @@ -157,9 +200,9 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, } } -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug) +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx, + int is_debug) { int user_mode = mmu_idx == MMU_USER_IDX; int kernel_mode = mmu_idx == MMU_KERNEL_IDX; @@ -167,11 +210,13 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, int64_t addr_high; uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); + vaddr address; /* Check PG and DA */ + address = context->addr; if (da & !pg) { - *physical = address & TARGET_PHYS_MASK; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = address & TARGET_PHYS_MASK; + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } @@ -189,8 +234,8 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); } if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { - *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]); + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } } @@ -202,19 +247,18 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Mapped address */ - return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx, is_debug); + return loongarch_map_address(env, context, access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { CPULoongArchState *env = cpu_env(cs); - hwaddr phys_addr; - int prot; + MMUContext context; - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false), 1) != 0) { + context.addr = addr; + if (get_physical_address(env, &context, MMU_DATA_LOAD, + cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) { return -1; } - return phys_addr; + return context.physical; } diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index a7384b0d31..e50d109767 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -32,19 +32,6 @@ void restore_fp_status(CPULoongArchState *env); #endif #ifndef CONFIG_USER_ONLY -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; - -bool check_ps(CPULoongArchState *ent, uint8_t ps); - extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -54,13 +41,6 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug); -void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level); -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); - #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h index 1051a341ec..51475675d6 100644 --- a/target/loongarch/kvm/kvm_loongarch.h +++ b/target/loongarch/kvm/kvm_loongarch.h @@ -5,11 +5,11 @@ * Copyright (c) 2023 Loongson Technology Corporation Limited */ -#include "cpu.h" - #ifndef QEMU_KVM_LOONGARCH_H #define QEMU_KVM_LOONGARCH_H +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +void kvm_loongarch_init_irq_routing(void); int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); void kvm_arch_reset_vcpu(CPUState *cs); diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 28b1bb86bd..0d99e2c92b 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -16,6 +16,7 @@ #include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" #include "cpu-csr.h" +#include "cpu-mmu.h" target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) { diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc index 3d70d75941..77eeedbc42 100644 --- a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -74,38 +74,38 @@ TRANS(sc_w, ALL, gen_sc, MO_TESL) TRANS(ll_d, 64, gen_ll, MO_TEUQ) TRANS(sc_d, 64, gen_sc, MO_TEUQ) TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc index eda3d6e561..298a80cff5 100644 --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -69,6 +69,10 @@ static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) { + if (!avail_64(ctx)) { + return false; + } + return gen_rdtime(ctx, a, 0, 0); } @@ -100,8 +104,8 @@ static bool gen_crc(DisasContext *ctx, arg_rrr *a, TRANS(crc_w_b_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) TRANS(crc_w_h_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) TRANS(crc_w_w_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) -TRANS(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS64(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) TRANS(crcc_w_b_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) TRANS(crcc_w_h_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) TRANS(crcc_w_w_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) -TRANS(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +TRANS64(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/tcg/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc index f4a0dea727..ff6cf3448e 100644 --- a/target/loongarch/tcg/insn_trans/trans_farith.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc @@ -183,16 +183,16 @@ TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) -TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) -TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) +TRANS64(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) +TRANS64(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) -TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) -TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) +TRANS64(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) +TRANS64(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) diff --git a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc index 833c059d6d..ca1d76a366 100644 --- a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc @@ -29,5 +29,5 @@ TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) -TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) -TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) +TRANS64(frint_s, FP_SP, gen_ff, gen_helper_frint_s) +TRANS64(frint_d, FP_DP, gen_ff, gen_helper_frint_d) diff --git a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc index 13452bc7e5..79da4718a5 100644 --- a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc @@ -148,11 +148,11 @@ TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) -TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) -TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) -TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) -TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) -TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) -TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) -TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) -TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) +TRANS64(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) +TRANS64(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) +TRANS64(fldle_s, FP_SP, gen_fload_le, MO_TEUL) +TRANS64(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) +TRANS64(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) +TRANS64(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) +TRANS64(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) +TRANS64(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index ecbfe23b63..34cfab8879 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -233,11 +233,11 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) -TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) +TRANS64(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) -TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) +TRANS64(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) static void check_mmu_idx(DisasContext *ctx) { diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc index 377307785a..136c4c8455 100644 --- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -78,7 +78,7 @@ TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) -TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) +TRANS(rotr_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) @@ -86,5 +86,5 @@ TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) -TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) +TRANS(rotri_w, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index fd4e116022..47702893e3 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -7,6 +7,7 @@ #ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H #define TARGET_LOONGARCH_TCG_LOONGARCH_H #include "cpu.h" +#include "cpu-mmu.h" void loongarch_csr_translate_init(void); @@ -14,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx); +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, + MMUAccessType access_type, int mmu_idx); #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8872593ff0..9365860c8c 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -10,6 +10,7 @@ #include "qemu/guest-random.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" @@ -28,8 +29,8 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } -static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, - MMUAccessType access_type, int tlb_error) +static void raise_mmu_exception(CPULoongArchState *env, vaddr address, + MMUAccessType access_type, TLBRet tlb_error) { CPUState *cs = env_cpu(env); @@ -110,23 +111,20 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) if (!tlb_e) { return; } - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); + addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; + addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS); if (tlb_v0) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, mmu_idx, TARGET_LONG_BITS); } if (tlb_v1) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ - tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, + tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize, mmu_idx, TARGET_LONG_BITS); } } @@ -173,11 +171,8 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) lo1 = env->CSR_TLBELO1; } - /* Only MTLB has the ps fields */ - if (index >= LOONGARCH_STLB) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); - } - + /* Store page size in field PS */ + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); @@ -203,7 +198,7 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, +static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, int *index) { LoongArchTLB *tlb; @@ -283,12 +278,7 @@ void helper_tlbrd(CPULoongArchState *env) index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); tlb = &env->tlb[index]; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); if (!tlb_e) { @@ -476,11 +466,8 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -509,11 +496,8 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -533,13 +517,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; - int ret; + MMUContext context; + TLBRet ret; /* Data access */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx, 0); - + context.addr = address; + ret = get_physical_address(env, &context, access_type, mmu_idx, 0); if (ret == TLBRET_MATCH) { + physical = context.physical; + prot = context.prot; tlb_set_page(cs, address & TARGET_PAGE_MASK, physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); @@ -664,85 +650,31 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) +static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, + MMUContext *context, + MMUAccessType access_type, int index, + int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } - n = (address >> tlb_ps) & 0x1;/* Odd or even */ - - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); - } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; - } - - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } + uint8_t tlb_ps, n; - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + n = (context->addr >> tlb_ps) & 0x1;/* Odd or even */ + context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + context->ps = tlb_ps; + return loongarch_check_pte(env, context, access_type, mmu_idx); } -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, + MMUAccessType access_type, int mmu_idx) { int index, match; - match = loongarch_tlb_search(env, address, &index); + match = loongarch_tlb_search(env, context->addr, &index); if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); + return loongarch_map_tlb_entry(env, context, access_type, index, + mmu_idx); } return TLBRET_NOMATCH; diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 018dc5eb17..bbe015ba57 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -14,6 +14,10 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ { return avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } +#define TRANS64(NAME, AVAIL, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ + { return avail_64(ctx) && avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } + #define avail_ALL(C) true #define avail_64(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, ARCH) == \ CPUCFG1_ARCH_LA64) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 6a09db3a6f..f1b673119d 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -74,7 +74,7 @@ static void m68k_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool m68k_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index ee0a869a94..22231f09e6 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -129,7 +129,7 @@ static void mb_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool mb_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 1f6c41fd34..5989c3ba17 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -145,7 +145,7 @@ static bool mips_cpu_has_work(CPUState *cs) * check for interrupts that can be taken. For pre-release 6 CPUs, * check for CP0 Config7 'Wait IE ignore' bit. */ - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { if (cpu_mips_hw_interrupts_enabled(env) || (env->CP0_Config7 & (1 << CP0C7_WII)) || @@ -160,7 +160,7 @@ static bool mips_cpu_has_work(CPUState *cs) * The QEMU model will issue an _WAKE request whenever the CPUs * should be woken up. */ - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } @@ -170,7 +170,7 @@ static bool mips_cpu_has_work(CPUState *cs) } /* MIPS Release 6 has the ability to halt the CPU. */ if (env->CP0_Config5 & (1 << CP0C5_VP)) { - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } if (!mips_vp_active(env)) { diff --git a/target/mips/kvm.c b/target/mips/kvm.c index ec53acb51a..450947c3fa 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -144,7 +144,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) bql_lock(); - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_io_interrupts_pending(cpu)) { intr.cpu = -1; intr.irq = 2; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index dfbb2df643..9bbfe22ed3 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -78,8 +78,7 @@ static void openrisc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool openrisc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | - CPU_INTERRUPT_TIMER); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index a0e77f2673..db841f1260 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7225,7 +7225,7 @@ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) #ifndef CONFIG_USER_ONLY static bool ppc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 015658049e..d145774b09 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1354,7 +1354,7 @@ static int kvmppc_handle_halt(PowerPCCPU *cpu) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && FIELD_EX64(env->msr, MSR, EE)) { cs->halted = 1; cs->exception_index = EXCP_HLT; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index c6dd5d6f83..da02ae7bf8 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -75,8 +75,7 @@ static void rx_restore_state_to_opc(CPUState *cs, static bool rx_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & - (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); } static int rx_cpu_mmu_index(CPUState *cs, bool ifunc) diff --git a/target/rx/helper.c b/target/rx/helper.c index 0640ab322b..ce003af421 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -44,7 +44,7 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) void rx_cpu_do_interrupt(CPUState *cs) { CPURXState *env = cpu_env(cs); - int do_irq = cs->interrupt_request & INT_FLAGS; + int do_irq = cpu_test_interrupt(cs, INT_FLAGS); uint32_t save_psw; env->in_sleep = 0; diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index 709ccd5299..f3a9ffb2a2 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -49,7 +49,7 @@ bool s390_cpu_has_work(CPUState *cs) return false; } - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { return false; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4f561e8c91..21ccb86df4 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -108,7 +108,7 @@ static bool superh_io_recompile_replay_branch(CPUState *cs, static bool superh_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sh4/helper.c b/target/sh4/helper.c index fb7642bda1..1744ef0e6d 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -58,7 +58,7 @@ int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr) void superh_cpu_do_interrupt(CPUState *cs) { CPUSH4State *env = cpu_env(cs); - int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; + int do_irq = cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); int do_exp, irq_vector = cs->exception_index; /* prioritize exceptions over interrupts */ diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 245caf2de0..c9773f1540 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -783,7 +783,7 @@ static void sparc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool sparc_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & CPU_INTERRUPT_HARD) && + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_interrupts_enabled(cpu_env(cs)); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index bd14c7a0db..49e4e51c6d 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -89,7 +89,7 @@ void cpu_check_irqs(CPUSPARCState *env) * the next bit is (2 << psrpil). */ if (pil < (2 << env->psrpil)) { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_reset_irq(env->interrupt_index); env->interrupt_index = 0; cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -120,7 +120,7 @@ void cpu_check_irqs(CPUSPARCState *env) break; } } - } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + } else if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_disabled(pil, env->pil_in, env->softint, env->interrupt_index); env->interrupt_index = 0; diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 548496002d..67c15fd4d0 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -801,6 +801,8 @@ typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv_i64, TCGv_i32, TCGv_i32); typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64, TCGv_i32); +typedef void (*gen_atomic_op_i128)(TCGv_i128, TCGv_env, TCGv_i64, + TCGv_i128, TCGv_i32); #ifdef CONFIG_ATOMIC64 # define WITH_ATOMIC64(X) X, @@ -1201,6 +1203,94 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, } } +static void do_nonatomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, bool new_val, + void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i128 t = tcg_temp_ebb_new_i128(); + TCGv_i128 r = tcg_temp_ebb_new_i128(); + + tcg_gen_qemu_ld_i128_int(r, addr, idx, memop); + gen(TCGV128_LOW(t), TCGV128_LOW(r), TCGV128_LOW(val)); + gen(TCGV128_HIGH(t), TCGV128_HIGH(r), TCGV128_HIGH(val)); + tcg_gen_qemu_st_i128_int(t, addr, idx, memop); + + tcg_gen_mov_i128(ret, r); + tcg_temp_free_i128(t); + tcg_temp_free_i128(r); +} + +static void do_atomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, void * const table[]) +{ + gen_atomic_op_i128 gen = table[memop & (MO_SIZE | MO_BSWAP)]; + + if (gen) { + MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + /* Produce a result */ + tcg_gen_movi_i64(TCGV128_LOW(ret), 0); + tcg_gen_movi_i64(TCGV128_HIGH(ret), 0); +} + +#define GEN_ATOMIC_HELPER128(NAME, OP, NEW) \ +static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ + [MO_8] = gen_helper_atomic_##NAME##b, \ + [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ + [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ + [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ + [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ + WITH_ATOMIC128([MO_128 | MO_LE] = gen_helper_atomic_##NAME##o_le) \ + WITH_ATOMIC128([MO_128 | MO_BE] = gen_helper_atomic_##NAME##o_be) \ +}; \ +void tcg_gen_atomic_##NAME##_i32_chk(TCGv_i32 ret, TCGTemp *addr, \ + TCGv_i32 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_32); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i32); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ + TCGv_i64 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_64); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i128_chk(TCGv_i128 ret, TCGTemp *addr, \ + TCGv_i128 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) == MO_128); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i128(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i128(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} + #define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ [MO_8] = gen_helper_atomic_##NAME##b, \ @@ -1239,8 +1329,8 @@ void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ } GEN_ATOMIC_HELPER(fetch_add, add, 0) -GEN_ATOMIC_HELPER(fetch_and, and, 0) -GEN_ATOMIC_HELPER(fetch_or, or, 0) +GEN_ATOMIC_HELPER128(fetch_and, and, 0) +GEN_ATOMIC_HELPER128(fetch_or, or, 0) GEN_ATOMIC_HELPER(fetch_xor, xor, 0) GEN_ATOMIC_HELPER(fetch_smin, smin, 0) GEN_ATOMIC_HELPER(fetch_umin, umin, 0) @@ -1266,6 +1356,7 @@ static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) tcg_gen_mov_i64(r, b); } -GEN_ATOMIC_HELPER(xchg, mov2, 0) +GEN_ATOMIC_HELPER128(xchg, mov2, 0) #undef GEN_ATOMIC_HELPER +#undef GEN_ATOMIC_HELPER128 diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build new file mode 100644 index 0000000000..04846c6eb1 --- /dev/null +++ b/tests/functional/aarch64/meson.build @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_aarch64_timeouts = { + 'aspeed_ast2700' : 600, + 'aspeed_ast2700fc' : 600, + 'device_passthrough' : 720, + 'imx8mp_evk' : 240, + 'raspi4' : 480, + 'reverse_debug' : 180, + 'rme_virt' : 1200, + 'rme_sbsaref' : 1200, + 'sbsaref_alpine' : 1200, + 'sbsaref_freebsd' : 720, + 'smmu' : 720, + 'tuxrun' : 240, + 'virt' : 360, + 'virt_gpu' : 480, +} + +tests_aarch64_system_quick = [ + 'migration', +] + +tests_aarch64_system_thorough = [ + 'aspeed_ast2700', + 'aspeed_ast2700fc', + 'device_passthrough', + 'hotplug_pci', + 'imx8mp_evk', + 'kvm', + 'multiprocess', + 'raspi3', + 'raspi4', + 'replay', + 'reverse_debug', + 'rme_virt', + 'rme_sbsaref', + 'sbsaref', + 'sbsaref_alpine', + 'sbsaref_freebsd', + 'smmu', + 'tcg_plugins', + 'tuxrun', + 'virt', + 'virt_gpu', + 'xen', + 'xlnx_versal', +] diff --git a/tests/functional/test_aarch64_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index d02dc7991c..d02dc7991c 100755 --- a/tests/functional/test_aarch64_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index b85370e182..b85370e182 100755 --- a/tests/functional/test_aarch64_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py index 1f3f158a9f..17437784bb 100755 --- a/tests/functional/test_aarch64_device_passthrough.py +++ b/tests/functional/aarch64/test_device_passthrough.py @@ -9,7 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os +from os.path import join from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern @@ -77,15 +77,16 @@ echo device_passthrough_test_ok class Aarch64DevicePassthrough(QemuSystemTest): - # https://github.com/pbo-linaro/qemu-linux-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/device_passthrough + # $ ./build.sh && ./archive_artifacts.sh out.tar.xz # # Linux kernel is compiled with defconfig + # IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde ASSET_DEVICE_PASSTHROUGH_STACK = Asset( - ('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/' - 'download/device_passthrough.tar.xz'), - '812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/device_passthrough-c3fb84a.tar.xz'), + '15ac2b02bed0c0ea8e3e007de0bcfdaf6fd51c1ba98213f841dc7d01d6f72f04') # This tests the device passthrough implementation, by booting a VM # supporting it with two nvme disks attached, and launching a nested VM @@ -96,16 +97,16 @@ class Aarch64DevicePassthrough(QemuSystemTest): self.vm.set_console() - stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() + self.archive_extract(stack_path_tar, format="tar") stack = self.scratch_file('out') - kernel = os.path.join(stack, 'Image.gz') - rootfs_host = os.path.join(stack, 'host.ext4') - disk_vfio = os.path.join(stack, 'disk_vfio') - disk_iommufd = os.path.join(stack, 'disk_iommufd') - guest_cmd = os.path.join(stack, 'guest.sh') - nested_guest_cmd = os.path.join(stack, 'nested_guest.sh') + kernel = join(stack, 'Image.gz') + rootfs_host = join(stack, 'host.ext4') + disk_vfio = join(stack, 'disk_vfio') + disk_iommufd = join(stack, 'disk_iommufd') + guest_cmd = join(stack, 'guest.sh') + nested_guest_cmd = join(stack, 'nested_guest.sh') # we generate two random disks with open(disk_vfio, "wb") as d: d.write(randbytes(512)) with open(disk_iommufd, "wb") as d: d.write(randbytes(1024)) diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/aarch64/test_hotplug_pci.py index 0c67991b89..0c67991b89 100755 --- a/tests/functional/test_aarch64_hotplug_pci.py +++ b/tests/functional/aarch64/test_hotplug_pci.py diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/aarch64/test_imx8mp_evk.py index 99ddcdef83..99ddcdef83 100755 --- a/tests/functional/test_aarch64_imx8mp_evk.py +++ b/tests/functional/aarch64/test_imx8mp_evk.py diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/aarch64/test_kvm.py index 9fb9286139..9fb9286139 100755 --- a/tests/functional/test_aarch64_kvm.py +++ b/tests/functional/aarch64/test_kvm.py diff --git a/tests/functional/aarch64/test_migration.py b/tests/functional/aarch64/test_migration.py new file mode 100755 index 0000000000..70267e756d --- /dev/null +++ b/tests/functional/aarch64/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# aarch64 migration test + +from migration import MigrationTest + + +class Aarch64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('quanta-gsj') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('quanta-gsj') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('quanta-gsj') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/aarch64/test_multiprocess.py b/tests/functional/aarch64/test_multiprocess.py new file mode 100755 index 0000000000..1c6e45ecb6 --- /dev/null +++ b/tests/functional/aarch64/test_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on aarch64 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class Aarch64Multiprocess(Multiprocess): + + ASSET_KERNEL_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), + '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') + + def test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'rdinit=/bin/bash console=ttyAMA0') + self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, + kernel_command_line, 'virt,gic-version=3') + + +if __name__ == '__main__': + Multiprocess.main() diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/aarch64/test_raspi3.py index 74f6630ed2..74f6630ed2 100755 --- a/tests/functional/test_aarch64_raspi3.py +++ b/tests/functional/aarch64/test_raspi3.py diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/aarch64/test_raspi4.py index 7a4302b0c5..7a4302b0c5 100755 --- a/tests/functional/test_aarch64_raspi4.py +++ b/tests/functional/aarch64/test_raspi4.py diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/aarch64/test_replay.py index db12e76603..db12e76603 100755 --- a/tests/functional/test_aarch64_replay.py +++ b/tests/functional/aarch64/test_replay.py diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/aarch64/test_reverse_debug.py index 58d4532835..8bc91ccfde 100755 --- a/tests/functional/test_aarch64_reverse_debug.py +++ b/tests/functional/aarch64/test_reverse_debug.py @@ -21,7 +21,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): REG_PC = 32 - KERNEL_ASSET = Asset( + ASSET_KERNEL = Asset( ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') @@ -30,7 +30,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' - kernel_path = self.KERNEL_ASSET.fetch() + kernel_path = self.ASSET_KERNEL.fetch() self.reverse_debugging(args=('-kernel', kernel_path)) diff --git a/tests/functional/aarch64/test_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py new file mode 100755 index 0000000000..ca892e0a8c --- /dev/null +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on sbsa-ref machine and a +# nested guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +from os.path import join +import shutil + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +class Aarch64RMESbsaRefMachine(QemuSystemTest): + + # Stack is inspired from: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_sbsa_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz + ASSET_RME_STACK_SBSA = Asset( + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_sbsa_release-a7f02cf.tar.xz'), + '27d8400b11befb828d6db0cab97e7ae102d0992c928d3dfbf38b24b6cf6c324c') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_sbsaref(self): + self.set_machine('sbsa-ref') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar, format="tar") + + rme_stack = self.scratch_file('.') + pflash0 = join(rme_stack, 'out', 'SBSA_FLASH0.fd') + pflash1 = join(rme_stack, 'out', 'SBSA_FLASH1.fd') + rootfs = join(rme_stack, 'out', 'host.ext4') + + efi = join(rme_stack, 'out', 'EFI') + os.makedirs(efi, exist_ok=True) + shutil.copyfile(join(rme_stack, 'out', 'Image'), join(efi, 'Image')) + with open(join(efi, 'startup.nsh'), 'w') as startup: + startup.write('fs0:Image nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') + + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'sbsa-ref') + self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') + self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') + self.vm.add_args('-drive', f'file=fat:rw:{efi},format=raw') + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') + self.vm.launch() + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/aarch64/test_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py new file mode 100755 index 0000000000..bb603aaa26 --- /dev/null +++ b/tests/functional/aarch64/test_rme_virt.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on virt machine and a nested +# guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from os.path import join + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +class Aarch64RMEVirtMachine(QemuSystemTest): + + # Stack is inspired from: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz + ASSET_RME_STACK_VIRT = Asset( + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_release-86101e5.tar.xz'), + 'e42fef8439badb52a071ac446fc33cff4cb7d61314c7a28fdbe61a11e1faad3a') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_virt(self): + self.set_machine('virt') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar, format="tar") + + rme_stack = self.scratch_file('.') + kernel = join(rme_stack, 'out', 'Image') + bios = join(rme_stack, 'out', 'flash.bin') + rootfs = join(rme_stack, 'out', 'host.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,acpi=off,' + 'virtualization=on,' + 'secure=on,' + 'gic-version=3') + self.vm.add_args('-bios', bios) + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') + # We need to add nokaslr to avoid triggering this sporadic bug: + # https://gitlab.com/qemu-project/qemu/-/issues/2823 + self.vm.add_args('-append', + 'nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') + + self.vm.launch() + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/aarch64/test_sbsaref.py index d3402f5080..d3402f5080 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/aarch64/test_sbsaref.py diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/aarch64/test_sbsaref_alpine.py index 8776999383..abb8f5114b 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/aarch64/test_sbsaref_alpine.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefAlpine(QemuSystemTest): diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/aarch64/test_sbsaref_freebsd.py index 7ef016fba6..3b942f7795 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/aarch64/test_sbsaref_freebsd.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefFreeBSD(QemuSystemTest): diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/aarch64/test_smmu.py index e0f4a92217..e0f4a92217 100755 --- a/tests/functional/test_aarch64_smmu.py +++ b/tests/functional/aarch64/test_smmu.py diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/aarch64/test_tcg_plugins.py index cb7e9298fb..cb7e9298fb 100755 --- a/tests/functional/test_aarch64_tcg_plugins.py +++ b/tests/functional/aarch64/test_tcg_plugins.py diff --git a/tests/functional/test_aarch64_tuxrun.py b/tests/functional/aarch64/test_tuxrun.py index 75adc8acb8..75adc8acb8 100755 --- a/tests/functional/test_aarch64_tuxrun.py +++ b/tests/functional/aarch64/test_tuxrun.py diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/aarch64/test_virt.py index 4d0ad90ff8..4d0ad90ff8 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/aarch64/test_virt.py diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/aarch64/test_virt_gpu.py index 3844727857..4e50887c3e 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/aarch64/test_virt_gpu.py @@ -76,6 +76,8 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): self.skipTest("egl-headless support is not available") elif "'type' does not accept value 'dbus'" in excp.output: self.skipTest("dbus display support is not available") + elif "eglInitialize failed: EGL_NOT_INITIALIZED" in excp.output: + self.skipTest("EGL failed to initialize on this host") else: self.log.info("unhandled launch failure: %s", excp.output) raise excp diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/aarch64/test_xen.py index 261d796540..261d796540 100755 --- a/tests/functional/test_aarch64_xen.py +++ b/tests/functional/aarch64/test_xen.py diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/aarch64/test_xlnx_versal.py index 4b9c49e5d6..4b9c49e5d6 100755 --- a/tests/functional/test_aarch64_xlnx_versal.py +++ b/tests/functional/aarch64/test_xlnx_versal.py diff --git a/tests/functional/alpha/meson.build b/tests/functional/alpha/meson.build new file mode 100644 index 0000000000..26a5b3f2e4 --- /dev/null +++ b/tests/functional/alpha/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_alpha_system_quick = [ + 'migration', +] + +tests_alpha_system_thorough = [ + 'clipper', + 'replay', +] diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/alpha/test_clipper.py index c5d7181953..c5d7181953 100755 --- a/tests/functional/test_alpha_clipper.py +++ b/tests/functional/alpha/test_clipper.py diff --git a/tests/functional/alpha/test_migration.py b/tests/functional/alpha/test_migration.py new file mode 100755 index 0000000000..f11b523ec9 --- /dev/null +++ b/tests/functional/alpha/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Alpha migration test + +from migration import MigrationTest + + +class AlphaMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('clipper') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('clipper') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('clipper') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/alpha/test_replay.py index 24a17ef590..24a17ef590 100755 --- a/tests/functional/test_alpha_replay.py +++ b/tests/functional/alpha/test_replay.py diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build new file mode 100644 index 0000000000..e4e7dba8d0 --- /dev/null +++ b/tests/functional/arm/meson.build @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_arm_timeouts = { + 'aspeed_palmetto' : 120, + 'aspeed_romulus' : 120, + 'aspeed_witherspoon' : 120, + 'aspeed_ast2500' : 720, + 'aspeed_ast2600' : 1200, + 'aspeed_bletchley' : 480, + 'aspeed_catalina' : 480, + 'aspeed_gb200nvl_bmc' : 480, + 'aspeed_rainier' : 480, + 'bpim2u' : 500, + 'collie' : 180, + 'cubieboard' : 360, + 'orangepi' : 540, + 'quanta_gsj' : 240, + 'raspi2' : 120, + 'replay' : 240, + 'tuxrun' : 240, + 'sx1' : 360, +} + +tests_arm_system_quick = [ + 'migration', +] + +tests_arm_system_thorough = [ + 'aspeed_ast1030', + 'aspeed_palmetto', + 'aspeed_romulus', + 'aspeed_witherspoon', + 'aspeed_ast2500', + 'aspeed_ast2600', + 'aspeed_bletchley', + 'aspeed_catalina', + 'aspeed_gb200nvl_bmc', + 'aspeed_rainier', + 'bpim2u', + 'canona1100', + 'collie', + 'cubieboard', + 'emcraft_sf2', + 'integratorcp', + 'max78000fthr', + 'microbit', + 'orangepi', + 'quanta_gsj', + 'raspi2', + 'realview', + 'replay', + 'smdkc210', + 'stellaris', + 'sx1', + 'vexpress', + 'virt', + 'tuxrun', +] + +tests_arm_linuxuser_thorough = [ + 'bflt', +] diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index 77037f0179..77037f0179 100755 --- a/tests/functional/test_arm_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py index 6923fe8701..6923fe8701 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/arm/test_aspeed_ast2500.py diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index fdae4c939d..fdae4c939d 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/arm/test_aspeed_bletchley.py index 5a60b24b3d..5a60b24b3d 100644..100755 --- a/tests/functional/test_arm_aspeed_bletchley.py +++ b/tests/functional/arm/test_aspeed_bletchley.py diff --git a/tests/functional/test_arm_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py index dc2f24e7b4..dc2f24e7b4 100755 --- a/tests/functional/test_arm_aspeed_catalina.py +++ b/tests/functional/arm/test_aspeed_catalina.py diff --git a/tests/functional/test_arm_aspeed_gb200nvl_bmc.py b/tests/functional/arm/test_aspeed_gb200nvl_bmc.py index 8e8e3f05c1..8e8e3f05c1 100644..100755 --- a/tests/functional/test_arm_aspeed_gb200nvl_bmc.py +++ b/tests/functional/arm/test_aspeed_gb200nvl_bmc.py diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/arm/test_aspeed_palmetto.py index ff0b821be6..ff0b821be6 100755 --- a/tests/functional/test_arm_aspeed_palmetto.py +++ b/tests/functional/arm/test_aspeed_palmetto.py diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/arm/test_aspeed_rainier.py index 602d6194ac..602d6194ac 100755 --- a/tests/functional/test_arm_aspeed_rainier.py +++ b/tests/functional/arm/test_aspeed_rainier.py diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/arm/test_aspeed_romulus.py index 0447212bbf..0447212bbf 100755 --- a/tests/functional/test_arm_aspeed_romulus.py +++ b/tests/functional/arm/test_aspeed_romulus.py diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/arm/test_aspeed_witherspoon.py index 51a2d47af2..51a2d47af2 100644..100755 --- a/tests/functional/test_arm_aspeed_witherspoon.py +++ b/tests/functional/arm/test_aspeed_witherspoon.py diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/arm/test_bflt.py index f273fc8354..f273fc8354 100755 --- a/tests/functional/test_arm_bflt.py +++ b/tests/functional/arm/test_bflt.py diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/arm/test_bpim2u.py index 8bed64b702..8bed64b702 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/arm/test_bpim2u.py diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/arm/test_canona1100.py index 21a1a596a0..21a1a596a0 100755 --- a/tests/functional/test_arm_canona1100.py +++ b/tests/functional/arm/test_canona1100.py diff --git a/tests/functional/test_arm_collie.py b/tests/functional/arm/test_collie.py index fe1be3d079..fe1be3d079 100755 --- a/tests/functional/test_arm_collie.py +++ b/tests/functional/arm/test_collie.py diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/arm/test_cubieboard.py index b536c2f77a..b536c2f77a 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/arm/test_cubieboard.py diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/arm/test_emcraft_sf2.py index f9f3f069e2..f9f3f069e2 100755 --- a/tests/functional/test_arm_emcraft_sf2.py +++ b/tests/functional/arm/test_emcraft_sf2.py diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/arm/test_integratorcp.py index 4f00924aa0..4f00924aa0 100755 --- a/tests/functional/test_arm_integratorcp.py +++ b/tests/functional/arm/test_integratorcp.py diff --git a/tests/functional/test_arm_max78000fthr.py b/tests/functional/arm/test_max78000fthr.py index a82980b0f7..a82980b0f7 100755 --- a/tests/functional/test_arm_max78000fthr.py +++ b/tests/functional/arm/test_max78000fthr.py diff --git a/tests/functional/test_arm_microbit.py b/tests/functional/arm/test_microbit.py index 68ea4e73d6..68ea4e73d6 100755 --- a/tests/functional/test_arm_microbit.py +++ b/tests/functional/arm/test_microbit.py diff --git a/tests/functional/arm/test_migration.py b/tests/functional/arm/test_migration.py new file mode 100755 index 0000000000..0aa89f4f61 --- /dev/null +++ b/tests/functional/arm/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# arm migration test + +from migration import MigrationTest + + +class ArmMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('npcm750-evb') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('npcm750-evb') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('npcm750-evb') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/arm/test_orangepi.py index f9bfa8c78d..f9bfa8c78d 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/arm/test_orangepi.py diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/arm/test_quanta_gsj.py index cb0545f7bf..cb0545f7bf 100755 --- a/tests/functional/test_arm_quanta_gsj.py +++ b/tests/functional/arm/test_quanta_gsj.py diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/arm/test_raspi2.py index d3c7aaa39b..d3c7aaa39b 100755 --- a/tests/functional/test_arm_raspi2.py +++ b/tests/functional/arm/test_raspi2.py diff --git a/tests/functional/test_arm_realview.py b/tests/functional/arm/test_realview.py index 82cc964333..82cc964333 100755 --- a/tests/functional/test_arm_realview.py +++ b/tests/functional/arm/test_realview.py diff --git a/tests/functional/test_arm_replay.py b/tests/functional/arm/test_replay.py index e002e6a264..e002e6a264 100755 --- a/tests/functional/test_arm_replay.py +++ b/tests/functional/arm/test_replay.py diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/arm/test_smdkc210.py index 3154e7f732..3154e7f732 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/arm/test_smdkc210.py diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/arm/test_stellaris.py index cbd21cb1a0..cbd21cb1a0 100755 --- a/tests/functional/test_arm_stellaris.py +++ b/tests/functional/arm/test_stellaris.py diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/arm/test_sx1.py index 25800b388c..25800b388c 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/arm/test_sx1.py diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/arm/test_tuxrun.py index 4ac85f48ac..4ac85f48ac 100755 --- a/tests/functional/test_arm_tuxrun.py +++ b/tests/functional/arm/test_tuxrun.py diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/arm/test_vexpress.py index 6b11552894..6b11552894 100755 --- a/tests/functional/test_arm_vexpress.py +++ b/tests/functional/arm/test_vexpress.py diff --git a/tests/functional/test_arm_virt.py b/tests/functional/arm/test_virt.py index 7b6549176f..7b6549176f 100755 --- a/tests/functional/test_arm_virt.py +++ b/tests/functional/arm/test_virt.py diff --git a/tests/functional/avr/meson.build b/tests/functional/avr/meson.build new file mode 100644 index 0000000000..7a2cb7099e --- /dev/null +++ b/tests/functional/avr/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_avr_system_thorough = [ + 'mega2560', + 'uno', +] diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/avr/test_mega2560.py index 6359b72af3..6359b72af3 100755 --- a/tests/functional/test_avr_mega2560.py +++ b/tests/functional/avr/test_mega2560.py diff --git a/tests/functional/test_avr_uno.py b/tests/functional/avr/test_uno.py index adb3b73da4..adb3b73da4 100755 --- a/tests/functional/test_avr_uno.py +++ b/tests/functional/avr/test_uno.py diff --git a/tests/functional/generic/meson.build b/tests/functional/generic/meson.build new file mode 100644 index 0000000000..013cc96fbf --- /dev/null +++ b/tests/functional/generic/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', + 'vnc', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] diff --git a/tests/functional/test_empty_cpu_model.py b/tests/functional/generic/test_empty_cpu_model.py index 0081b06d85..0081b06d85 100755 --- a/tests/functional/test_empty_cpu_model.py +++ b/tests/functional/generic/test_empty_cpu_model.py diff --git a/tests/functional/test_info_usernet.py b/tests/functional/generic/test_info_usernet.py index e8cbc37eed..e8cbc37eed 100755 --- a/tests/functional/test_info_usernet.py +++ b/tests/functional/generic/test_info_usernet.py diff --git a/tests/functional/test_version.py b/tests/functional/generic/test_version.py index 3ab3b67f7e..3ab3b67f7e 100755 --- a/tests/functional/test_version.py +++ b/tests/functional/generic/test_version.py diff --git a/tests/functional/test_vnc.py b/tests/functional/generic/test_vnc.py index f1dd1597cf..f1dd1597cf 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/generic/test_vnc.py diff --git a/tests/functional/hppa/meson.build b/tests/functional/hppa/meson.build new file mode 100644 index 0000000000..a334837088 --- /dev/null +++ b/tests/functional/hppa/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_hppa_system_quick = [ + 'seabios', +] diff --git a/tests/functional/test_hppa_seabios.py b/tests/functional/hppa/test_seabios.py index 661b2464e1..661b2464e1 100755 --- a/tests/functional/test_hppa_seabios.py +++ b/tests/functional/hppa/test_seabios.py diff --git a/tests/functional/i386/meson.build b/tests/functional/i386/meson.build new file mode 100644 index 0000000000..23d8c216be --- /dev/null +++ b/tests/functional/i386/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_i386_system_quick = [ + 'migration', +] + +tests_i386_system_thorough = [ + 'replay', + 'tuxrun', +] diff --git a/tests/functional/i386/test_migration.py b/tests/functional/i386/test_migration.py new file mode 100755 index 0000000000..a57f316404 --- /dev/null +++ b/tests/functional/i386/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# i386 migration test + +from migration import MigrationTest + + +class I386MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('isapc') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('isapc') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('isapc') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_i386_replay.py b/tests/functional/i386/test_replay.py index 7c4c2602da..7c4c2602da 100755 --- a/tests/functional/test_i386_replay.py +++ b/tests/functional/i386/test_replay.py diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/i386/test_tuxrun.py index f3ccf11ae8..f3ccf11ae8 100755 --- a/tests/functional/test_i386_tuxrun.py +++ b/tests/functional/i386/test_tuxrun.py diff --git a/tests/functional/loongarch64/meson.build b/tests/functional/loongarch64/meson.build new file mode 100644 index 0000000000..d1687176a3 --- /dev/null +++ b/tests/functional/loongarch64/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_loongarch64_system_thorough = [ + 'virt', +] diff --git a/tests/functional/test_loongarch64_virt.py b/tests/functional/loongarch64/test_virt.py index b7d9abf933..b7d9abf933 100755 --- a/tests/functional/test_loongarch64_virt.py +++ b/tests/functional/loongarch64/test_virt.py diff --git a/tests/functional/m68k/meson.build b/tests/functional/m68k/meson.build new file mode 100644 index 0000000000..e29044a6d7 --- /dev/null +++ b/tests/functional/m68k/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_m68k_system_thorough = [ + 'mcf5208evb', + 'nextcube', + 'replay', + 'q800', + 'tuxrun', +] diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/m68k/test_mcf5208evb.py index c7d1998933..c7d1998933 100755 --- a/tests/functional/test_m68k_mcf5208evb.py +++ b/tests/functional/m68k/test_mcf5208evb.py diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/m68k/test_nextcube.py index 13c72bd136..13c72bd136 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/m68k/test_nextcube.py diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/m68k/test_q800.py index b3e655346c..b3e655346c 100755 --- a/tests/functional/test_m68k_q800.py +++ b/tests/functional/m68k/test_q800.py diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/m68k/test_replay.py index 213d6ae07e..213d6ae07e 100755 --- a/tests/functional/test_m68k_replay.py +++ b/tests/functional/m68k/test_replay.py diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/m68k/test_tuxrun.py index 7eacba135f..7eacba135f 100755 --- a/tests/functional/test_m68k_tuxrun.py +++ b/tests/functional/m68k/test_tuxrun.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 311c6f1806..2a0c5aa141 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -9,345 +9,34 @@ if get_option('tcg_interpreter') subdir_done() endif -# Timeouts for individual tests that can be slow e.g. with debugging enabled -test_timeouts = { - 'aarch64_aspeed_ast2700' : 600, - 'aarch64_aspeed_ast2700fc' : 600, - 'aarch64_device_passthrough' : 720, - 'aarch64_imx8mp_evk' : 240, - 'aarch64_raspi4' : 480, - 'aarch64_reverse_debug' : 180, - 'aarch64_rme_virt' : 1200, - 'aarch64_rme_sbsaref' : 1200, - 'aarch64_sbsaref_alpine' : 1200, - 'aarch64_sbsaref_freebsd' : 720, - 'aarch64_smmu' : 720, - 'aarch64_tuxrun' : 240, - 'aarch64_virt' : 360, - 'aarch64_virt_gpu' : 480, - 'acpi_bits' : 420, - 'arm_aspeed_palmetto' : 120, - 'arm_aspeed_romulus' : 120, - 'arm_aspeed_witherspoon' : 120, - 'arm_aspeed_ast2500' : 720, - 'arm_aspeed_ast2600' : 1200, - 'arm_aspeed_bletchley' : 480, - 'arm_aspeed_catalina' : 480, - 'arm_aspeed_gb200nvl_bmc' : 480, - 'arm_aspeed_rainier' : 480, - 'arm_bpim2u' : 500, - 'arm_collie' : 180, - 'arm_cubieboard' : 360, - 'arm_orangepi' : 540, - 'arm_quanta_gsj' : 240, - 'arm_raspi2' : 120, - 'arm_replay' : 240, - 'arm_tuxrun' : 240, - 'arm_sx1' : 360, - 'intel_iommu': 300, - 'mips_malta' : 480, - 'mipsel_malta' : 420, - 'mipsel_replay' : 480, - 'mips64_malta' : 240, - 'mips64el_malta' : 420, - 'mips64el_replay' : 180, - 'netdev_ethtool' : 180, - 'ppc_40p' : 240, - 'ppc64_hv' : 1000, - 'ppc64_powernv' : 480, - 'ppc64_pseries' : 480, - 'ppc64_replay' : 210, - 'ppc64_tuxrun' : 420, - 'ppc64_mac99' : 120, - 'riscv64_tuxrun' : 120, - 's390x_ccw_virtio' : 420, - 'sh4_tuxrun' : 240, - 'virtio_balloon': 120, - 'x86_64_kvm_xen' : 180, - 'x86_64_replay' : 480, -} - -tests_generic_system = [ - 'empty_cpu_model', - 'info_usernet', - 'version', -] - -tests_generic_linuxuser = [ -] - -tests_generic_bsduser = [ -] - -tests_aarch64_system_quick = [ - 'migration', -] - -tests_aarch64_system_thorough = [ - 'aarch64_aspeed_ast2700', - 'aarch64_aspeed_ast2700fc', - 'aarch64_device_passthrough', - 'aarch64_hotplug_pci', - 'aarch64_imx8mp_evk', - 'aarch64_kvm', - 'aarch64_raspi3', - 'aarch64_raspi4', - 'aarch64_replay', - 'aarch64_reverse_debug', - 'aarch64_rme_virt', - 'aarch64_rme_sbsaref', - 'aarch64_sbsaref', - 'aarch64_sbsaref_alpine', - 'aarch64_sbsaref_freebsd', - 'aarch64_smmu', - 'aarch64_tcg_plugins', - 'aarch64_tuxrun', - 'aarch64_virt', - 'aarch64_virt_gpu', - 'aarch64_xen', - 'aarch64_xlnx_versal', - 'multiprocess', -] - -tests_alpha_system_quick = [ - 'migration', -] - -tests_alpha_system_thorough = [ - 'alpha_clipper', - 'alpha_replay', -] - -tests_arm_system_quick = [ - 'migration', -] - -tests_arm_system_thorough = [ - 'arm_aspeed_ast1030', - 'arm_aspeed_palmetto', - 'arm_aspeed_romulus', - 'arm_aspeed_witherspoon', - 'arm_aspeed_ast2500', - 'arm_aspeed_ast2600', - 'arm_aspeed_bletchley', - 'arm_aspeed_catalina', - 'arm_aspeed_gb200nvl_bmc', - 'arm_aspeed_rainier', - 'arm_bpim2u', - 'arm_canona1100', - 'arm_collie', - 'arm_cubieboard', - 'arm_emcraft_sf2', - 'arm_integratorcp', - 'arm_max78000fthr', - 'arm_microbit', - 'arm_orangepi', - 'arm_quanta_gsj', - 'arm_raspi2', - 'arm_realview', - 'arm_replay', - 'arm_smdkc210', - 'arm_stellaris', - 'arm_sx1', - 'arm_vexpress', - 'arm_virt', - 'arm_tuxrun', -] - -tests_arm_linuxuser_thorough = [ - 'arm_bflt', -] - -tests_avr_system_thorough = [ - 'avr_mega2560', - 'avr_uno', -] - -tests_hppa_system_quick = [ - 'hppa_seabios', -] - -tests_i386_system_quick = [ - 'migration', -] - -tests_i386_system_thorough = [ - 'i386_replay', - 'i386_tuxrun', -] - -tests_loongarch64_system_thorough = [ - 'loongarch64_virt', -] - -tests_m68k_system_thorough = [ - 'm68k_mcf5208evb', - 'm68k_nextcube', - 'm68k_replay', - 'm68k_q800', - 'm68k_tuxrun', -] - -tests_microblaze_system_thorough = [ - 'microblaze_replay', - 'microblaze_s3adsp1800' -] - -tests_microblazeel_system_thorough = [ - 'microblazeel_s3adsp1800' -] - -tests_mips_system_thorough = [ - 'mips_malta', - 'mips_replay', - 'mips_tuxrun', -] - -tests_mipsel_system_thorough = [ - 'mipsel_malta', - 'mipsel_replay', - 'mipsel_tuxrun', -] - -tests_mips64_system_thorough = [ - 'mips64_malta', - 'mips64_tuxrun', -] - -tests_mips64el_system_thorough = [ - 'mips64el_fuloong2e', - 'mips64el_loongson3v', - 'mips64el_malta', - 'mips64el_replay', - 'mips64el_tuxrun', -] - -tests_or1k_system_thorough = [ - 'or1k_replay', - 'or1k_sim', -] - -tests_ppc_system_quick = [ - 'migration', - 'ppc_74xx', -] - -tests_ppc_system_thorough = [ - 'ppc_40p', - 'ppc_amiga', - 'ppc_bamboo', - 'ppc_mac', - 'ppc_mpc8544ds', - 'ppc_replay', - 'ppc_sam460ex', - 'ppc_tuxrun', - 'ppc_virtex_ml507', -] - -tests_ppc64_system_quick = [ - 'migration', -] - -tests_ppc64_system_thorough = [ - 'ppc64_e500', - 'ppc64_hv', - 'ppc64_powernv', - 'ppc64_pseries', - 'ppc64_replay', - 'ppc64_reverse_debug', - 'ppc64_tuxrun', - 'ppc64_mac99', -] - -tests_riscv32_system_quick = [ - 'migration', - 'riscv_opensbi', -] - -tests_riscv32_system_thorough = [ - 'riscv32_tuxrun', -] - -tests_riscv64_system_quick = [ - 'migration', - 'riscv_opensbi', -] - -tests_riscv64_system_thorough = [ - 'riscv64_sifive_u', - 'riscv64_tuxrun', -] - -tests_rx_system_thorough = [ - 'rx_gdbsim', -] - -tests_s390x_system_thorough = [ - 's390x_ccw_virtio', - 's390x_pxelinux', - 's390x_replay', - 's390x_topology', - 's390x_tuxrun', -] - -tests_sh4_system_thorough = [ - 'sh4_r2d', - 'sh4_tuxrun', -] - -tests_sh4eb_system_thorough = [ - 'sh4eb_r2d', -] - -tests_sparc_system_quick = [ - 'migration', -] - -tests_sparc_system_thorough = [ - 'sparc_replay', - 'sparc_sun4m', -] - -tests_sparc64_system_quick = [ - 'migration', -] - -tests_sparc64_system_thorough = [ - 'sparc64_sun4u', - 'sparc64_tuxrun', -] - -tests_x86_64_system_quick = [ - 'cpu_queries', - 'mem_addr_space', - 'migration', - 'pc_cpu_hotplug_props', - 'virtio_version', - 'x86_cpu_model_versions', - 'vnc', - 'memlock', -] - -tests_x86_64_system_thorough = [ - 'acpi_bits', - 'intel_iommu', - 'linux_initrd', - 'multiprocess', - 'netdev_ethtool', - 'virtio_balloon', - 'virtio_gpu', - 'x86_64_hotplug_blk', - 'x86_64_hotplug_cpu', - 'x86_64_kvm_xen', - 'x86_64_replay', - 'x86_64_reverse_debug', - 'x86_64_tuxrun', -] - -tests_xtensa_system_thorough = [ - 'xtensa_lx60', - 'xtensa_replay', -] +subdir('aarch64') +subdir('alpha') +subdir('arm') +subdir('avr') +subdir('hppa') +subdir('i386') +subdir('loongarch64') +subdir('m68k') +subdir('microblaze') +subdir('microblazeel') +subdir('mips') +subdir('mipsel') +subdir('mips64') +subdir('mips64el') +subdir('or1k') +subdir('ppc') +subdir('ppc64') +subdir('riscv32') +subdir('riscv64') +subdir('rx') +subdir('s390x') +subdir('sh4') +subdir('sh4eb') +subdir('sparc') +subdir('sparc64') +subdir('x86_64') +subdir('xtensa') +subdir('generic') precache_all = [] foreach speed : ['quick', 'thorough'] @@ -390,7 +79,11 @@ foreach speed : ['quick', 'thorough'] foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) - testfile = 'test_' + test + '.py' + if fs.exists('generic' / 'test_' + test + '.py') + testfile = 'generic' / 'test_' + test + '.py' + else + testfile = target_base / 'test_' + test + '.py' + endif testpath = meson.current_source_dir() / testfile teststamp = testname + '.tstamp' test_precache_env = environment() @@ -404,6 +97,11 @@ foreach speed : ['quick', 'thorough'] build_by_default: false, env: test_precache_env) precache_all += precache + if is_variable('test_' + target_base + '_timeouts') + time_out = get_variable('test_' + target_base + '_timeouts').get(test, 90) + else + time_out = 90 + endif # Ideally we would add 'precache' to 'depends' here, such that # 'build_by_default: false' lets the pre-caching automatically @@ -419,8 +117,8 @@ foreach speed : ['quick', 'thorough'] env: test_env, args: [testpath], protocol: 'tap', - timeout: test_timeouts.get(test, 90), - priority: test_timeouts.get(test, 90), + timeout: time_out, + priority: time_out, suite: suites) endforeach endforeach diff --git a/tests/functional/microblaze/meson.build b/tests/functional/microblaze/meson.build new file mode 100644 index 0000000000..8069ca9be6 --- /dev/null +++ b/tests/functional/microblaze/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblaze_system_thorough = [ + 'replay', + 's3adsp1800' +] diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/microblaze/test_replay.py index 7484c4186f..7484c4186f 100755 --- a/tests/functional/test_microblaze_replay.py +++ b/tests/functional/microblaze/test_replay.py diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/microblaze/test_s3adsp1800.py index f093b162c0..f093b162c0 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/microblaze/test_s3adsp1800.py diff --git a/tests/functional/microblazeel/meson.build b/tests/functional/microblazeel/meson.build new file mode 100644 index 0000000000..27619dc5a9 --- /dev/null +++ b/tests/functional/microblazeel/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblazeel_system_thorough = [ + 's3adsp1800' +] diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/microblazeel/test_s3adsp1800.py index 915902d48b..75ce8856ed 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/microblazeel/test_s3adsp1800.py @@ -7,7 +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 test_microblaze_s3adsp1800 import MicroblazeMachine +from microblaze.test_s3adsp1800 import MicroblazeMachine class MicroblazeLittleEndianMachine(MicroblazeMachine): diff --git a/tests/functional/test_migration.py b/tests/functional/migration.py index c4393c3543..0739554483 100755..100644 --- a/tests/functional/test_migration.py +++ b/tests/functional/migration.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # -# Migration test +# Migration test base class # # Copyright (c) 2019 Red Hat, Inc. # @@ -14,7 +14,7 @@ import tempfile import time -from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test import QemuSystemTest, which from qemu_test.ports import Ports @@ -41,24 +41,7 @@ class MigrationTest(QemuSystemTest): self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') - def select_machine(self): - target_machine = { - 'aarch64': 'quanta-gsj', - 'alpha': 'clipper', - 'arm': 'npcm750-evb', - 'i386': 'isapc', - 'ppc': 'sam460ex', - 'ppc64': 'mac99', - 'riscv32': 'spike', - 'riscv64': 'virt', - 'sparc': 'SS-4', - 'sparc64': 'sun4u', - 'x86_64': 'microvm', - } - self.set_machine(target_machine[self.arch]) - def do_migrate(self, dest_uri, src_uri=None): - self.select_machine() dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu") dest_vm.add_args('-nodefaults') dest_vm.launch() @@ -76,23 +59,21 @@ class MigrationTest(QemuSystemTest): self.skipTest('Failed to find a free port') return port - def test_migration_with_tcp_localhost(self): + def migration_with_tcp_localhost(self): with Ports() as ports: dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports) self.do_migrate(dest_uri) - def test_migration_with_unix(self): + def migration_with_unix(self): with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: dest_uri = 'unix:%s/qemu-test.sock' % socket_path self.do_migrate(dest_uri) - @skipIfMissingCommands('ncat') - def test_migration_with_exec(self): + def migration_with_exec(self): + if not which('ncat'): + self.skipTest('ncat is not available') with Ports() as ports: free_port = self._get_free_port(ports) dest_uri = 'exec:ncat -l localhost %u' % free_port src_uri = 'exec:ncat localhost %u' % free_port self.do_migrate(dest_uri, src_uri) - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/mips/meson.build b/tests/functional/mips/meson.build new file mode 100644 index 0000000000..49aaf53b02 --- /dev/null +++ b/tests/functional/mips/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips_timeouts = { + 'malta' : 480, +} + +tests_mips_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips_malta.py b/tests/functional/mips/test_malta.py index 30279f0ff2..30279f0ff2 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/mips/test_malta.py diff --git a/tests/functional/test_mips_replay.py b/tests/functional/mips/test_replay.py index 4327481e35..4327481e35 100755 --- a/tests/functional/test_mips_replay.py +++ b/tests/functional/mips/test_replay.py diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/mips/test_tuxrun.py index 6771dbd57e..6771dbd57e 100755 --- a/tests/functional/test_mips_tuxrun.py +++ b/tests/functional/mips/test_tuxrun.py diff --git a/tests/functional/mips64/meson.build b/tests/functional/mips64/meson.build new file mode 100644 index 0000000000..3ff2118987 --- /dev/null +++ b/tests/functional/mips64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64_timeouts = { + 'malta' : 240, +} + +tests_mips64_system_thorough = [ + 'malta', + 'tuxrun', +] diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/mips64/test_malta.py index 53c3e0c122..a553d3c5bc 100755 --- a/tests/functional/test_mips64_malta.py +++ b/tests/functional/mips64/test_malta.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/mips64/test_tuxrun.py index 0e4c65961d..0e4c65961d 100755 --- a/tests/functional/test_mips64_tuxrun.py +++ b/tests/functional/mips64/test_tuxrun.py diff --git a/tests/functional/mips64el/meson.build b/tests/functional/mips64el/meson.build new file mode 100644 index 0000000000..69ec50174c --- /dev/null +++ b/tests/functional/mips64el/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64el_timeouts = { + 'malta' : 420, + 'replay' : 180, +} + +tests_mips64el_system_thorough = [ + 'fuloong2e', + 'loongson3v', + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/mips64el/test_fuloong2e.py index 35e500b022..35e500b022 100755 --- a/tests/functional/test_mips64el_fuloong2e.py +++ b/tests/functional/mips64el/test_fuloong2e.py diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/mips64el/test_loongson3v.py index f85371e50c..f85371e50c 100755 --- a/tests/functional/test_mips64el_loongson3v.py +++ b/tests/functional/mips64el/test_loongson3v.py diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/mips64el/test_malta.py index 3cc79b74c1..8fdc49b300 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/mips64el/test_malta.py @@ -16,7 +16,7 @@ from qemu_test import LinuxKernelTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): @@ -191,7 +191,7 @@ class MaltaMachineFramebuffer(LinuxKernelTest): self.do_test_i6400_framebuffer_logo(8) -from test_mipsel_malta import MaltaMachineYAMON +from mipsel.test_malta import MaltaMachineYAMON if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/mips64el/test_replay.py index 26a6ccff3f..26a6ccff3f 100755 --- a/tests/functional/test_mips64el_replay.py +++ b/tests/functional/mips64el/test_replay.py diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/mips64el/test_tuxrun.py index 0a24757c51..0a24757c51 100755 --- a/tests/functional/test_mips64el_tuxrun.py +++ b/tests/functional/mips64el/test_tuxrun.py diff --git a/tests/functional/mipsel/meson.build b/tests/functional/mipsel/meson.build new file mode 100644 index 0000000000..8bfdf0649b --- /dev/null +++ b/tests/functional/mipsel/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mipsel_timeouts = { + 'malta' : 420, + 'replay' : 480, +} + +tests_mipsel_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/mipsel/test_malta.py index 9ee2884da8..427e163d19 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/mipsel/test_malta.py @@ -13,7 +13,7 @@ from qemu_test import QemuSystemTest, LinuxKernelTest, Asset from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/mipsel/test_replay.py index 5f4796cf89..5f4796cf89 100644..100755 --- a/tests/functional/test_mipsel_replay.py +++ b/tests/functional/mipsel/test_replay.py diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/mipsel/test_tuxrun.py index d4b39baab5..d4b39baab5 100755 --- a/tests/functional/test_mipsel_tuxrun.py +++ b/tests/functional/mipsel/test_tuxrun.py diff --git a/tests/functional/test_multiprocess.py b/tests/functional/multiprocess.py index 92d5207b0e..6a06c1eda1 100755..100644 --- a/tests/functional/test_multiprocess.py +++ b/tests/functional/multiprocess.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # # Test for multiprocess qemu # @@ -9,33 +9,13 @@ import os import socket -from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import QemuSystemTest, wait_for_console_pattern from qemu_test import exec_command, exec_command_and_wait_for_pattern class Multiprocess(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - ASSET_KERNEL_X86 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), - 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') - - ASSET_INITRD_X86 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), - '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') - - ASSET_KERNEL_AARCH64 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), - '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') - - ASSET_INITRD_AARCH64 = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), - '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') - def do_test(self, kernel_asset, initrd_asset, kernel_command_line, machine_type): """Main test method""" @@ -85,19 +65,3 @@ class Multiprocess(QemuSystemTest): proxy_sock.close() remote_sock.close() - - def test_multiprocess(self): - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE - if self.arch == 'x86_64': - kernel_command_line += 'console=ttyS0 rdinit=/bin/bash' - self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, - kernel_command_line, 'pc') - elif self.arch == 'aarch64': - kernel_command_line += 'rdinit=/bin/bash console=ttyAMA0' - self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, - kernel_command_line, 'virt,gic-version=3') - else: - assert False - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/or1k/meson.build b/tests/functional/or1k/meson.build new file mode 100644 index 0000000000..e246e2ab08 --- /dev/null +++ b/tests/functional/or1k/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_or1k_system_thorough = [ + 'replay', + 'sim', +] diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/or1k/test_replay.py index 2b60a9372c..2b60a9372c 100755 --- a/tests/functional/test_or1k_replay.py +++ b/tests/functional/or1k/test_replay.py diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/or1k/test_sim.py index f9f0b690a0..f9f0b690a0 100755 --- a/tests/functional/test_or1k_sim.py +++ b/tests/functional/or1k/test_sim.py diff --git a/tests/functional/ppc/meson.build b/tests/functional/ppc/meson.build new file mode 100644 index 0000000000..3d562010d8 --- /dev/null +++ b/tests/functional/ppc/meson.build @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc_timeouts = { + '40p' : 240, +} + +tests_ppc_system_quick = [ + 'migration', + '74xx', +] + +tests_ppc_system_thorough = [ + '40p', + 'amiga', + 'bamboo', + 'mac', + 'mpc8544ds', + 'replay', + 'sam460ex', + 'tuxrun', + 'virtex_ml507', +] diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/ppc/test_40p.py index 614972a7eb..614972a7eb 100755 --- a/tests/functional/test_ppc_40p.py +++ b/tests/functional/ppc/test_40p.py diff --git a/tests/functional/test_ppc_74xx.py b/tests/functional/ppc/test_74xx.py index 5386016f26..5386016f26 100755 --- a/tests/functional/test_ppc_74xx.py +++ b/tests/functional/ppc/test_74xx.py diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/ppc/test_amiga.py index 8600e2e963..8600e2e963 100755 --- a/tests/functional/test_ppc_amiga.py +++ b/tests/functional/ppc/test_amiga.py diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/ppc/test_bamboo.py index c634ae7b4a..c634ae7b4a 100755 --- a/tests/functional/test_ppc_bamboo.py +++ b/tests/functional/ppc/test_bamboo.py diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/ppc/test_mac.py index 9e4bc1a52c..9e4bc1a52c 100755 --- a/tests/functional/test_ppc_mac.py +++ b/tests/functional/ppc/test_mac.py diff --git a/tests/functional/ppc/test_migration.py b/tests/functional/ppc/test_migration.py new file mode 100755 index 0000000000..a8692826d3 --- /dev/null +++ b/tests/functional/ppc/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sam460ex') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sam460ex') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sam460ex') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/ppc/test_mpc8544ds.py index 0715410d7a..0715410d7a 100755 --- a/tests/functional/test_ppc_mpc8544ds.py +++ b/tests/functional/ppc/test_mpc8544ds.py diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/ppc/test_replay.py index 8382070abd..8382070abd 100755 --- a/tests/functional/test_ppc_replay.py +++ b/tests/functional/ppc/test_replay.py diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/ppc/test_sam460ex.py index 31cf9dd6de..31cf9dd6de 100644..100755 --- a/tests/functional/test_ppc_sam460ex.py +++ b/tests/functional/ppc/test_sam460ex.py diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/ppc/test_tuxrun.py index 5458a7fb71..5458a7fb71 100755 --- a/tests/functional/test_ppc_tuxrun.py +++ b/tests/functional/ppc/test_tuxrun.py diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/ppc/test_virtex_ml507.py index 8fe43549b7..8fe43549b7 100755 --- a/tests/functional/test_ppc_virtex_ml507.py +++ b/tests/functional/ppc/test_virtex_ml507.py diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build new file mode 100644 index 0000000000..842fe0fc71 --- /dev/null +++ b/tests/functional/ppc64/meson.build @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc64_timeouts = { + 'hv' : 1000, + 'mac99' : 120, + 'powernv' : 480, + 'pseries' : 480, + 'replay' : 210, + 'tuxrun' : 420, +} + +tests_ppc64_system_quick = [ + 'migration', +] + +tests_ppc64_system_thorough = [ + 'e500', + 'hv', + 'mac99', + 'powernv', + 'pseries', + 'replay', + 'reverse_debug', + 'tuxrun', +] diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/ppc64/test_e500.py index f5fcad9f6b..f5fcad9f6b 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/ppc64/test_e500.py diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/ppc64/test_hv.py index d87f440fa7..d87f440fa7 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/ppc64/test_hv.py diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/ppc64/test_mac99.py index dfd9c01371..dfd9c01371 100755 --- a/tests/functional/test_ppc64_mac99.py +++ b/tests/functional/ppc64/test_mac99.py diff --git a/tests/functional/ppc64/test_migration.py b/tests/functional/ppc64/test_migration.py new file mode 100755 index 0000000000..5dfdaaf709 --- /dev/null +++ b/tests/functional/ppc64/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('mac99') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('mac99') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('mac99') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc64_powernv.py b/tests/functional/ppc64/test_powernv.py index 685e2178ed..685e2178ed 100755 --- a/tests/functional/test_ppc64_powernv.py +++ b/tests/functional/ppc64/test_powernv.py diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/ppc64/test_pseries.py index 67057934e8..67057934e8 100755 --- a/tests/functional/test_ppc64_pseries.py +++ b/tests/functional/ppc64/test_pseries.py diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/ppc64/test_replay.py index e8c9c4bcbf..e8c9c4bcbf 100755 --- a/tests/functional/test_ppc64_replay.py +++ b/tests/functional/ppc64/test_replay.py diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/ppc64/test_reverse_debug.py index 5931adef5a..5931adef5a 100755 --- a/tests/functional/test_ppc64_reverse_debug.py +++ b/tests/functional/ppc64/test_reverse_debug.py diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/ppc64/test_tuxrun.py index e8f79c676e..e8f79c676e 100755 --- a/tests/functional/test_ppc64_tuxrun.py +++ b/tests/functional/ppc64/test_tuxrun.py diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py index 631b77abf6..81174a6153 100644 --- a/tests/functional/qemu_test/ports.py +++ b/tests/functional/qemu_test/ports.py @@ -23,8 +23,9 @@ class Ports(): PORTS_END = PORTS_START + PORTS_RANGE_SIZE def __enter__(self): - lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock") - self.lock_fh = os.open(lock_file, os.O_CREAT) + lock_file = os.path.join(BUILD_DIR, "tests", "functional", + f".port_lock.{self.PORTS_START}") + self.lock_fh = os.open(lock_file, os.O_CREAT, mode=0o666) fcntl.flock(self.lock_fh, fcntl.LOCK_EX) return self diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 5caf7b13fe..fbeb171058 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -235,6 +235,7 @@ class QemuBaseTest(unittest.TestCase): self.log.removeHandler(self._log_fh) self._log_fh.close() + @staticmethod def main(): warnings.simplefilter("default") os.environ["PYTHONWARNINGS"] = "default" diff --git a/tests/functional/riscv32/meson.build b/tests/functional/riscv32/meson.build new file mode 100644 index 0000000000..f3ebbb8db5 --- /dev/null +++ b/tests/functional/riscv32/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_riscv32_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv32_system_thorough = [ + 'tuxrun', +] diff --git a/tests/functional/riscv32/test_migration.py b/tests/functional/riscv32/test_migration.py new file mode 100755 index 0000000000..30acbbe69f --- /dev/null +++ b/tests/functional/riscv32/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv32 migration test + +from migration import MigrationTest + + +class Rv32MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('spike') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('virt') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('spike') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/riscv32/test_opensbi.py b/tests/functional/riscv32/test_opensbi.py new file mode 100755 index 0000000000..d1ac706f0b --- /dev/null +++ b/tests/functional/riscv32/test_opensbi.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reuse the 64-bit OpenSBI test for RISC-V 32-bit machines + +from riscv64.test_opensbi import RiscvOpenSBI + +if __name__ == '__main__': + RiscvOpenSBI.main() diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/riscv32/test_tuxrun.py index 3c570208d0..3c570208d0 100755 --- a/tests/functional/test_riscv32_tuxrun.py +++ b/tests/functional/riscv32/test_tuxrun.py diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build new file mode 100644 index 0000000000..c1704d9275 --- /dev/null +++ b/tests/functional/riscv64/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_riscv64_timeouts = { + 'tuxrun' : 120, +} + +tests_riscv64_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv64_system_thorough = [ + 'sifive_u', + 'tuxrun', +] diff --git a/tests/functional/riscv64/test_migration.py b/tests/functional/riscv64/test_migration.py new file mode 100755 index 0000000000..2d613a29ec --- /dev/null +++ b/tests/functional/riscv64/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv64 migration test + +from migration import MigrationTest + + +class Rv64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('virt') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('spike') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('virt') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_riscv_opensbi.py b/tests/functional/riscv64/test_opensbi.py index d077e40f42..d077e40f42 100755 --- a/tests/functional/test_riscv_opensbi.py +++ b/tests/functional/riscv64/test_opensbi.py diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/riscv64/test_sifive_u.py index 358ff0d1f6..358ff0d1f6 100755 --- a/tests/functional/test_riscv64_sifive_u.py +++ b/tests/functional/riscv64/test_sifive_u.py diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py index 0d8de36204..0d8de36204 100755 --- a/tests/functional/test_riscv64_tuxrun.py +++ b/tests/functional/riscv64/test_tuxrun.py diff --git a/tests/functional/rx/meson.build b/tests/functional/rx/meson.build new file mode 100644 index 0000000000..6af83a9f23 --- /dev/null +++ b/tests/functional/rx/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_rx_system_thorough = [ + 'gdbsim', +] diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/rx/test_gdbsim.py index 49245793e1..49245793e1 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/rx/test_gdbsim.py diff --git a/tests/functional/s390x/meson.build b/tests/functional/s390x/meson.build new file mode 100644 index 0000000000..030b116039 --- /dev/null +++ b/tests/functional/s390x/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_s390x_timeouts = { + 'ccw_virtio' : 420, +} + +tests_s390x_system_thorough = [ + 'ccw_virtio', + 'pxelinux', + 'replay', + 'topology', + 'tuxrun', +] diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/s390x/test_ccw_virtio.py index 453711aa0f..453711aa0f 100755 --- a/tests/functional/test_s390x_ccw_virtio.py +++ b/tests/functional/s390x/test_ccw_virtio.py diff --git a/tests/functional/test_s390x_pxelinux.py b/tests/functional/s390x/test_pxelinux.py index 4fc33b8c46..4fc33b8c46 100755 --- a/tests/functional/test_s390x_pxelinux.py +++ b/tests/functional/s390x/test_pxelinux.py diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/s390x/test_replay.py index 33b5843ada..33b5843ada 100755 --- a/tests/functional/test_s390x_replay.py +++ b/tests/functional/s390x/test_replay.py diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/s390x/test_topology.py index 1b5dc65135..1b5dc65135 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/s390x/test_topology.py diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/s390x/test_tuxrun.py index 8df3c6893b..8df3c6893b 100755 --- a/tests/functional/test_s390x_tuxrun.py +++ b/tests/functional/s390x/test_tuxrun.py diff --git a/tests/functional/sh4/meson.build b/tests/functional/sh4/meson.build new file mode 100644 index 0000000000..56f824e1e7 --- /dev/null +++ b/tests/functional/sh4/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_sh4_timeouts = { + 'tuxrun' : 240, +} + +tests_sh4_system_thorough = [ + 'r2d', + 'tuxrun', +] diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/sh4/test_r2d.py index 03a648374c..03a648374c 100755 --- a/tests/functional/test_sh4_r2d.py +++ b/tests/functional/sh4/test_r2d.py diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/sh4/test_tuxrun.py index 1748f8c7ef..1748f8c7ef 100755 --- a/tests/functional/test_sh4_tuxrun.py +++ b/tests/functional/sh4/test_tuxrun.py diff --git a/tests/functional/sh4eb/meson.build b/tests/functional/sh4eb/meson.build new file mode 100644 index 0000000000..25e9a6e404 --- /dev/null +++ b/tests/functional/sh4eb/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sh4eb_system_thorough = [ + 'r2d', +] diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/sh4eb/test_r2d.py index 473093bbe1..473093bbe1 100755 --- a/tests/functional/test_sh4eb_r2d.py +++ b/tests/functional/sh4eb/test_r2d.py diff --git a/tests/functional/sparc/meson.build b/tests/functional/sparc/meson.build new file mode 100644 index 0000000000..88732becd8 --- /dev/null +++ b/tests/functional/sparc/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc_system_quick = [ + 'migration', +] + +tests_sparc_system_thorough = [ + 'replay', + 'sun4m', +] diff --git a/tests/functional/sparc/test_migration.py b/tests/functional/sparc/test_migration.py new file mode 100755 index 0000000000..dd6d5783b1 --- /dev/null +++ b/tests/functional/sparc/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc migration test + +from migration import MigrationTest + + +class SparcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('SS-4') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('SS-5') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('SS-4') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/sparc/test_replay.py index 865d6486f9..865d6486f9 100755 --- a/tests/functional/test_sparc_replay.py +++ b/tests/functional/sparc/test_replay.py diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/sparc/test_sun4m.py index 7cd28ebdd1..7cd28ebdd1 100755 --- a/tests/functional/test_sparc_sun4m.py +++ b/tests/functional/sparc/test_sun4m.py diff --git a/tests/functional/sparc64/meson.build b/tests/functional/sparc64/meson.build new file mode 100644 index 0000000000..2e04e7d4f3 --- /dev/null +++ b/tests/functional/sparc64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc64_system_quick = [ + 'migration', +] + +tests_sparc64_system_thorough = [ + 'sun4u', + 'tuxrun', +] diff --git a/tests/functional/sparc64/test_migration.py b/tests/functional/sparc64/test_migration.py new file mode 100755 index 0000000000..a8a6c73c35 --- /dev/null +++ b/tests/functional/sparc64/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc64 migration test + +from migration import MigrationTest + + +class Sparc64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sun4u') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sun4u') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sun4u') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/sparc64/test_sun4u.py index 27ac289659..27ac289659 100755 --- a/tests/functional/test_sparc64_sun4u.py +++ b/tests/functional/sparc64/test_sun4u.py diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/sparc64/test_tuxrun.py index 0d7b43dd74..0d7b43dd74 100755 --- a/tests/functional/test_sparc64_tuxrun.py +++ b/tests/functional/sparc64/test_tuxrun.py diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py deleted file mode 100755 index 746770e776..0000000000 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# -# Functional test that boots a Realms environment on sbsa-ref machine and a -# nested guest VM using it. -# -# Copyright (c) 2024 Linaro Ltd. -# -# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os - -from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern -from qemu_test import exec_command_and_wait_for_pattern -from test_aarch64_rme_virt import test_realms_guest - - -class Aarch64RMESbsaRefMachine(QemuSystemTest): - - # Stack is built with OP-TEE build environment from those instructions: - # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack - ASSET_RME_STACK_SBSA = Asset( - ('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/' - 'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'), - 'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e') - - # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, - # and launching a nested VM using it. - def test_aarch64_rme_sbsaref(self): - self.set_machine('sbsa-ref') - self.require_accelerator('tcg') - self.require_netdev('user') - - self.vm.set_console() - - stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") - - rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa') - pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd') - pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd') - virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual') - drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') - - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') - self.vm.add_args('-m', '2G') - self.vm.add_args('-M', 'sbsa-ref') - self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') - self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') - self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw') - self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') - self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') - self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0') - self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') - self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') - self.vm.add_args('-netdev', 'user,id=net0') - - self.vm.launch() - # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot', - failure_message='Synchronous Exception at') - exec_command_and_wait_for_pattern(self, 'root', '#') - - test_realms_guest(self) - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py deleted file mode 100755 index 8452d27928..0000000000 --- a/tests/functional/test_aarch64_rme_virt.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -# Functional test that boots a Realms environment on virt machine and a nested -# guest VM using it. -# -# Copyright (c) 2024 Linaro Ltd. -# -# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os - -from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command, wait_for_console_pattern -from qemu_test import exec_command_and_wait_for_pattern - -def test_realms_guest(test_rme_instance): - - # Boot the (nested) guest VM - exec_command(test_rme_instance, - 'qemu-system-aarch64 -M virt,gic-version=3 ' - '-cpu host -enable-kvm -m 512M ' - '-M confidential-guest-support=rme0 ' - '-object rme-guest,id=rme0 ' - '-device virtio-net-pci,netdev=net0,romfile= ' - '-netdev user,id=net0 ' - '-kernel /mnt/out/bin/Image ' - '-initrd /mnt/out-br/images/rootfs.cpio ' - '-serial stdio') - # Detect Realm activation during (nested) guest boot. - wait_for_console_pattern(test_rme_instance, - 'SMC_RMI_REALM_ACTIVATE') - # Wait for (nested) guest boot to complete. - wait_for_console_pattern(test_rme_instance, - 'Welcome to Buildroot') - exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#') - # query (nested) guest cca report - exec_command(test_rme_instance, 'cca-workload-attestation report') - wait_for_console_pattern(test_rme_instance, - '"cca-platform-hash-algo-id": "sha-256"') - wait_for_console_pattern(test_rme_instance, - '"cca-realm-hash-algo-id": "sha-512"') - wait_for_console_pattern(test_rme_instance, - '"cca-realm-public-key-hash-algo-id": "sha-256"') - -class Aarch64RMEVirtMachine(QemuSystemTest): - - # Stack is built with OP-TEE build environment from those instructions: - # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack - ASSET_RME_STACK_VIRT = Asset( - ('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/' - 'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'), - '1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad') - - # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, - # and launching a nested VM using it. - def test_aarch64_rme_virt(self): - self.set_machine('virt') - self.require_accelerator('tcg') - self.require_netdev('user') - - self.vm.set_console() - - stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") - - rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8') - kernel = os.path.join(rme_stack, 'out', 'bin', 'Image') - bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin') - drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') - - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') - self.vm.add_args('-m', '2G') - self.vm.add_args('-M', 'virt,acpi=off,' - 'virtualization=on,' - 'secure=on,' - 'gic-version=3') - self.vm.add_args('-bios', bios) - self.vm.add_args('-kernel', kernel) - self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') - self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') - self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0') - self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') - self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') - self.vm.add_args('-netdev', 'user,id=net0') - # We need to add nokaslr to avoid triggering this sporadic bug: - # https://gitlab.com/qemu-project/qemu/-/issues/2823 - self.vm.add_args('-append', 'root=/dev/vda nokaslr') - - self.vm.launch() - # Wait for host VM boot to complete. - wait_for_console_pattern(self, 'Welcome to Buildroot', - failure_message='Synchronous Exception at') - exec_command_and_wait_for_pattern(self, 'root', '#') - - test_realms_guest(self) - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build new file mode 100644 index 0000000000..d0b4667bb8 --- /dev/null +++ b/tests/functional/x86_64/meson.build @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_x86_64_timeouts = { + 'acpi_bits' : 420, + 'intel_iommu': 300, + 'kvm_xen' : 180, + 'netdev_ethtool' : 180, + 'replay' : 480, + 'virtio_balloon': 120, +} + +tests_x86_64_system_quick = [ + 'cpu_model_versions', + 'cpu_queries', + 'mem_addr_space', + 'migration', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'memlock', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'hotplug_blk', + 'hotplug_cpu', + 'intel_iommu', + 'kvm_xen', + 'linux_initrd', + 'multiprocess', + 'netdev_ethtool', + 'replay', + 'reverse_debug', + 'tuxrun', + 'virtio_balloon', + 'virtio_gpu', +] diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/x86_64/test_acpi_bits.py index 8e0563a97b..8e0563a97b 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/x86_64/test_acpi_bits.py diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/x86_64/test_cpu_model_versions.py index 36c968f1c0..36c968f1c0 100755 --- a/tests/functional/test_x86_cpu_model_versions.py +++ b/tests/functional/x86_64/test_cpu_model_versions.py diff --git a/tests/functional/test_cpu_queries.py b/tests/functional/x86_64/test_cpu_queries.py index b1122a0e8f..b1122a0e8f 100755 --- a/tests/functional/test_cpu_queries.py +++ b/tests/functional/x86_64/test_cpu_queries.py diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/x86_64/test_hotplug_blk.py index 7ddbfefc21..7ddbfefc21 100755 --- a/tests/functional/test_x86_64_hotplug_blk.py +++ b/tests/functional/x86_64/test_hotplug_blk.py diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/x86_64/test_hotplug_cpu.py index 7b9200ac2e..7b9200ac2e 100755 --- a/tests/functional/test_x86_64_hotplug_cpu.py +++ b/tests/functional/x86_64/test_hotplug_cpu.py diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/x86_64/test_intel_iommu.py index 62268d6f27..62268d6f27 100755 --- a/tests/functional/test_intel_iommu.py +++ b/tests/functional/x86_64/test_intel_iommu.py diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/x86_64/test_kvm_xen.py index a5d445023c..a5d445023c 100755 --- a/tests/functional/test_x86_64_kvm_xen.py +++ b/tests/functional/x86_64/test_kvm_xen.py diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/x86_64/test_linux_initrd.py index 2207f83fbf..2207f83fbf 100755 --- a/tests/functional/test_linux_initrd.py +++ b/tests/functional/x86_64/test_linux_initrd.py diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/x86_64/test_mem_addr_space.py index 61b4a190b4..61b4a190b4 100755 --- a/tests/functional/test_mem_addr_space.py +++ b/tests/functional/x86_64/test_mem_addr_space.py diff --git a/tests/functional/test_memlock.py b/tests/functional/x86_64/test_memlock.py index 2b515ff979..2b515ff979 100755 --- a/tests/functional/test_memlock.py +++ b/tests/functional/x86_64/test_memlock.py diff --git a/tests/functional/x86_64/test_migration.py b/tests/functional/x86_64/test_migration.py new file mode 100755 index 0000000000..f3a517ae1f --- /dev/null +++ b/tests/functional/x86_64/test_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# x86_64 migration test + +from migration import MigrationTest + + +class X8664MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('microvm') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('microvm') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('microvm') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/x86_64/test_multiprocess.py b/tests/functional/x86_64/test_multiprocess.py new file mode 100755 index 0000000000..756629dd44 --- /dev/null +++ b/tests/functional/x86_64/test_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on x86 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class X86Multiprocess(Multiprocess): + + ASSET_KERNEL_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), + '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') + + def test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 rdinit=/bin/bash') + self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, + kernel_command_line, 'pc') + + +if __name__ == '__main__': + Multiprocess.main() diff --git a/tests/functional/test_netdev_ethtool.py b/tests/functional/x86_64/test_netdev_ethtool.py index ee1a397bd2..ee1a397bd2 100755 --- a/tests/functional/test_netdev_ethtool.py +++ b/tests/functional/x86_64/test_netdev_ethtool.py diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/x86_64/test_pc_cpu_hotplug_props.py index 2bed8ada02..2bed8ada02 100755 --- a/tests/functional/test_pc_cpu_hotplug_props.py +++ b/tests/functional/x86_64/test_pc_cpu_hotplug_props.py diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/x86_64/test_replay.py index 27287d452d..27287d452d 100755 --- a/tests/functional/test_x86_64_replay.py +++ b/tests/functional/x86_64/test_replay.py diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/x86_64/test_reverse_debug.py index d713e91e14..d713e91e14 100755 --- a/tests/functional/test_x86_64_reverse_debug.py +++ b/tests/functional/x86_64/test_reverse_debug.py diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/x86_64/test_tuxrun.py index fcbc62b1b0..fcbc62b1b0 100755 --- a/tests/functional/test_x86_64_tuxrun.py +++ b/tests/functional/x86_64/test_tuxrun.py diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/x86_64/test_virtio_balloon.py index 5877b6c408..5877b6c408 100755 --- a/tests/functional/test_virtio_balloon.py +++ b/tests/functional/x86_64/test_virtio_balloon.py diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/x86_64/test_virtio_gpu.py index be96de24da..be96de24da 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/x86_64/test_virtio_gpu.py diff --git a/tests/functional/test_virtio_version.py b/tests/functional/x86_64/test_virtio_version.py index a5ea73237f..a5ea73237f 100755 --- a/tests/functional/test_virtio_version.py +++ b/tests/functional/x86_64/test_virtio_version.py diff --git a/tests/functional/xtensa/meson.build b/tests/functional/xtensa/meson.build new file mode 100644 index 0000000000..d61d82a135 --- /dev/null +++ b/tests/functional/xtensa/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_xtensa_system_thorough = [ + 'lx60', + 'replay', +] diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/xtensa/test_lx60.py index 147c920899..147c920899 100755 --- a/tests/functional/test_xtensa_lx60.py +++ b/tests/functional/xtensa/test_lx60.py diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/xtensa/test_replay.py index eb00a3b004..eb00a3b004 100755 --- a/tests/functional/test_xtensa_replay.py +++ b/tests/functional/xtensa/test_replay.py diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index b3f2e7fbef..fd27521a9c 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -977,7 +977,7 @@ void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, * @cb: Pointer to the callback function * @skip_old_versioned: true if versioned old machine types should be skipped * - * Call a callback function for every name of all available machines. + * Call a callback function for every name of all available machines. */ void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned); diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 4ade1c728c..2da9918e16 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -180,7 +180,7 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) links = g_slist_delete_link(links, links); } while (children) { - test_properties(qts, children->data, true); + test_properties(qts, children->data, g_test_slow()); g_free(children->data); children = g_slist_delete_link(children, children); } @@ -211,14 +211,10 @@ static void test_machine(gconstpointer data) test_properties(qts, "/machine", true); - qlist_append_str(paths, "/machine"); + qlist_append_str(paths, "/"); test_list_get(qts, paths); test_list_get_value(qts); - response = qtest_qmp(qts, "{ 'execute': 'quit' }"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); - qtest_quit(qts); g_free((void *)machine); } diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index abfd4b9512..00f39f33f6 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -328,11 +328,6 @@ static void walk_path(QOSGraphNode *orig_path, int len) int main(int argc, char **argv, char** envp) { g_test_init(&argc, &argv, NULL); - - if (g_test_subprocess()) { - qos_printf("qos_test running single test in subprocess\n"); - } - if (g_test_verbose()) { qos_printf("ENVIRONMENT VARIABLES: {\n"); for (char **env = envp; *env != 0; env++) { diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 75cb3e44b2..56472ca709 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -26,7 +26,6 @@ #include "libqos/virtio-pci.h" #include "libqos/malloc-pc.h" -#include "libqos/qgraph_internal.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/vhost_types.h" @@ -345,7 +344,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } if (size != VHOST_USER_HDR_SIZE) { - qos_printf("%s: Wrong message size received %d\n", __func__, size); + g_test_message("Wrong message size received %d", size); return; } @@ -356,8 +355,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { - qos_printf("%s: Wrong message size received %d != %d\n", - __func__, size, msg.size); + g_test_message("Wrong message size received %d != %d", + size, msg.size); goto out; } } @@ -393,7 +392,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * We don't need to do anything here, the remote is just * letting us know it is in charge. Just log it. */ - qos_printf("set_owner: start of session\n"); + g_test_message("set_owner: start of session\n"); break; case VHOST_USER_GET_PROTOCOL_FEATURES: @@ -419,7 +418,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * the remote end to send this. There is no handshake reply so * just log the details for debugging. */ - qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); + g_test_message("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); break; /* @@ -427,11 +426,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * address of the vrings but we can simply report them. */ case VHOST_USER_SET_VRING_NUM: - qos_printf("set_vring_num: %d/%d\n", + g_test_message("set_vring_num: %d/%d\n", msg.payload.state.index, msg.payload.state.num); break; case VHOST_USER_SET_VRING_ADDR: - qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", + g_test_message("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", msg.payload.addr.avail_user_addr, msg.payload.addr.desc_user_addr, msg.payload.addr.used_user_addr); @@ -464,7 +463,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_CALL: /* consume the fd */ if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { - qos_printf("call fd: %d, do not set non-blocking\n", fd); + g_test_message("call fd: %d, do not set non-blocking\n", fd); break; } /* @@ -510,12 +509,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * fully functioning vhost-user we would enable/disable the * vring monitoring. */ - qos_printf("set_vring(%d)=%s\n", msg.payload.state.index, + g_test_message("set_vring(%d)=%s\n", msg.payload.state.index, msg.payload.state.num ? "enabled" : "disabled"); break; default: - qos_printf("vhost-user: un-handled message: %d\n", msg.request); + g_test_message("vhost-user: un-handled message: %d\n", msg.request); break; } @@ -539,7 +538,7 @@ static const char *init_hugepagefs(void) } if (access(path, R_OK | W_OK | X_OK)) { - qos_printf("access on path (%s): %s", path, strerror(errno)); + g_test_message("access on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } @@ -549,13 +548,13 @@ static const char *init_hugepagefs(void) } while (ret != 0 && errno == EINTR); if (ret != 0) { - qos_printf("statfs on path (%s): %s", path, strerror(errno)); + g_test_message("statfs on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { - qos_printf("Warning: path not on HugeTLBFS: %s", path); + g_test_message("Warning: path not on HugeTLBFS: %s", path); g_test_fail(); return NULL; } diff --git a/ui/input-barrier.c b/ui/input-barrier.c index 9793258aac..0a2198ca50 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -490,7 +490,6 @@ static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED, static void input_barrier_complete(UserCreatable *uc, Error **errp) { InputBarrier *ib = INPUT_BARRIER(uc); - Error *local_err = NULL; if (!ib->name) { error_setg(errp, QERR_MISSING_PARAMETER, "name"); @@ -506,9 +505,7 @@ static void input_barrier_complete(UserCreatable *uc, Error **errp) ib->sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client"); - qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, errp) < 0) { return; } diff --git a/ui/keymaps.c b/ui/keymaps.c index 6ceaa97085..2359dbfe7e 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -86,19 +86,25 @@ static int parse_keyboard_layout(kbd_layout_t *k, const name2keysym_t *table, const char *language, Error **errp) { + g_autofree char *filename = NULL; int ret; FILE *f; - char * filename; char line[1024]; char keyname[64]; int len; filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); + if (!filename) { + error_setg(errp, "could not find keymap file for language '%s'", + language); + return -1; + } + trace_keymap_parse(filename); - f = filename ? fopen(filename, "r") : NULL; - g_free(filename); + + f = fopen(filename, "r"); if (!f) { - error_setg(errp, "could not read keymap file: '%s'", language); + error_setg_file_open(errp, errno, filename); return -1; } |