diff options
Diffstat (limited to 'tests/tcg')
28 files changed, 1966 insertions, 42 deletions
diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 95ff76e..18afd5b 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -127,8 +127,14 @@ else # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. EXTRA_CFLAGS += -ffreestanding -fno-stack-protector + +# We skip the multiarch tests if the target hasn't provided a boot.S +MULTIARCH_SOFTMMU_TARGETS = i386 alpha aarch64 arm loongarch64 s390x x86_64 + +ifneq ($(filter $(TARGET_NAME),$(MULTIARCH_SOFTMMU_TARGETS)),) -include $(SRC_PATH)/tests/tcg/minilib/Makefile.target -include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target +endif -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target endif @@ -151,20 +157,41 @@ ifeq ($(CONFIG_PLUGIN),y) PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) -PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) +# Some plugins need to be disabled for all tests to avoid exponential explosion. +# For example, libpatch.so only needs to run against the arch-specific patch +# target test, so we explicitly run it in the arch-specific Makefile. +DISABLE_PLUGINS=libpatch.so + +# Likewise don't bother with the syscall plugin for softmmu +ifneq ($(filter %-softmmu, $(TARGET)),) +DISABLE_PLUGINS += libsyscall.so +endif + +PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ + $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets -# to avoid an exponential explosion as new tests are added. We also -# add some special helpers the run-plugin- rules can use below. -# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. +# and rotate the plugins so we don't grow too out of control as new +# tests are added. Plugins that need to run with a specific test +# should ensure they add their combination to EXTRA_RUNS. ifneq ($(MULTIARCH_TESTS),) -$(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ - $(eval run-plugin-$(t)-with-$(p): $t $p) \ - $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) + +NUM_PLUGINS := $(words $(PLUGINS)) +NUM_TESTS := $(words $(MULTIARCH_TESTS)) + +define mod_plus_one + $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )") +endef + +$(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \ + $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \ + $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \ + $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \ + $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin))) + endif # MULTIARCH_TESTS endif # CONFIG_PLUGIN diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 9c52475..f7a7d2b 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -68,7 +68,8 @@ run-plugin-semiconsole-with-%: semiconsole # vtimer test needs EL2 QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -smp 4 -run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_BASE_ARGS) -kernel +QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2" +run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel # Simple Record/Replay Test .PHONY: memory-record diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index 4eb1b35..8bfa4e4 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -16,6 +16,7 @@ #define semihosting_call hlt 0xf000 #define SYS_WRITEC 0x03 /* character to debug channel */ #define SYS_WRITE0 0x04 /* string to debug channel */ +#define SYS_GET_CMDLINE 0x15 /* get command line */ #define SYS_EXIT 0x18 .align 12 @@ -70,22 +71,172 @@ lower_a32_sync: lower_a32_irq: lower_a32_fiq: lower_a32_serror: + adr x1, .unexp_excp +exit_msg: mov x0, SYS_WRITE0 - adr x1, .error - semihosting_call - mov x0, SYS_EXIT - mov x1, 1 semihosting_call + mov x0, 1 /* EXIT_FAILURE */ + bl _exit /* never returns */ .section .rodata -.error: - .string "Terminated by exception.\n" +.unexp_excp: + .string "Unexpected exception.\n" +.high_el_msg: + .string "Started in lower EL than requested.\n" +.unexp_el0: + .string "Started in invalid EL.\n" + + .align 8 +.get_cmd: + .quad cmdline + .quad 128 .text .align 4 .global __start __start: + /* + * Initialise the stack for whatever EL we are in before + * anything else, we need it to be able to _exit cleanly. + * It's smaller than the stack we pass to the C code but we + * don't need much. + */ + adrp x0, system_stack_end + add x0, x0, :lo12:system_stack_end + mov sp, x0 + + /* + * The test can set the semihosting command line to the target + * EL needed for the test. However if no semihosting args are set we will + * end up with -kernel/-append data (see semihosting_arg_fallback). + * Keep the normalised target in w11. + */ + mov x0, SYS_GET_CMDLINE + adr x1, .get_cmd + semihosting_call + adrp x10, cmdline + add x10, x10, :lo12:cmdline + ldrb w11, [x10] + + /* sanity check, normalise char to EL, clamp to 1 if outside range */ + subs w11, w11, #'0' + b.lt el_default + cmp w11, #3 + b.gt el_default + b 1f + +el_high: + adr x1, .high_el_msg + b exit_msg + +el_default: + mov w11, #1 + +1: + /* Determine current Exception Level */ + mrs x0, CurrentEL + lsr x0, x0, #2 /* CurrentEL[3:2] contains the current EL */ + + /* Are we already in a lower EL than we want? */ + cmp w11, w0 + bgt el_high + + /* Branch based on current EL */ + cmp x0, #3 + b.eq setup_el3 + cmp x0, #2 + b.eq setup_el2 + cmp x0, #1 + b.eq at_testel /* Already at EL1, skip transition */ + + /* Should not be at EL0 - error out */ + adr x1, .unexp_el0 + b exit_msg + +setup_el3: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el3, x0 + + /* Does the test want to be at EL3? */ + cmp w11, #3 + beq at_testel + + /* Configure EL3 to for lower states (EL2 or EL1) */ + mrs x0, scr_el3 + orr x0, x0, #(1 << 10) /* RW = 1: EL2/EL1 execution state is AArch64 */ + orr x0, x0, #(1 << 0) /* NS = 1: Non-secure state */ + msr scr_el3, x0 + + /* + * We need to check if EL2 is actually enabled via ID_AA64PFR0_EL1, + * otherwise we should just jump straight to EL1. + */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #8, #4 /* Extract EL2 field (bits 11:8) */ + cbz x0, el2_not_present /* If field is 0 no EL2 */ + + + /* Prepare SPSR for exception return to EL2 */ + mov x0, #0x3c9 /* DAIF bits and EL2h mode (9) */ + msr spsr_el3, x0 + + /* Set EL2 entry point */ + adr x0, setup_el2 + msr elr_el3, x0 + + /* Return to EL2 */ + eret + +el2_not_present: + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el3, x0 + + /* Prepare SPSR for exception return to EL1h with interrupts masked */ + mov x0, #0x3c5 /* DAIF bits and EL1h mode (5) */ + msr spsr_el3, x0 + + isb /* Synchronization barrier */ + eret /* Jump to EL1 */ + +setup_el2: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el2, x0 + + /* Does the test want to be at EL2? */ + cmp w11, #2 + beq at_testel + + /* Configure EL2 to allow transition to EL1 */ + mrs x0, hcr_el2 + orr x0, x0, #(1 << 31) /* RW = 1: EL1 execution state is AArch64 */ + msr hcr_el2, x0 + + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el2, x0 + + /* Prepare SPSR for exception return to EL1 */ + mov x0, #(0x5 << 0) /* EL1h (SPx), with interrupts disabled */ + msr spsr_el2, x0 + + /* Return to EL1 */ + eret + + /* + * At the target EL for the test, usually EL1. Note we still + * set everything up as if we were at EL1. + */ +at_testel: /* Installs a table of exception vectors to catch and handle all exceptions by terminating the process with a diagnostic. */ adr x0, vector_table @@ -101,7 +252,7 @@ __start: * maps RAM to the first Gb. The stage2 tables have two 2mb * translation block entries covering a series of adjacent * 4k pages. - */ + */ /* Stage 1 entry: indexed by IA[38:30] */ adr x1, . /* phys address */ @@ -199,7 +350,8 @@ __start: orr x0, x0, #(3 << 16) msr cpacr_el1, x0 - /* Setup some stack space and enter the test code. + /* + * Setup some stack space before we enter the test code. * Assume everything except the return value is garbage when we * return, we won't need it. */ @@ -234,6 +386,11 @@ __sys_outc: ret .data + + .align 8 +cmdline: + .space 128, 0 + .align 12 /* Translation table @@ -247,6 +404,10 @@ ttb_stage2: .space 4096, 0 .align 12 +system_stack: + .space 4096, 0 +system_stack_end: + stack: .space 65536, 0 stack_end: diff --git a/tests/tcg/loongarch64/system/kernel.ld b/tests/tcg/loongarch64/system/kernel.ld index f1a7c01..56d8588 100644 --- a/tests/tcg/loongarch64/system/kernel.ld +++ b/tests/tcg/loongarch64/system/kernel.ld @@ -3,7 +3,7 @@ ENTRY(_start) SECTIONS { /* Linux kernel legacy start address. */ - . = 0x9000000000200000; + . = 0x200000; _text = .; .text : { *(.text) diff --git a/tests/tcg/mips/include/wrappers_mips64r6.h b/tests/tcg/mips/include/wrappers_mips64r6.h index d1e5edb..33d03de 100644 --- a/tests/tcg/mips/include/wrappers_mips64r6.h +++ b/tests/tcg/mips/include/wrappers_mips64r6.h @@ -23,6 +23,7 @@ #ifndef WRAPPERS_MIPS64R6_H #define WRAPPERS_MIPS64R6_H +#include <string.h> #define DO_MIPS64R6__RD__RS(suffix, mnemonic) \ static inline void do_mips64r6_##suffix(const void *input, \ @@ -80,4 +81,35 @@ DO_MIPS64R6__RD__RS_RT(DMULU, dmulu) DO_MIPS64R6__RD__RS_RT(DMUHU, dmuhu) +#define DO_MIPS64R6__RT__RS_RT(suffix, mnemonic) \ +static inline void do_mips64r6_##suffix(const void *input1, \ + const void *input2, \ + void *output) \ +{ \ + if (strncmp(#mnemonic, "crc32", 5) == 0) \ + __asm__ volatile ( \ + ".set crc\n\t" \ + ); \ + \ + __asm__ volatile ( \ + "ld $t1, 0(%0)\n\t" \ + "ld $t2, 0(%1)\n\t" \ + #mnemonic " $t2, $t1, $t2\n\t" \ + "sd $t2, 0(%2)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "t1", "t2", "memory" \ + ); \ +} + +DO_MIPS64R6__RT__RS_RT(CRC32B, crc32b) +DO_MIPS64R6__RT__RS_RT(CRC32H, crc32h) +DO_MIPS64R6__RT__RS_RT(CRC32W, crc32w) +DO_MIPS64R6__RT__RS_RT(CRC32D, crc32d) + +DO_MIPS64R6__RT__RS_RT(CRC32CB, crc32cb) +DO_MIPS64R6__RT__RS_RT(CRC32CH, crc32ch) +DO_MIPS64R6__RT__RS_RT(CRC32CW, crc32cw) +DO_MIPS64R6__RT__RS_RT(CRC32CD, crc32cd) + #endif diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/Makefile b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile new file mode 100644 index 0000000..b7f5811 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile @@ -0,0 +1,40 @@ +# +# Test program for MIPS64R6 CRC32 instructions +# +# Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +ifndef PREFIX + $(error "PREFIX not set, please export GNU Toolchain install directory.") +endif + +ifndef SYSROOT + $(error "SYSROOT not set, please export GNU Toolchain system root directory.") +endif + +SIM = ../../../../../../../build/qemu-mips64 +SIM_FLAGS = -L $(SYSROOT) + +CC = $(PREFIX)/bin/mips64-r6-linux-gnu-gcc + +TESTCASES = test_mips64r6_crc32b.tst +TESTCASES += test_mips64r6_crc32h.tst +TESTCASES += test_mips64r6_crc32w.tst +TESTCASES += test_mips64r6_crc32d.tst +TESTCASES += test_mips64r6_crc32cb.tst +TESTCASES += test_mips64r6_crc32ch.tst +TESTCASES += test_mips64r6_crc32cw.tst +TESTCASES += test_mips64r6_crc32cd.tst + +all: $(TESTCASES) + @for case in $(TESTCASES); do \ + echo $(SIM) $(SIM_FLAGS) ./$$case; \ + $(SIM) $(SIM_FLAGS) ./$$case; \ + echo $(RM) -rf ./$$case; \ + $(RM) -rf ./$$case; \ + done + +%.tst: %.c + $(CC) $< -o $@ diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c new file mode 100644 index 0000000..bb1f3f6 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0x000000002d02ef8dULL, + 0x000000001bab0fd1ULL, + 0x0000000036561fa3ULL, + 0xffffffffbf1caddaULL, + 0xffffffff92e1bda8ULL, + 0x00000000278c7949ULL, + 0x000000000a71693bULL, + 0x000000002dfd1072ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000036a9e05cULL, + 0x000000001b54f02eULL, + 0xffffffff921e4257ULL, + 0xffffffffbfe35225ULL, + 0x000000000a8e96c4ULL, + 0x00000000277386b6ULL, + 0x000000001bfe5a84ULL, /* 16 */ + 0x0000000036034af6ULL, + 0x0000000000aaaaaaULL, + 0x000000002d57bad8ULL, + 0xffffffffa41d08a1ULL, + 0xffffffff89e018d3ULL, + 0x000000003c8ddc32ULL, + 0x000000001170cc40ULL, + 0x0000000036fcb509ULL, /* 24 */ + 0x000000001b01a57bULL, + 0x000000002da84527ULL, + 0x0000000000555555ULL, + 0xffffffff891fe72cULL, + 0xffffffffa4e2f75eULL, + 0x00000000118f33bfULL, + 0x000000003c7223cdULL, + 0xffffffffbf2f9ee9ULL, /* 32 */ + 0xffffffff92d28e9bULL, + 0xffffffffa47b6ec7ULL, + 0xffffffff89867eb5ULL, + 0x0000000000ccccccULL, + 0x000000002d31dcbeULL, + 0xffffffff985c185fULL, + 0xffffffffb5a1082dULL, + 0xffffffff922d7164ULL, /* 40 */ + 0xffffffffbfd06116ULL, + 0xffffffff8979814aULL, + 0xffffffffa4849138ULL, + 0x000000002dce2341ULL, + 0x0000000000333333ULL, + 0xffffffffb55ef7d2ULL, + 0xffffffff98a3e7a0ULL, + 0x0000000027fdbe55ULL, /* 48 */ + 0x000000000a00ae27ULL, + 0x000000003ca94e7bULL, + 0x0000000011545e09ULL, + 0xffffffff981eec70ULL, + 0xffffffffb5e3fc02ULL, + 0x00000000008e38e3ULL, + 0x000000002d732891ULL, + 0x000000000aff51d8ULL, /* 56 */ + 0x00000000270241aaULL, + 0x0000000011aba1f6ULL, + 0x000000003c56b184ULL, + 0xffffffffb51c03fdULL, + 0xffffffff98e1138fULL, + 0x000000002d8cd76eULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0x00000000784a5a65ULL, + 0xffffffff9bdd0d3bULL, + 0xffffffffe7e61ce5ULL, + 0x00000000782fabf7ULL, + 0x00000000004d93c7ULL, + 0xffffffffe3dac499ULL, + 0xffffffff9fe1d547ULL, + 0xffffffff9b4ca0e5ULL, /* 72 */ + 0xffffffffe32e98d5ULL, + 0x0000000000b9cf8bULL, + 0x000000007c82de55ULL, + 0xffffffffe7904f52ULL, + 0xffffffff9ff27762ULL, + 0x000000007c65203cULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c new file mode 100644 index 0000000..1439d44 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CB + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CB"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0xffffffffad7d5351ULL, + 0x00000000647e6465ULL, + 0xffffffffc9fcc8cbULL, + 0x00000000237f7689ULL, + 0xffffffff8efdda27ULL, + 0xffffffff837defedULL, + 0x000000002eff4343ULL, + 0xffffffffad82acaeULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffffc9033734ULL, + 0x0000000064819b9aULL, + 0xffffffff8e0225d8ULL, + 0x0000000023808976ULL, + 0x000000002e00bcbcULL, + 0xffffffff83821012ULL, + 0x00000000642b3130ULL, /* 16 */ + 0xffffffffc9a99d9eULL, + 0x0000000000aaaaaaULL, + 0xffffffffad280604ULL, + 0x0000000047abb846ULL, + 0xffffffffea2914e8ULL, + 0xffffffffe7a92122ULL, + 0x000000004a2b8d8cULL, + 0xffffffffc9566261ULL, /* 24 */ + 0x0000000064d4cecfULL, + 0xffffffffadd7f9fbULL, + 0x0000000000555555ULL, + 0xffffffffead6eb17ULL, + 0x00000000475447b9ULL, + 0x000000004ad47273ULL, + 0xffffffffe756deddULL, + 0x00000000234c45baULL, /* 32 */ + 0xffffffff8ecee914ULL, + 0x0000000047cdde20ULL, + 0xffffffffea4f728eULL, + 0x0000000000ccccccULL, + 0xffffffffad4e6062ULL, + 0xffffffffa0ce55a8ULL, + 0x000000000d4cf906ULL, + 0xffffffff8e3116ebULL, /* 40 */ + 0x0000000023b3ba45ULL, + 0xffffffffeab08d71ULL, + 0x00000000473221dfULL, + 0xffffffffadb19f9dULL, + 0x0000000000333333ULL, + 0x000000000db306f9ULL, + 0xffffffffa031aa57ULL, + 0xffffffff830c28f1ULL, /* 48 */ + 0x000000002e8e845fULL, + 0xffffffffe78db36bULL, + 0x000000004a0f1fc5ULL, + 0xffffffffa08ca187ULL, + 0x000000000d0e0d29ULL, + 0x00000000008e38e3ULL, + 0xffffffffad0c944dULL, + 0x000000002e717ba0ULL, /* 56 */ + 0xffffffff83f3d70eULL, + 0x000000004af0e03aULL, + 0xffffffffe7724c94ULL, + 0x000000000df1f2d6ULL, + 0xffffffffa0735e78ULL, + 0xffffffffadf36bb2ULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0xffffffffcbefd6b4ULL, + 0xffffffffc334e94fULL, + 0xffffffffac268ec5ULL, + 0xffffffffcb8a2726ULL, + 0x00000000004d93c7ULL, + 0x000000000896ac3cULL, + 0x000000006784cbb6ULL, + 0xffffffffc3a54491ULL, /* 72 */ + 0x000000000862f070ULL, + 0x0000000000b9cf8bULL, + 0x000000006faba801ULL, + 0xffffffffac50dd72ULL, + 0x0000000067976993ULL, + 0x000000006f4c5668ULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c new file mode 100644 index 0000000..bf258e0 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CD + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CD"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffb798b438ULL, /* 0 */ + 0xffffffffc44ff94dULL, + 0xffffffff992a70ebULL, + 0xffffffffeafd3d9eULL, + 0x000000005152da26ULL, + 0x0000000022859753ULL, + 0x0000000015cb6d32ULL, + 0x00000000661c2047ULL, + 0x0000000073d74d75ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005d6589a6ULL, + 0x000000002eb2c4d3ULL, + 0xffffffff951d236bULL, + 0xffffffffe6ca6e1eULL, + 0xffffffffd184947fULL, + 0xffffffffa253d90aULL, + 0x0000000008f9ceacULL, /* 16 */ + 0x000000007b2e83d9ULL, + 0x00000000264b0a7fULL, + 0x00000000559c470aULL, + 0xffffffffee33a0b2ULL, + 0xffffffff9de4edc7ULL, + 0xffffffffaaaa17a6ULL, + 0xffffffffd97d5ad3ULL, + 0xffffffffccb637e1ULL, /* 24 */ + 0xffffffffbf617a94ULL, + 0xffffffffe204f332ULL, + 0xffffffff91d3be47ULL, + 0x000000002a7c59ffULL, + 0x0000000059ab148aULL, + 0x000000006ee5eeebULL, + 0x000000001d32a39eULL, + 0x0000000021e3b01bULL, /* 32 */ + 0x000000005234fd6eULL, + 0x000000000f5174c8ULL, + 0x000000007c8639bdULL, + 0xffffffffc729de05ULL, + 0xffffffffb4fe9370ULL, + 0xffffffff83b06911ULL, + 0xfffffffff0672464ULL, + 0xffffffffe5ac4956ULL, /* 40 */ + 0xffffffff967b0423ULL, + 0xffffffffcb1e8d85ULL, + 0xffffffffb8c9c0f0ULL, + 0x0000000003662748ULL, + 0x0000000070b16a3dULL, + 0x0000000047ff905cULL, + 0x000000003428dd29ULL, + 0xffffffffb89d59a6ULL, /* 48 */ + 0xffffffffcb4a14d3ULL, + 0xffffffff962f9d75ULL, + 0xffffffffe5f8d000ULL, + 0x000000005e5737b8ULL, + 0x000000002d807acdULL, + 0x000000001ace80acULL, + 0x000000006919cdd9ULL, + 0x000000007cd2a0ebULL, /* 56 */ + 0x000000000f05ed9eULL, + 0x0000000052606438ULL, + 0x0000000021b7294dULL, + 0xffffffff9a18cef5ULL, + 0xffffffffe9cf8380ULL, + 0xffffffffde8179e1ULL, + 0xffffffffad563494ULL, + 0x000000003a358bb3ULL, /* 64 */ + 0xffffffff975446ebULL, + 0x0000000041d37ad6ULL, + 0x000000004be84fe1ULL, + 0xffffffff9671b1b3ULL, + 0x000000003b107cebULL, + 0xffffffffed9740d6ULL, + 0xffffffffe7ac75e1ULL, + 0xffffffffa1489696ULL, /* 72 */ + 0x000000000c295bceULL, + 0xffffffffdaae67f3ULL, + 0xffffffffd09552c4ULL, + 0x0000000042bd7071ULL, + 0xffffffffefdcbd29ULL, + 0x00000000395b8114ULL, + 0x000000003360b423ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c new file mode 100644 index 0000000..0e7b677 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CH + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CH"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0x000000000e9e77d2ULL, + 0xfffffffff92eaa4bULL, + 0xfffffffff7b02266ULL, + 0x00000000571acc93ULL, + 0x00000000598444beULL, + 0xfffffffff1e6ca77ULL, + 0xffffffffff78425aULL, + 0x000000000e9e882dULL, /* 8 */ + 0x0000000000000000ULL, + 0xfffffffff7b0dd99ULL, + 0xfffffffff92e55b4ULL, + 0x000000005984bb41ULL, + 0x00000000571a336cULL, + 0xffffffffff78bda5ULL, + 0xfffffffff1e63588ULL, + 0xfffffffff92eff1eULL, /* 16 */ + 0xfffffffff7b07733ULL, + 0x000000000000aaaaULL, + 0x000000000e9e2287ULL, + 0xffffffffae34cc72ULL, + 0xffffffffa0aa445fULL, + 0x0000000008c8ca96ULL, + 0x00000000065642bbULL, + 0xfffffffff7b088ccULL, /* 24 */ + 0xfffffffff92e00e1ULL, + 0x000000000e9edd78ULL, + 0x0000000000005555ULL, + 0xffffffffa0aabba0ULL, + 0xffffffffae34338dULL, + 0x000000000656bd44ULL, + 0x0000000008c83569ULL, + 0x00000000571affa0ULL, /* 32 */ + 0x000000005984778dULL, + 0xffffffffae34aa14ULL, + 0xffffffffa0aa2239ULL, + 0x000000000000ccccULL, + 0x000000000e9e44e1ULL, + 0xffffffffa6fcca28ULL, + 0xffffffffa8624205ULL, + 0x0000000059848872ULL, /* 40 */ + 0x00000000571a005fULL, + 0xffffffffa0aaddc6ULL, + 0xffffffffae3455ebULL, + 0x000000000e9ebb1eULL, + 0x0000000000003333ULL, + 0xffffffffa862bdfaULL, + 0xffffffffa6fc35d7ULL, + 0xfffffffff1e6bbb0ULL, /* 48 */ + 0xffffffffff78339dULL, + 0x0000000008c8ee04ULL, + 0x0000000006566629ULL, + 0xffffffffa6fc88dcULL, + 0xffffffffa86200f1ULL, + 0x0000000000008e38ULL, + 0x000000000e9e0615ULL, + 0xffffffffff78cc62ULL, /* 56 */ + 0xfffffffff1e6444fULL, + 0x00000000065699d6ULL, + 0x0000000008c811fbULL, + 0xffffffffa862ff0eULL, + 0xffffffffa6fc7723ULL, + 0x000000000e9ef9eaULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x000000001190c4cfULL, + 0x000000007b7fdbbeULL, + 0xffffffff9204da99ULL, + 0x000000001190a13eULL, + 0x0000000000004d93ULL, + 0x000000006aef52e2ULL, + 0xffffffff839453c5ULL, + 0x000000007b7f4a13ULL, /* 72 */ + 0x000000006aefa6beULL, + 0x000000000000b9cfULL, + 0xffffffffe97bb8e8ULL, + 0xffffffff9204accaULL, + 0xffffffff83944067ULL, + 0xffffffffe97b5f16ULL, + 0x0000000000005e31ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c new file mode 100644 index 0000000..f7110b3 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CW + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CW"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffb798b438ULL, + 0xffffffff91d3be47ULL, + 0x00000000264b0a7fULL, + 0x0000000070b16a3dULL, + 0xffffffffc729de05ULL, + 0x0000000063c5950aULL, + 0xffffffffd45d2132ULL, + 0xffffffffb798b438ULL, /* 8 */ + 0x0000000000000000ULL, + 0x00000000264b0a7fULL, + 0xffffffff91d3be47ULL, + 0xffffffffc729de05ULL, + 0x0000000070b16a3dULL, + 0xffffffffd45d2132ULL, + 0x0000000063c5950aULL, + 0xffffffff91d3be47ULL, /* 16 */ + 0x00000000264b0a7fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x00000000264b0a7fULL, /* 24 */ + 0xffffffff91d3be47ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0x0000000070b16a3dULL, /* 32 */ + 0xffffffffc729de05ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0xffffffffc729de05ULL, /* 40 */ + 0x0000000070b16a3dULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0x0000000063c5950aULL, /* 48 */ + 0xffffffffd45d2132ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffd45d2132ULL, /* 56 */ + 0x0000000063c5950aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffffea0755b2ULL, + 0x0000000008b188e6ULL, + 0xffffffffff3cc8d9ULL, + 0xffffffffea0755b2ULL, + 0x0000000000000000ULL, + 0xffffffffe2b6dd54ULL, + 0x00000000153b9d6bULL, + 0x0000000008b188e6ULL, /* 72 */ + 0xffffffffe2b6dd54ULL, + 0x0000000000000000ULL, + 0xfffffffff78d403fULL, + 0xffffffffff3cc8d9ULL, + 0x00000000153b9d6bULL, + 0xfffffffff78d403fULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c new file mode 100644 index 0000000..e391be8 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffdebb20e3ULL, /* 0 */ + 0x0000000044660075ULL, + 0x000000001e20c2aeULL, + 0xffffffff84fde238ULL, + 0x00000000281d7ce7ULL, + 0xffffffffb2c05c71ULL, + 0xffffffffd660a024ULL, + 0x000000004cbd80b2ULL, + 0xffffffff9add2096ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005a46c2dbULL, + 0xffffffffc09be24dULL, + 0x000000006c7b7c92ULL, + 0xfffffffff6a65c04ULL, + 0xffffffff9206a051ULL, + 0x0000000008db80c7ULL, + 0x000000005449dd0fULL, /* 16 */ + 0xffffffffce94fd99ULL, + 0xffffffff94d23f42ULL, + 0x000000000e0f1fd4ULL, + 0xffffffffa2ef810bULL, + 0x000000003832a19dULL, + 0x000000005c925dc8ULL, + 0xffffffffc64f7d5eULL, + 0x00000000102fdd7aULL, /* 24 */ + 0xffffffff8af2fdecULL, + 0xffffffffd0b43f37ULL, + 0x000000004a691fa1ULL, + 0xffffffffe689817eULL, + 0x000000007c54a1e8ULL, + 0x0000000018f45dbdULL, + 0xffffffff82297d2bULL, + 0xffffffffa7157447ULL, /* 32 */ + 0x000000003dc854d1ULL, + 0x00000000678e960aULL, + 0xfffffffffd53b69cULL, + 0x0000000051b32843ULL, + 0xffffffffcb6e08d5ULL, + 0xffffffffafcef480ULL, + 0x000000003513d416ULL, + 0xffffffffe3737432ULL, /* 40 */ + 0x0000000079ae54a4ULL, + 0x0000000023e8967fULL, + 0xffffffffb935b6e9ULL, + 0x0000000015d52836ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffeba8f4f5ULL, + 0x000000007175d463ULL, + 0x000000007a6adc3eULL, /* 48 */ + 0xffffffffe0b7fca8ULL, + 0xffffffffbaf13e73ULL, + 0x00000000202c1ee5ULL, + 0xffffffff8ccc803aULL, + 0x000000001611a0acULL, + 0x0000000072b15cf9ULL, + 0xffffffffe86c7c6fULL, + 0x000000003e0cdc4bULL, /* 56 */ + 0xffffffffa4d1fcddULL, + 0xfffffffffe973e06ULL, + 0x00000000644a1e90ULL, + 0xffffffffc8aa804fULL, + 0x000000005277a0d9ULL, + 0x0000000036d75c8cULL, + 0xffffffffac0a7c1aULL, + 0xffffffffed857593ULL, /* 64 */ + 0xffffffffe0b6f95fULL, + 0x00000000253b462cULL, + 0xffffffffe15579b9ULL, + 0x0000000074897c83ULL, + 0x0000000079baf04fULL, + 0xffffffffbc374f3cULL, + 0x00000000785970a9ULL, + 0xffffffffa6bae0a9ULL, /* 72 */ + 0xffffffffab896c65ULL, + 0x000000006e04d316ULL, + 0xffffffffaa6aec83ULL, + 0x000000005ae171feULL, + 0x0000000057d2fd32ULL, + 0xffffffff925f4241ULL, + 0x0000000056317dd4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c new file mode 100644 index 0000000..100f02c --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0xffffffffbe2612ffULL, + 0xffffffffdccda6c0ULL, + 0x0000000062eb4bc0ULL, + 0x000000004bbbc8eaULL, + 0xfffffffff59d25eaULL, + 0x0000000022259ac0ULL, + 0xffffffff9c0377c0ULL, + 0xffffffffbe26ed00ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000062ebb43fULL, + 0xffffffffdccd593fULL, + 0xfffffffff59dda15ULL, + 0x000000004bbb3715ULL, + 0xffffffff9c03883fULL, + 0x000000002225653fULL, + 0xffffffffdccdf395ULL, /* 16 */ + 0x0000000062eb1e95ULL, + 0x000000000000aaaaULL, + 0xffffffffbe2647aaULL, + 0xffffffff9776c480ULL, + 0x0000000029502980ULL, + 0xfffffffffee896aaULL, + 0x0000000040ce7baaULL, + 0x0000000062ebe16aULL, /* 24 */ + 0xffffffffdccd0c6aULL, + 0xffffffffbe26b855ULL, + 0x0000000000005555ULL, + 0x000000002950d67fULL, + 0xffffffff97763b7fULL, + 0x0000000040ce8455ULL, + 0xfffffffffee86955ULL, + 0x000000004bbbfbd9ULL, /* 32 */ + 0xfffffffff59d16d9ULL, + 0xffffffff9776a2e6ULL, + 0x0000000029504fe6ULL, + 0x000000000000ccccULL, + 0xffffffffbe2621ccULL, + 0x00000000699e9ee6ULL, + 0xffffffffd7b873e6ULL, + 0xfffffffff59de926ULL, /* 40 */ + 0x000000004bbb0426ULL, + 0x000000002950b019ULL, + 0xffffffff97765d19ULL, + 0xffffffffbe26de33ULL, + 0x0000000000003333ULL, + 0xffffffffd7b88c19ULL, + 0x00000000699e6119ULL, + 0x000000002225eb07ULL, /* 48 */ + 0xffffffff9c030607ULL, + 0xfffffffffee8b238ULL, + 0x0000000040ce5f38ULL, + 0x00000000699edc12ULL, + 0xffffffffd7b83112ULL, + 0x0000000000008e38ULL, + 0xffffffffbe266338ULL, + 0xffffffff9c03f9f8ULL, /* 56 */ + 0x00000000222514f8ULL, + 0x0000000040cea0c7ULL, + 0xfffffffffee84dc7ULL, + 0xffffffffd7b8ceedULL, + 0x00000000699e23edULL, + 0xffffffffbe269cc7ULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x0000000026a17af6ULL, + 0xffffffffaa919152ULL, + 0xffffffffcb865590ULL, + 0x0000000026a11f07ULL, + 0x0000000000004d93ULL, + 0xffffffff8c30a637ULL, + 0xffffffffed2762f5ULL, + 0xffffffffaa9100ffULL, /* 72 */ + 0xffffffff8c30526bULL, + 0x000000000000b9cfULL, + 0x0000000061177d0dULL, + 0xffffffffcb8623c3ULL, + 0xffffffffed277157ULL, + 0x0000000061179af3ULL, + 0x0000000000005e31ULL + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c new file mode 100644 index 0000000..b4f5f4b --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic <amarkovic@wavecomp.com> + * Copyright (C) 2025 Aleksandar Rakic <aleksandar.rakic@htecgroup.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <sys/time.h> +#include <stdint.h> + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffdebb20e3ULL, + 0x000000004a691fa1ULL, + 0xffffffff94d23f42ULL, + 0xffffffff8f0808a0ULL, + 0x0000000051b32843ULL, + 0x0000000065069dceULL, + 0xffffffffbbbdbd2dULL, + 0xffffffffdebb20e3ULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffff94d23f42ULL, + 0x000000004a691fa1ULL, + 0x0000000051b32843ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffbbbdbd2dULL, + 0x0000000065069dceULL, + 0x000000004a691fa1ULL, /* 16 */ + 0xffffffff94d23f42ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffff94d23f42ULL, /* 24 */ + 0x000000004a691fa1ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0xffffffff8f0808a0ULL, /* 32 */ + 0x0000000051b32843ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000051b32843ULL, /* 40 */ + 0xffffffff8f0808a0ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0x0000000065069dceULL, /* 48 */ + 0xffffffffbbbdbd2dULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffbbbdbd2dULL, /* 56 */ + 0x0000000065069dceULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffff90485967ULL, + 0x000000006dfb974aULL, + 0x00000000083e4538ULL, + 0xffffffff90485967ULL, + 0x0000000000000000ULL, + 0xfffffffffdb3ce2dULL, + 0xffffffff98761c5fULL, + 0x000000006dfb974aULL, /* 72 */ + 0xfffffffffdb3ce2dULL, + 0x0000000000000000ULL, + 0x0000000065c5d272ULL, + 0x00000000083e4538ULL, + 0xffffffff98761c5fULL, + 0x0000000065c5d272ULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 45c9cfe..38345ff 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -29,6 +29,7 @@ run-float_%: float_% $(call run-test,$<, $(QEMU) $(QEMU_OPTS) $<) $(call conditional-diff-out,$<,$(SRC_PATH)/tests/tcg/$(TARGET_NAME)/$<.ref) +fnmsub: LDFLAGS+=-lm testthread: LDFLAGS+=-lpthread @@ -188,6 +189,10 @@ run-plugin-semiconsole-with-%: TESTS += semihosting semiconsole endif +test-plugin-mem-access: CFLAGS+=-pthread -O0 +test-plugin-mem-access: LDFLAGS+=-pthread -O0 + +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation run-plugin-test-plugin-mem-access-with-libmem.so: \ PLUGIN_ARGS=$(COMMA)print-accesses=true @@ -196,8 +201,8 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \ $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ $(QEMU) $< -test-plugin-mem-access: CFLAGS+=-pthread -O0 -test-plugin-mem-access: LDFLAGS+=-pthread -O0 +EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so +endif # Update TESTS TESTS += $(MULTIARCH_TESTS) diff --git a/tests/tcg/multiarch/fnmsub.c b/tests/tcg/multiarch/fnmsub.c new file mode 100644 index 0000000..15dd41d --- /dev/null +++ b/tests/tcg/multiarch/fnmsub.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdio.h> +#include <math.h> +#include <fenv.h> + +union U { + double d; + unsigned long long l; +}; + +union U x = { .l = 0x4ff0000000000000ULL }; +union U y = { .l = 0x2ff0000000000000ULL }; +union U r; + +int main() +{ +#ifdef FE_DOWNWARD + fesetround(FE_DOWNWARD); + +#if defined(__loongarch__) + asm("fnmsub.d %0, %1, %1, %2" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#elif defined(__powerpc64__) + asm("fnmsub %0,%1,%1,%2" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#elif defined(__s390x__) && 0 /* need -march=z14 */ + asm("vfnms %0,%1,%1,%2,0,3" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#else + r.d = -fma(x.d, x.d, -y.d); +#endif + + if (r.l != 0xdfefffffffffffffULL) { + printf("r = %.18a (%016llx)\n", r.d, r.l); + return 1; + } +#endif + return 0; +} diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 07be001..4171b4e 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -6,6 +6,11 @@ # architecture to add to the test dependencies and deal with the # complications of building. # +# To support the multiarch guests the target arch needs to provide a +# boot.S that jumps to main and provides a __sys_outc functions. +# Remember to update MULTIARCH_SOFTMMU_TARGETS in the tcg test +# Makefile.target when this is done. +# MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch MULTIARCH_SYSTEM_SRC=$(MULTIARCH_SRC)/system @@ -66,8 +71,11 @@ endif MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ run-gdbstub-untimely-packet run-gdbstub-registers +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation -run-plugin-memory-with-libmem.so: \ - PLUGIN_ARGS=$(COMMA)region-summary=true -run-plugin-memory-with-libmem.so: \ - CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out +run-plugin-memory-with-libmem.so: memory libmem.so +run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true +run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out + +EXTRA_RUNS += run-plugin-memory-with-libmem.so +endif diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index d87d662..9649bce 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -20,6 +20,7 @@ * few things provided by compiler.h. */ #include <compiler.h> +#include <stdbool.h> #include <bswap.h> #include <qemu-plugin.h> @@ -67,7 +68,7 @@ static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; static GMutex lock; static GHashTable *regions; -static gint addr_order(gconstpointer a, gconstpointer b) +static gint addr_order(gconstpointer a, gconstpointer b, gpointer d) { RegionInfo *na = (RegionInfo *) a; RegionInfo *nb = (RegionInfo *) b; @@ -94,7 +95,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (do_region_summary) { GList *counts = g_hash_table_get_values(regions); - counts = g_list_sort(counts, addr_order); + counts = g_list_sort_with_data(counts, addr_order, NULL); g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index c8cb062..61a007d 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,6 +1,6 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', @@ -17,7 +17,7 @@ endif if t.length() > 0 alias_target('test-plugins', t) else - run_target('test-plugins', command: find_program('true')) + run_target('test-plugins', command: [python, '-c', '']) endif plugin_modules += t diff --git a/tests/tcg/plugins/patch.c b/tests/tcg/plugins/patch.c new file mode 100644 index 0000000..111c5c1 --- /dev/null +++ b/tests/tcg/plugins/patch.c @@ -0,0 +1,251 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This plugin patches instructions matching a pattern to a different + * instruction as they execute + * + */ + +#include "glib.h" +#include "glibconfig.h" + +#include <qemu-plugin.h> +#include <string.h> +#include <stdio.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static bool use_hwaddr; +static GByteArray *target_data; +static GByteArray *patch_data; + +/** + * Parse a string of hexadecimal digits into a GByteArray. The string must be + * even length + */ +static GByteArray *str_to_bytes(const char *str) +{ + size_t len = strlen(str); + + if (len == 0 || len % 2 != 0) { + return NULL; + } + + GByteArray *bytes = g_byte_array_new(); + char byte[3] = {0}; + guint8 value = 0; + + for (size_t i = 0; i < len; i += 2) { + byte[0] = str[i]; + byte[1] = str[i + 1]; + value = (guint8)g_ascii_strtoull(byte, NULL, 16); + g_byte_array_append(bytes, &value, 1); + } + + return bytes; +} + +static void patch_hwaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR "\n", + addr); + qemu_plugin_outs(str->str); + + enum qemu_plugin_hwaddr_operation_result result = + qemu_plugin_write_memory_hwaddr(addr, patch_data); + + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to write memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + GByteArray *read_data = g_byte_array_new(); + + result = qemu_plugin_read_memory_hwaddr(addr, read_data, + patch_data->len); + + qemu_plugin_outs("Reading memory...\n"); + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to read memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +static void patch_vaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(addr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + return; + } + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR " hw: @0x%" PRIx64 "\n", + addr, hwaddr); + qemu_plugin_outs(str->str); + + qemu_plugin_outs("Writing memory (vaddr)...\n"); + + if (!qemu_plugin_write_memory_vaddr(addr, patch_data)) { + qemu_plugin_outs("Failed to write memory\n"); + return; + } + + qemu_plugin_outs("Reading memory (vaddr)...\n"); + + g_autoptr(GByteArray) read_data = g_byte_array_new(); + + if (!qemu_plugin_read_memory_vaddr(addr, read_data, patch_data->len)) { + qemu_plugin_outs("Failed to read memory\n"); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +/* + * Callback on translation of a translation block. + */ +static void vcpu_tb_trans_cb(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_autoptr(GByteArray) insn_data = g_byte_array_new(); + uintptr_t addr = 0; + + for (size_t i = 0; i < qemu_plugin_tb_n_insns(tb); i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + uint64_t vaddr = qemu_plugin_insn_vaddr(insn); + + if (use_hwaddr) { + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(vaddr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + continue; + } + /* + * As we cannot emulate 64 bit systems on 32 bit hosts we + * should never see the top bits set, hence we can safely + * cast to uintptr_t. + */ + g_assert(hwaddr <= UINTPTR_MAX); + addr = (uintptr_t) hwaddr; + } else { + g_assert(vaddr <= UINTPTR_MAX); + addr = (uintptr_t) vaddr; + } + + g_byte_array_set_size(insn_data, qemu_plugin_insn_size(insn)); + qemu_plugin_insn_data(insn, insn_data->data, insn_data->len); + + if (insn_data->len >= target_data->len && + !memcmp(insn_data->data, target_data->data, + MIN(target_data->len, insn_data->len))) { + if (use_hwaddr) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_hwaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } else { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_vaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } + } + } +} + +static void usage(void) +{ + fprintf(stderr, "Usage: <lib>,target=<bytes>,patch=<new_bytes>" + "[,use_hwaddr=true|false]"); +} + +/* + * Called when the plugin is installed + */ +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + + use_hwaddr = true; + target_data = NULL; + patch_data = NULL; + + if (argc > 4) { + usage(); + return -1; + } + + for (size_t i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "use_hwaddr") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &use_hwaddr)) { + fprintf(stderr, + "Failed to parse boolean argument use_hwaddr\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "target") == 0) { + target_data = str_to_bytes(tokens[1]); + if (!target_data) { + fprintf(stderr, + "Failed to parse target bytes.\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "patch") == 0) { + patch_data = str_to_bytes(tokens[1]); + if (!patch_data) { + fprintf(stderr, "Failed to parse patch bytes.\n"); + return -1; + } + } else { + fprintf(stderr, "Unknown argument: %s\n", tokens[0]); + usage(); + return -1; + } + } + + if (!target_data) { + fprintf(stderr, "target argument is required\n"); + usage(); + return -1; + } + + if (!patch_data) { + fprintf(stderr, "patch argument is required\n"); + usage(); + return -1; + } + + if (target_data->len != patch_data->len) { + fprintf(stderr, "Target and patch data must be the same length\n"); + return -1; + } + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans_cb); + + return 0; +} diff --git a/tests/tcg/plugins/reset.c b/tests/tcg/plugins/reset.c new file mode 100644 index 0000000..1be8be2 --- /dev/null +++ b/tests/tcg/plugins/reset.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Linaro Ltd + * + * Test the reset/uninstall cycle of a plugin. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <glib.h> + +#include <qemu-plugin.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +static qemu_plugin_id_t plugin_id; +static bool was_reset; +static bool was_uninstalled; + +static void after_uninstall(qemu_plugin_id_t id) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_outs("uninstall done\n"); + was_uninstalled = true; +} + +static void tb_exec_after_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_uninstall(plugin_id, after_uninstall); +} + +static void tb_trans_after_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_after_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +static void after_reset(qemu_plugin_id_t id) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_outs("reset done\n"); + was_reset = true; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_after_reset); +} + +static void tb_exec_before_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_reset(plugin_id, after_reset); +} + +static void tb_trans_before_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_before_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + plugin_id = id; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_before_reset); + return 0; +} + +/* Since we uninstall the plugin, we can't use qemu_plugin_register_atexit_cb, + * so we use destructor attribute instead. */ +static void __attribute__((destructor)) on_plugin_exit(void) +{ + g_assert(was_reset && was_uninstalled); + qemu_plugin_outs("plugin exit\n"); +} diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c index 47aad55..42801f5 100644 --- a/tests/tcg/plugins/syscall.c +++ b/tests/tcg/plugins/syscall.c @@ -180,7 +180,7 @@ static void print_entry(gpointer val, gpointer user_data) qemu_plugin_outs(out); } -static gint comp_func(gconstpointer ea, gconstpointer eb) +static gint comp_func(gconstpointer ea, gconstpointer eb, gpointer d) { SyscallStats *ent_a = (SyscallStats *) ea; SyscallStats *ent_b = (SyscallStats *) eb; @@ -197,7 +197,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) g_mutex_lock(&lock); GList *entries = g_hash_table_get_values(statistics); - entries = g_list_sort(entries, comp_func); + entries = g_list_sort_with_data(entries, comp_func, NULL); qemu_plugin_outs("syscall no. calls errors\n"); g_list_foreach(entries, print_entry, NULL); diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index 7c1d44d..3ca5953 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -20,5 +20,9 @@ EXTRA_RUNS += run-issue1060 run-issue1060: issue1060 $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-mepc-masking +run-test-mepc-masking: test-mepc-masking + $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-mepc-masking.S b/tests/tcg/riscv64/test-mepc-masking.S new file mode 100644 index 0000000..fccd2a7 --- /dev/null +++ b/tests/tcg/riscv64/test-mepc-masking.S @@ -0,0 +1,73 @@ +/* + * Test for MEPC masking bug fix + * + * This test verifies that MEPC properly masks the lower bits according + * to the RISC-V specification when vectored mode bits from STVEC are + * written to MEPC. + */ + + .option norvc + + .text + .global _start +_start: + /* Set up machine trap vector */ + lla t0, machine_trap_handler + csrw mtvec, t0 + + /* Set STVEC with vectored mode (mode bits = 01) */ + li t0, 0x80004001 + csrw stvec, t0 + + /* Clear medeleg to handle exceptions in M-mode */ + csrw medeleg, zero + + /* Trigger illegal instruction exception */ + .word 0xffffffff + +test_completed: + /* Exit with result in a0 */ + /* a0 = 0: success (bits [1:0] were masked) */ + /* a0 != 0: failure (some bits were not masked) */ + j _exit + +machine_trap_handler: + /* Check if illegal instruction (mcause = 2) */ + csrr t0, mcause + li t1, 2 + bne t0, t1, skip_test + + /* Test: Copy STVEC (with mode bits) to MEPC */ + csrr t0, stvec /* t0 = 0x80004001 */ + csrw mepc, t0 /* Write to MEPC */ + csrr t1, mepc /* Read back MEPC */ + + /* Check if bits [1:0] are masked (IALIGN=32 without RVC) */ + andi a0, t1, 3 /* a0 = 0 if both bits masked correctly */ + + /* Set correct return address */ + lla t0, test_completed + csrw mepc, t0 + +skip_test: + mret + +/* Exit with semihosting */ +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 8 +semiargs: + .space 16 diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index ef6bcb4..3e30ca9 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -1,13 +1,11 @@ # -# x86 system tests -# -# This currently builds only for i386. The common C code is built -# with standard compiler flags however so we can support both by -# adding additional boot files for x86_64. +# x86_64 system tests # -I386_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/i386/system X64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/x86_64/system +X64_SYSTEM_TESTS=$(patsubst $(X64_SYSTEM_SRC)/%.c, %, $(wildcard $(X64_SYSTEM_SRC)/*.c)) + +VPATH+=$(X64_SYSTEM_SRC) # These objects provide the basic boot code and helper functions for all tests CRT_OBJS=boot.o @@ -18,7 +16,7 @@ LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_x86_64 CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc -TESTS+=$(MULTIARCH_TESTS) +TESTS+=$(MULTIARCH_TESTS) $(X64_SYSTEM_TESTS) EXTRA_RUNS+=$(MULTIARCH_RUNS) # building head blobs @@ -35,3 +33,12 @@ memory: CFLAGS+=-DCHECK_UNALIGNED=1 # Running QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel + +ifeq ($(CONFIG_PLUGIN),y) +run-plugin-patch-target-with-libpatch.so: \ + PLUGIN_ARGS=$(COMMA)target=ffc0$(COMMA)patch=9090$(COMMA)use_hwaddr=true +run-plugin-patch-target-with-libpatch.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(X64_SYSTEM_SRC)/validate-patch.py $@.out +run-plugin-patch-target-with-libpatch.so: patch-target libpatch.so +EXTRA_RUNS+=run-plugin-patch-target-with-libpatch.so +endif diff --git a/tests/tcg/x86_64/fma.c b/tests/tcg/x86_64/fma.c index 09c622e..3421961 100644 --- a/tests/tcg/x86_64/fma.c +++ b/tests/tcg/x86_64/fma.c @@ -79,14 +79,21 @@ static testdata tests[] = { /* * Flushing of denormal outputs to zero should also happen after * rounding, so setting FTZ should not affect the result or the flags. - * QEMU currently does not emulate this correctly because we do the - * flush-to-zero check before rounding, so we incorrectly produce a - * zero result and set Underflow as well as Precision. */ -#ifdef ENABLE_FAILING_TESTS { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ -#endif + /* + * normal * 0 + a denormal. With FTZ disabled this gives an exact + * result (equal to the input denormal) that has consumed the denormal. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, false, + 0x8008000000000000, 0x2 }, /* Denormal */ + /* + * With FTZ enabled, this consumes the denormal, returns zero (because + * flushed) and indicates also Underflow and Precision. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, true, + 0x8000000000000000, 0x32 }, /* Precision, Underflow, Denormal */ }; int main(void) diff --git a/tests/tcg/x86_64/system/patch-target.c b/tests/tcg/x86_64/system/patch-target.c new file mode 100644 index 0000000..8c2b6f4 --- /dev/null +++ b/tests/tcg/x86_64/system/patch-target.c @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This test target increments a value 100 times. The patcher converts the + * inc instruction to a nop, so it only increments the value once. + * + */ +#include <minilib.h> + +int main(void) +{ + ml_printf("Running test...\n"); + unsigned int x = 0; + for (int i = 0; i < 100; i++) { + asm volatile ( + "inc %[x]" + : [x] "+a" (x) + ); + } + ml_printf("Value: %d\n", x); + return 0; +} diff --git a/tests/tcg/x86_64/system/validate-patch.py b/tests/tcg/x86_64/system/validate-patch.py new file mode 100755 index 0000000..700950e --- /dev/null +++ b/tests/tcg/x86_64/system/validate-patch.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# validate-patch.py: check the patch applies +# +# This program takes two inputs: +# - the plugin output +# - the binary output +# +# Copyright (C) 2024 +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +from argparse import ArgumentParser + +def main() -> None: + """ + Process the arguments, injest the program and plugin out and + verify they match up and report if they do not. + """ + parser = ArgumentParser(description="Validate patch") + parser.add_argument('test_output', + help="The output from the test itself") + parser.add_argument('plugin_output', + help="The output from plugin") + args = parser.parse_args() + + with open(args.test_output, 'r') as f: + test_data = f.read() + with open(args.plugin_output, 'r') as f: + plugin_data = f.read() + if "Value: 1" in test_data: + sys.exit(0) + else: + sys.exit(1) + +if __name__ == "__main__": + main() + |