Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* The test validates periodic/one-shot constant timer IRQ using
* CSR.TCFG and CSR.TVAL registers.
*/
#include "arch_timer.h"
#include "kvm_util.h"
#include "processor.h"
#include "timer_test.h"
#include "ucall_common.h"
static void do_idle(void)
{
unsigned int intid;
unsigned long estat;
__asm__ __volatile__("idle 0" : : : "memory");
estat = csr_read(LOONGARCH_CSR_ESTAT);
intid = !!(estat & BIT(INT_TI));
/* Make sure pending timer IRQ arrived */
GUEST_ASSERT_EQ(intid, 1);
csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
}
static void guest_irq_handler(struct ex_regs *regs)
{
unsigned int intid;
u32 cpu = guest_get_vcpuid();
u64 xcnt, val, cfg, xcnt_diff_us;
struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
intid = !!(regs->estat & BIT(INT_TI));
/* Make sure we are dealing with the correct timer IRQ */
GUEST_ASSERT_EQ(intid, 1);
cfg = timer_get_cfg();
if (cfg & CSR_TCFG_PERIOD) {
WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1);
if (shared_data->nr_iter == 0)
disable_timer();
csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
return;
}
/*
* On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1
* On virtual machine, its value counts down from BIT_ULL(48) - 1
*/
val = timer_get_val();
xcnt = timer_get_cycles();
xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
/* Basic 'timer condition met' check */
__GUEST_ASSERT(val > cfg,
"val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx",
val, cfg, xcnt_diff_us);
csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
}
static void guest_test_period_timer(u32 cpu)
{
u32 irq_iter, config_iter;
u64 us;
struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
shared_data->nr_iter = test_args.nr_iter;
shared_data->xcnt = timer_get_cycles();
us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
timer_set_next_cmp_ms(test_args.timer_period_ms, true);
for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
/* Setup a timeout for the interrupt to arrive */
udelay(us);
}
irq_iter = READ_ONCE(shared_data->nr_iter);
__GUEST_ASSERT(irq_iter == 0,
"irq_iter = 0x%x.\n"
" Guest period timer interrupt was not triggered within the specified\n"
" interval, try to increase the error margin by [-e] option.\n",
irq_iter);
}
static void guest_test_oneshot_timer(u32 cpu)
{
u32 irq_iter, config_iter;
u64 us;
struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
shared_data->nr_iter = 0;
shared_data->guest_stage = 0;
us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
shared_data->xcnt = timer_get_cycles();
/* Setup the next interrupt */
timer_set_next_cmp_ms(test_args.timer_period_ms, false);
/* Setup a timeout for the interrupt to arrive */
udelay(us);
irq_iter = READ_ONCE(shared_data->nr_iter);
__GUEST_ASSERT(config_iter + 1 == irq_iter,
"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
" Guest timer interrupt was not triggered within the specified\n"
" interval, try to increase the error margin by [-e] option.\n",
config_iter + 1, irq_iter);
}
}
static void guest_test_emulate_timer(u32 cpu)
{
u32 config_iter;
u64 xcnt_diff_us, us;
struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
local_irq_disable();
shared_data->nr_iter = 0;
us = msecs_to_usecs(test_args.timer_period_ms);
for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
shared_data->xcnt = timer_get_cycles();
/* Setup the next interrupt */
timer_set_next_cmp_ms(test_args.timer_period_ms, false);
do_idle();
xcnt_diff_us = cycles_to_usec(timer_get_cycles() - shared_data->xcnt);
__GUEST_ASSERT(xcnt_diff_us >= us,
"xcnt_diff_us = 0x%lx, us = 0x%lx.\n",
xcnt_diff_us, us);
}
local_irq_enable();
}
static void guest_time_count_test(u32 cpu)
{
u32 config_iter;
unsigned long start, end, prev, us;
/* Assuming that test case starts to run in 1 second */
start = timer_get_cycles();
us = msec_to_cycles(1000);
__GUEST_ASSERT(start <= us,
"start = 0x%lx, us = 0x%lx.\n",
start, us);
us = msec_to_cycles(test_args.timer_period_ms);
for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
start = timer_get_cycles();
end = start + us;
/* test time count growing up always */
while (start < end) {
prev = start;
start = timer_get_cycles();
__GUEST_ASSERT(prev <= start,
"prev = 0x%lx, start = 0x%lx.\n",
prev, start);
}
}
}
static void guest_code(void)
{
u32 cpu = guest_get_vcpuid();
/* must run at first */
guest_time_count_test(cpu);
timer_irq_enable();
local_irq_enable();
guest_test_period_timer(cpu);
guest_test_oneshot_timer(cpu);
guest_test_emulate_timer(cpu);
GUEST_DONE();
}
struct kvm_vm *test_vm_create(void)
{
struct kvm_vm *vm;
int nr_vcpus = test_args.nr_vcpus;
vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
vm_init_descriptor_tables(vm);
vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);
/* Make all the test's cmdline args visible to the guest */
sync_global_to_guest(vm, test_args);
return vm;
}
void test_vm_cleanup(struct kvm_vm *vm)
{
kvm_vm_free(vm);
}
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0
/*
* LoongArch KVM PMU event counting test
*
* Test hardware event counting: CPU_CYCLES, INSTR_RETIRED,
* BRANCH_INSTRUCTIONS and BRANCH_MISSES.
*/
#include <linux/bitops.h>
#include "kvm_util.h"
#include "pmu.h"
#include "loongarch/processor.h"
static int pmu_irq_count;
/* Check PMU support */
static bool has_pmu_support(void)
{
u32 cfg6;
/* Read CPUCFG6 to check PMU */
cfg6 = read_cpucfg(LOONGARCH_CPUCFG6);
/* Check PMU present bit */
if (!(cfg6 & CPUCFG6_PMP))
return false;
/* Check that at least one counter exists */
if (((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) == 0)
return false;
return true;
}
/* Dump PMU capabilities */
static void dump_pmu_caps(void)
{
u32 cfg6;
int nr_counters, counter_bits;
cfg6 = read_cpucfg(LOONGARCH_CPUCFG6);
nr_counters = ((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) + 1;
counter_bits = ((cfg6 & CPUCFG6_PMBITS) >> CPUCFG6_PMBITS_SHIFT) + 1;
pr_info("PMU capabilities:\n");
pr_info(" Counters present: %s\n", cfg6 & CPUCFG6_PMP ? "yes" : "no");
pr_info(" Number of counters: %d\n", nr_counters);
pr_info(" Counter width: %d bits\n", counter_bits);
}
/* Guest test code - runs inside VM */
static void guest_pmu_base_test(void)
{
int i;
u32 cfg6, pmnum;
u64 cnt[4];
cfg6 = read_cpucfg(LOONGARCH_CPUCFG6);
pmnum = (cfg6 >> 4) & 0xf;
GUEST_PRINTF("CPUCFG6 = 0x%x\n", cfg6);
GUEST_PRINTF("PMP enabled: %s\n", (cfg6 & 0x1) ? "YES" : "NO");
GUEST_PRINTF("Number of counters (PMNUM): %x\n", pmnum + 1);
GUEST_ASSERT(pmnum == 3);
GUEST_PRINTF("Clean csr_perfcntr0-3\n");
csr_write(0, LOONGARCH_CSR_PERFCNTR0);
csr_write(0, LOONGARCH_CSR_PERFCNTR1);
csr_write(0, LOONGARCH_CSR_PERFCNTR2);
csr_write(0, LOONGARCH_CSR_PERFCNTR3);
GUEST_PRINTF("Set csr_perfctrl0 for cycles event\n");
csr_write(PMU_ENVENT_ENABLED |
LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0);
GUEST_PRINTF("Set csr_perfctrl1 for instr_retired event\n");
csr_write(PMU_ENVENT_ENABLED |
LOONGARCH_PMU_EVENT_INSTR_RETIRED, LOONGARCH_CSR_PERFCTRL1);
GUEST_PRINTF("Set csr_perfctrl2 for branch_instructions event\n");
csr_write(PMU_ENVENT_ENABLED |
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, LOONGARCH_CSR_PERFCTRL2);
GUEST_PRINTF("Set csr_perfctrl3 for branch_misses event\n");
csr_write(PMU_ENVENT_ENABLED |
PERF_COUNT_HW_BRANCH_MISSES, LOONGARCH_CSR_PERFCTRL3);
for (i = 0; i < NUM_LOOPS; i++)
cpu_relax();
cnt[0] = csr_read(LOONGARCH_CSR_PERFCNTR0);
GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt[0]);
cnt[1] = csr_read(LOONGARCH_CSR_PERFCNTR1);
GUEST_PRINTF("csr_perfcntr1 is %lx\n", cnt[1]);
cnt[2] = csr_read(LOONGARCH_CSR_PERFCNTR2);
GUEST_PRINTF("csr_perfcntr2 is %lx\n", cnt[2]);
cnt[3] = csr_read(LOONGARCH_CSR_PERFCNTR3);
GUEST_PRINTF("csr_perfcntr3 is %lx\n", cnt[3]);
GUEST_PRINTF("assert csr_perfcntr0 >EXPECTED_CYCLES_MIN && csr_perfcntr0 < UPPER_BOUND\n");
GUEST_ASSERT(cnt[0] > EXPECTED_CYCLES_MIN && cnt[0] < UPPER_BOUND);
GUEST_PRINTF("assert csr_perfcntr1 > EXPECTED_INSTR_MIN && csr_perfcntr1 < UPPER_BOUND\n");
GUEST_ASSERT(cnt[1] > EXPECTED_INSTR_MIN && cnt[1] < UPPER_BOUND);
GUEST_PRINTF("assert csr_perfcntr2 > 0 && csr_perfcntr2 < UPPER_BOUND\n");
GUEST_ASSERT(cnt[2] > 0 && cnt[2] < UPPER_BOUND);
GUEST_PRINTF("assert csr_perfcntr3 > 0 && csr_perfcntr3 < UPPER_BOUND\n");
GUEST_ASSERT(cnt[3] > 0 && cnt[3] < UPPER_BOUND);
}
static void guest_irq_handler(struct ex_regs *regs)
{
unsigned int intid;
pmu_irq_disable();
intid = !!(regs->estat & BIT(INT_PMI));
GUEST_ASSERT_EQ(intid, 1);
GUEST_PRINTF("Get PMU interrupt\n");
WRITE_ONCE(pmu_irq_count, pmu_irq_count + 1);
}
static void guest_pmu_interrupt_test(void)
{
u64 cnt;
csr_write(PMU_OVERFLOW - 1, LOONGARCH_CSR_PERFCNTR0);
csr_write(PMU_ENVENT_ENABLED | CSR_PERFCTRL_PMIE | LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0);
cpu_relax();
GUEST_ASSERT_EQ(pmu_irq_count, 1);
cnt = csr_read(LOONGARCH_CSR_PERFCNTR0);
GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt);
GUEST_PRINTF("PMU interrupt test success\n");
}
static void guest_code(void)
{
guest_pmu_base_test();
pmu_irq_enable();
local_irq_enable();
guest_pmu_interrupt_test();
GUEST_DONE();
}
int main(int argc, char *argv[])
{
int ret = 0;
struct kvm_device_attr attr;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
struct ucall uc;
/* Check host KVM PMU support */
if (!has_pmu_support()) {
print_skip("PMU not supported by host hardware\n");
dump_pmu_caps();
return KSFT_SKIP;
}
pr_info("Host support PMU\n");
/* Dump PMU capabilities */
dump_pmu_caps();
vm = vm_create(VM_MODE_P47V47_16K);
vcpu = vm_vcpu_add(vm, 0, guest_code);
pmu_irq_count = 0;
vm_init_descriptor_tables(vm);
loongarch_vcpu_setup(vcpu);
vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);
sync_global_to_guest(vm, pmu_irq_count);
attr.group = KVM_LOONGARCH_VM_FEAT_CTRL,
attr.attr = KVM_LOONGARCH_VM_FEAT_PMU,
ret = ioctl(vm->fd, KVM_HAS_DEVICE_ATTR, &attr);
if (ret == 0) {
pr_info("PMU is enabled in VM\n");
} else {
print_skip("PMU not enabled by VM config\n");
return KSFT_SKIP;
}
while (1) {
vcpu_run(vcpu);
switch (get_ucall(vcpu, &uc)) {
case UCALL_PRINTF:
printf("%s", (const char *)uc.buffer);
break;
case UCALL_DONE:
printf("PMU test PASSED\n");
goto done;
case UCALL_ABORT:
printf("PMU test FAILED\n");
ret = -1;
goto done;
default:
printf("Unexpected exit\n");
ret = -1;
goto done;
}
}
done:
kvm_vm_free(vm);
return ret;
}