On 2026/6/9 下午4:40, Huacai Chen wrote:
Hi, Bibo, On Tue, Jun 9, 2026 at 3:27 PM Bibo Mao <[email protected]> wrote:Add FPU test case and verify FPU register get and set APIs, the FPU width supports 64/128/256 bits. Signed-off-by: Bibo Mao <[email protected]> --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../kvm/include/loongarch/processor.h | 8 + .../selftests/kvm/loongarch/fpu_test.c | 145 ++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 tools/testing/selftests/kvm/loongarch/fpu_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..7d11592b3759 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -224,6 +224,7 @@ TEST_GEN_PROGS_riscv += rseq_test TEST_GEN_PROGS_riscv += steal_time TEST_GEN_PROGS_loongarch = loongarch/pmu_test +TEST_GEN_PROGS_loongarch += loongarch/fpu_test TEST_GEN_PROGS_loongarch += arch_timer TEST_GEN_PROGS_loongarch += coalesced_io_test TEST_GEN_PROGS_loongarch += demand_paging_test diff --git a/tools/testing/selftests/kvm/include/loongarch/processor.h b/tools/testing/selftests/kvm/include/loongarch/processor.h index 2324e311180f..981ae7078354 100644 --- a/tools/testing/selftests/kvm/include/loongarch/processor.h +++ b/tools/testing/selftests/kvm/include/loongarch/processor.h @@ -82,6 +82,14 @@ #define PLV_MASK 0x3 #define LOONGARCH_CSR_PRMD 0x1 #define LOONGARCH_CSR_EUEN 0x2 +#define CSR_EUEN_LBTEN_SHIFT 3 +#define CSR_EUEN_LBTEN BIT_ULL(CSR_EUEN_LBTEN_SHIFT) +#define CSR_EUEN_LASXEN_SHIFT 2 +#define CSR_EUEN_LASXEN BIT_ULL(CSR_EUEN_LASXEN_SHIFT) +#define CSR_EUEN_LSXEN_SHIFT 1 +#define CSR_EUEN_LSXEN BIT_ULL(CSR_EUEN_LSXEN_SHIFT) +#define CSR_EUEN_FPEN_SHIFT 0 +#define CSR_EUEN_FPEN BIT_ULL(CSR_EUEN_FPEN_SHIFT) #define LOONGARCH_CSR_ECFG 0x4 #define ECFGB_PMU 10 #define ECFGF_PMU (BIT_ULL(ECFGB_PMU)) diff --git a/tools/testing/selftests/kvm/loongarch/fpu_test.c b/tools/testing/selftests/kvm/loongarch/fpu_test.c new file mode 100644 index 000000000000..5bccce9db5d0 --- /dev/null +++ b/tools/testing/selftests/kvm/loongarch/fpu_test.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <string.h> +#include "kvm_util.h" +#include "processor.h" +#include "loongarch/processor.h" + +struct kvm_fpureg __aligned(64) vector = {{1, 2, 3, 4 }}; + +static void guest_code(void) +{ + unsigned long val; + struct kvm_fpureg *fp = &vector; + + val = csr_read(LOONGARCH_CSR_EUEN); + val |= CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN; + csr_write(val, LOONGARCH_CSR_EUEN); + + __asm__ __volatile__("fld.d $f0, %0, 0\n" : : "r"(fp) : "$f0"); + GUEST_SYNC(0); + + __asm__ __volatile__("vld $vr0, %0, 0\n" : : "r"(fp) : "$vr0"); + GUEST_SYNC(1); + + __asm__ __volatile__("xvld $xr0, %0, 0\n" : : "r"(fp) : "$xr0"); + GUEST_SYNC(2); + + __asm__ __volatile__("fst.d $f0, %0, 0\n" : : "r"(fp) : "memory"); + GUEST_SYNC(3); + + __asm__ __volatile__("vst $vr0, %0, 0\n" : : "r"(fp) : "memory"); + GUEST_SYNC(4); + + __asm__ __volatile__("xvst $xr0, %0, 0\n" : : "r"(fp) : "memory"); + GUEST_SYNC(5); + + GUEST_DONE(); +} + +static void run_vcpu(struct kvm_vcpu *vcpu) +{ + struct ucall uc; + int cont; + + cont = 1; + while (cont) { + vcpu_run(vcpu); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_PRINTF: + printf("%s", (const char *)uc.buffer); + break; + case UCALL_DONE: + printf("FPU test PASSED\n"); + fallthrough; + case UCALL_SYNC: + cont = 0; + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + default: + TEST_ASSERT(false, "Unexpected exit: %s", + exit_reason_str(vcpu->run->exit_reason)); + } + } +} + +static bool __vm_has_feature(struct kvm_vm *vm, int feature) +{ + int ret; + + ret = __kvm_has_device_attr(vm->fd, KVM_LOONGARCH_VM_FEAT_CTRL, feature); + return !ret; +} + + +int main(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct kvm_fpu fpu; + struct kvm_fpureg *fp = &vector; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + __TEST_REQUIRE(__vm_has_feature(vm, KVM_LOONGARCH_VM_FEAT_LSX), + "LSX not available, skipping test\n"); + __TEST_REQUIRE(__vm_has_feature(vm, KVM_LOONGARCH_VM_FEAT_LASX), + "LASX not available, skipping test\n");Directly use __kvm_has_device_attr() as the first patch, or use __vm_has_feature() in the first patch?
do you mean something like this?ret = __kvm_has_device_attr(vm->fd, KVM_LOONGARCH_VM_FEAT_CTRL, KVM_LOONGARCH_VM_FEAT_LSX);
__TEST_REQUIRE(!ret, "LSX not available, skipping test\n");
ret = __kvm_has_device_attr(vm->fd, KVM_LOONGARCH_VM_FEAT_CTRL,
KVM_LOONGARCH_VM_FEAT_LASX);
__TEST_REQUIRE(!ret, "LASX not available, skipping test\n");
I am ok with both.
There seem no unified rules with kvm selftest cases, I have no idea about this actually. There are __kvm_has_device_attr/__TEST_REQUIRE public APIs and real usages.BTW, I don't think the __ prefix is necessary.
Regards Bibo Mao
Huacai+ + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 8), "Wanted 0x%llx from f0, got 0x%llx", + fp->val64[0], fpu.fpr[0].val64[0]); + + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 16), "Wanted 0x%llx %llx from vr0, got 0x%llx %llx", + fp->val64[0], fp->val64[1], + fpu.fpr[0].val64[0], fpu.fpr[0].val64[1]); + + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 32), + "Wanted 0x%llx %llx %llx %llx from xr0, got 0x%llx %llx %llx %llx", + fp->val64[0], fp->val64[1], fp->val64[2], fp->val64[3], + fpu.fpr[0].val64[0], fpu.fpr[0].val64[1], + fpu.fpr[0].val64[2], fpu.fpr[0].val64[3]); + + fpu.fpr[0].val64[0] += random(); + vcpu_fpu_set(vcpu, &fpu); + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + sync_global_from_guest(vm, *fp); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 8), "Wanted 0x%llx from f0, got 0x%llx", + fp->val64[0], fpu.fpr[0].val64[0]); + + fpu.fpr[0].val64[0] += random(); + fpu.fpr[0].val64[1] += random(); + vcpu_fpu_set(vcpu, &fpu); + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + sync_global_from_guest(vm, *fp); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 16), "Wanted 0x%llx %llx from vr0, got 0x%llx %llx", + fp->val64[0], fp->val64[1], + fpu.fpr[0].val64[0], fpu.fpr[0].val64[1]); + + fpu.fpr[0].val64[0] += random(); + fpu.fpr[0].val64[1] += random(); + fpu.fpr[0].val64[2] += random(); + fpu.fpr[0].val64[3] += random(); + vcpu_fpu_set(vcpu, &fpu); + run_vcpu(vcpu); + vcpu_fpu_get(vcpu, &fpu); + sync_global_from_guest(vm, *fp); + TEST_ASSERT(!memcmp(fpu.fpr, fp, 32), + "Wanted 0x%llx %llx %llx %llx from xr0, got 0x%llx %llx %llx %llx", + fp->val64[0], fp->val64[1], fp->val64[2], fp->val64[3], + fpu.fpr[0].val64[0], fpu.fpr[0].val64[1], + fpu.fpr[0].val64[2], fpu.fpr[0].val64[3]); + + run_vcpu(vcpu); + kvm_vm_free(vm); + return 0; +} -- 2.39.3

