Re: [PATCH v2 2/2] KVM: LoongArch: selftests: Add FPU test case
From: Huacai Chen
Date: Tue Jun 09 2026 - 05:32:16 EST
On Tue, Jun 9, 2026 at 5:02 PM Bibo Mao <maobibo@xxxxxxxxxxx> wrote:
>
>
>
> On 2026/6/9 下午4:40, Huacai Chen wrote:
> > Hi, Bibo,
> >
> > On Tue, Jun 9, 2026 at 3:27 PM Bibo Mao <maobibo@xxxxxxxxxxx> 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 <maobibo@xxxxxxxxxxx>
> >> ---
> >> 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");
Yes, I think this is a little better, it removes the inconsistency
between two patch.
Huacai
>
> I am ok with both.
> >
> > BTW, I don't think the __ prefix is necessary.
> 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.
>
> 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
> >>
>