[PATCH 2/2] KVM: LoongArch: selftests: Add in-kernel MMIO read fast path test
Zeng Chi <[email protected]> Thu, 23 Jul 2026 19:16:05 +0800
| Newsgroups | dev.linux.lists.loongarch,org.kernel.vger.kvm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Zeng Chi <[email protected]> Add a test that exercises the in-kernel MMIO read fast path of kvm_emu_mmio_read(). The guest performs an MMIO read from an in-kernel emulated PCH-PIC, immediately followed by a marker instruction, and the test verifies that the marker instruction is executed, i.e. that the PC advances by exactly one instruction instead of skipping the instruction after the MMIO read. The test skips gracefully when the host KVM cannot create a PCH-PIC device. Signed-off-by: Zeng Chi <[email protected]> --- tools/testing/selftests/kvm/Makefile.kvm | 3 +- .../selftests/kvm/loongarch/mmio_read_test.c | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/kvm/loongarch/mmio_read_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 6fc34e9bf8e1..4daf90caaabc 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -228,7 +228,8 @@ TEST_GEN_PROGS_riscv += mmu_stress_test TEST_GEN_PROGS_riscv += rseq_test TEST_GEN_PROGS_riscv += steal_time -TEST_GEN_PROGS_loongarch = loongarch/pmu_test +TEST_GEN_PROGS_loongarch = loongarch/mmio_read_test +TEST_GEN_PROGS_loongarch += loongarch/pmu_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/loongarch/mmio_read_test.c b/tools/testing/selftests/kvm/loongarch/mmio_read_test.c new file mode 100644 index 000000000000..7f64ca91ca8f --- /dev/null +++ b/tools/testing/selftests/kvm/loongarch/mmio_read_test.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LoongArch KVM in-kernel MMIO read fast path test + * + * When an MMIO read hits a device emulated inside KVM (such as the + * PCH-PIC), kvm_emu_mmio_read() completes the access without returning + * to user space and advances the guest PC by one instruction. + * + * This test issues such an in-kernel MMIO read immediately followed by + * a marker instruction and checks that the marker instruction actually + * runs, i.e. that the PC advanced by exactly one instruction (4 bytes) + * rather than skipping the instruction after the MMIO read. + */ +#include "kvm_util.h" +#include "processor.h" +#include "test_util.h" +#include "loongarch/processor.h" + +/* Physical base the in-kernel PCH-PIC is mapped at (no memslot backs it) */ +#define PCH_PIC_BASE 0x10000000UL + +static void guest_code(void) +{ + unsigned long marker = 0; + unsigned int val = 0; + + /* + * 'ld.w' faults out as an MMIO read and is emulated in kernel. + * The following 'addi.d' must execute; it is skipped if the MMIO + * read fast path advances the PC twice (by 8 instead of 4). + */ + asm volatile( + "ld.w %[val], %[base], 0\n\t" + "addi.d %[marker], %[marker], 1\n\t" + : [val] "=&r" (val), [marker] "+&r" (marker) + : [base] "r" (PCH_PIC_BASE) + : "memory"); + + GUEST_PRINTF("mmio read val=0x%x, marker=%lu\n", val, marker); + GUEST_ASSERT_EQ(marker, 1); + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + uint64_t addr = PCH_PIC_BASE; + int dev_fd; + + vm = vm_create(VM_MODE_P47V47_16K); + vcpu = vm_vcpu_add(vm, 0, guest_code); + + /* Create the in-kernel PCH-PIC and map it at PCH_PIC_BASE */ + dev_fd = __kvm_create_device(vm, KVM_DEV_TYPE_LOONGARCH_PCHPIC); + if (dev_fd < 0) { + print_skip("PCH-PIC device not supported by host KVM"); + kvm_vm_free(vm); + return KSFT_SKIP; + } + kvm_device_attr_set(dev_fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL, + KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT, &addr); + close(dev_fd); + + /* Identity-map the MMIO page so the guest can reach the device */ + virt_map(vm, PCH_PIC_BASE, PCH_PIC_BASE, 1); + + while (1) { + vcpu_run(vcpu); + switch (get_ucall(vcpu, &uc)) { + case UCALL_PRINTF: + pr_info("%s", uc.buffer); + break; + case UCALL_DONE: + goto done; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + goto done; + default: + TEST_FAIL("Unexpected exit: %s", + exit_reason_str(vcpu->run->exit_reason)); + } + } + +done: + kvm_vm_free(vm); + return 0; +} -- 2.25.1 No virus found Checked by Hillstone Network AntiVirus