Commit ea325c68 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'for-master' of https://github.com/rhdrjones/kvm-unit-tests into HEAD

arm/arm64 patches ready for master
parents e0a5cfca da905c9d
......@@ -9,10 +9,11 @@ ifeq ($(LOADADDR),)
LOADADDR = 0x40000000
endif
tests-common = \
$(TEST_DIR)/selftest.flat \
$(TEST_DIR)/spinlock-test.flat \
$(TEST_DIR)/pci-test.flat
tests-common = $(TEST_DIR)/selftest.flat
tests-common += $(TEST_DIR)/spinlock-test.flat
tests-common += $(TEST_DIR)/pci-test.flat
tests-common += $(TEST_DIR)/pmu.flat
tests-common += $(TEST_DIR)/gic.flat
all: test_cases
......@@ -21,6 +22,7 @@ phys_base = $(LOADADDR)
CFLAGS += -std=gnu99
CFLAGS += -ffreestanding
CFLAGS += -fno-pic
CFLAGS += -Wextra
CFLAGS += -O2
CFLAGS += -I lib -I lib/libfdt
......@@ -46,6 +48,8 @@ cflatobjs += lib/arm/mmu.o
cflatobjs += lib/arm/bitops.o
cflatobjs += lib/arm/psci.o
cflatobjs += lib/arm/smp.o
cflatobjs += lib/arm/delay.o
cflatobjs += lib/arm/gic.o lib/arm/gic-v2.o lib/arm/gic-v3.o
libeabi = lib/arm/libeabi.a
eabiobjs = lib/arm/eabi_compat.o
......
......@@ -9,7 +9,7 @@
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/ptrace.h>
#include <asm/cp15.h>
#include <asm/sysreg.h>
#define THREAD_START_SP ((THREAD_SIZE - S_FRAME_SIZE * 8) & ~7)
......
/*
* GIC tests
*
* GICv2
* + test sending/receiving IPIs
* GICv3
* + test sending/receiving IPIs
*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include <libcflat.h>
#include <asm/setup.h>
#include <asm/processor.h>
#include <asm/delay.h>
#include <asm/gic.h>
#include <asm/smp.h>
#include <asm/barrier.h>
#include <asm/io.h>
#define IPI_SENDER 1
#define IPI_IRQ 1
struct gic {
struct {
void (*send_self)(void);
void (*send_broadcast)(void);
} ipi;
};
static struct gic *gic;
static int acked[NR_CPUS], spurious[NR_CPUS];
static int bad_sender[NR_CPUS], bad_irq[NR_CPUS];
static cpumask_t ready;
static void nr_cpu_check(int nr)
{
if (nr_cpus < nr)
report_abort("At least %d cpus required", nr);
}
static void wait_on_ready(void)
{
cpumask_set_cpu(smp_processor_id(), &ready);
while (!cpumask_full(&ready))
cpu_relax();
}
static void stats_reset(void)
{
int i;
for (i = 0; i < nr_cpus; ++i) {
acked[i] = 0;
bad_sender[i] = -1;
bad_irq[i] = -1;
}
smp_wmb();
}
static void check_acked(cpumask_t *mask)
{
int missing = 0, extra = 0, unexpected = 0;
int nr_pass, cpu, i;
bool bad = false;
/* Wait up to 5s for all interrupts to be delivered */
for (i = 0; i < 50; ++i) {
mdelay(100);
nr_pass = 0;
for_each_present_cpu(cpu) {
smp_rmb();
nr_pass += cpumask_test_cpu(cpu, mask) ?
acked[cpu] == 1 : acked[cpu] == 0;
if (bad_sender[cpu] != -1) {
printf("cpu%d received IPI from wrong sender %d\n",
cpu, bad_sender[cpu]);
bad = true;
}
if (bad_irq[cpu] != -1) {
printf("cpu%d received wrong irq %d\n",
cpu, bad_irq[cpu]);
bad = true;
}
}
if (nr_pass == nr_cpus) {
report("Completed in %d ms", !bad, ++i * 100);
return;
}
}
for_each_present_cpu(cpu) {
if (cpumask_test_cpu(cpu, mask)) {
if (!acked[cpu])
++missing;
else if (acked[cpu] > 1)
++extra;
} else {
if (acked[cpu])
++unexpected;
}
}
report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d",
false, missing, extra, unexpected);
}
static void check_spurious(void)
{
int cpu;
smp_rmb();
for_each_present_cpu(cpu) {
if (spurious[cpu])
report_info("WARN: cpu%d got %d spurious interrupts",
cpu, spurious[cpu]);
}
}
static void check_ipi_sender(u32 irqstat)
{
if (gic_version() == 2) {
int src = (irqstat >> 10) & 7;
if (src != IPI_SENDER)
bad_sender[smp_processor_id()] = src;
}
}
static void check_irqnr(u32 irqnr)
{
if (irqnr != IPI_IRQ)
bad_irq[smp_processor_id()] = irqnr;
}
static void ipi_handler(struct pt_regs *regs __unused)
{
u32 irqstat = gic_read_iar();
u32 irqnr = gic_iar_irqnr(irqstat);
if (irqnr != GICC_INT_SPURIOUS) {
gic_write_eoir(irqstat);
smp_rmb(); /* pairs with wmb in stats_reset */
++acked[smp_processor_id()];
check_ipi_sender(irqstat);
check_irqnr(irqnr);
smp_wmb(); /* pairs with rmb in check_acked */
} else {
++spurious[smp_processor_id()];
smp_wmb();
}
}
static void gicv2_ipi_send_self(void)
{
writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
}
static void gicv2_ipi_send_broadcast(void)
{
writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
}
static void gicv3_ipi_send_self(void)
{
gic_ipi_send_single(IPI_IRQ, smp_processor_id());
}
static void gicv3_ipi_send_broadcast(void)
{
gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
isb();
}
static void ipi_test_self(void)
{
cpumask_t mask;
report_prefix_push("self");
stats_reset();
cpumask_clear(&mask);
cpumask_set_cpu(smp_processor_id(), &mask);
gic->ipi.send_self();
check_acked(&mask);
report_prefix_pop();
}
static void ipi_test_smp(void)
{
cpumask_t mask;
int i;
report_prefix_push("target-list");
stats_reset();
cpumask_copy(&mask, &cpu_present_mask);
for (i = smp_processor_id() & 1; i < nr_cpus; i += 2)
cpumask_clear_cpu(i, &mask);
gic_ipi_send_mask(IPI_IRQ, &mask);
check_acked(&mask);
report_prefix_pop();
report_prefix_push("broadcast");
stats_reset();
cpumask_copy(&mask, &cpu_present_mask);
cpumask_clear_cpu(smp_processor_id(), &mask);
gic->ipi.send_broadcast();
check_acked(&mask);
report_prefix_pop();
}
static void ipi_enable(void)
{
gic_enable_defaults();
#ifdef __arm__
install_exception_handler(EXCPTN_IRQ, ipi_handler);
#else
install_irq_handler(EL1H_IRQ, ipi_handler);
#endif
local_irq_enable();
}
static void ipi_send(void)
{
ipi_enable();
wait_on_ready();
ipi_test_self();
ipi_test_smp();
check_spurious();
exit(report_summary());
}
static void ipi_recv(void)
{
ipi_enable();
cpumask_set_cpu(smp_processor_id(), &ready);
while (1)
wfi();
}
static struct gic gicv2 = {
.ipi = {
.send_self = gicv2_ipi_send_self,
.send_broadcast = gicv2_ipi_send_broadcast,
},
};
static struct gic gicv3 = {
.ipi = {
.send_self = gicv3_ipi_send_self,
.send_broadcast = gicv3_ipi_send_broadcast,
},
};
int main(int argc, char **argv)
{
char pfx[8];
int cpu;
if (!gic_init()) {
printf("No supported gic present, skipping tests...\n");
return report_summary();
}
snprintf(pfx, sizeof(pfx), "gicv%d", gic_version());
report_prefix_push(pfx);
switch (gic_version()) {
case 2:
gic = &gicv2;
break;
case 3:
gic = &gicv3;
break;
}
if (argc < 2)
report_abort("no test specified");
if (strcmp(argv[1], "ipi") == 0) {
report_prefix_push(argv[1]);
nr_cpu_check(2);
for_each_present_cpu(cpu) {
if (cpu == 0)
continue;
smp_boot_secondary(cpu,
cpu == IPI_SENDER ? ipi_send : ipi_recv);
}
ipi_recv();
} else {
report_abort("Unknown subtest '%s'", argv[1]);
}
return report_summary();
}
......@@ -14,14 +14,19 @@ int main(void)
{
int ret;
if (!pci_probe())
report_abort("PCI bus probing failed\n");
if (!pci_probe()) {
printf("PCI bus probing failed, skipping tests...\n");
return report_summary();
}
pci_print();
ret = pci_testdev();
report("PCI test device passed %d/%d tests",
ret >= NR_TESTS, ret > 0 ? ret : 0, NR_TESTS);
if (ret == -1)
report_skip("No PCI test device");
else
report("PCI test device passed %d/%d tests",
ret >= NR_TESTS, ret > 0 ? ret : 0, NR_TESTS);
return report_summary();
}
/*
* Test the ARM Performance Monitors Unit (PMU).
*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2016, Red Hat Inc, Wei Huang <wei@redhat.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1 and
* only version 2.1 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*/
#include "libcflat.h"
#include "asm/barrier.h"
#include "asm/sysreg.h"
#include "asm/processor.h"
#define PMU_PMCR_E (1 << 0)
#define PMU_PMCR_C (1 << 2)
#define PMU_PMCR_LC (1 << 6)
#define PMU_PMCR_N_SHIFT 11
#define PMU_PMCR_N_MASK 0x1f
#define PMU_PMCR_ID_SHIFT 16
#define PMU_PMCR_ID_MASK 0xff
#define PMU_PMCR_IMP_SHIFT 24
#define PMU_PMCR_IMP_MASK 0xff
#define PMU_CYCLE_IDX 31
#define NR_SAMPLES 10
static unsigned int pmu_version;
#if defined(__arm__)
#define ID_DFR0_PERFMON_SHIFT 24
#define ID_DFR0_PERFMON_MASK 0xf
#define PMCR __ACCESS_CP15(c9, 0, c12, 0)
#define ID_DFR0 __ACCESS_CP15(c0, 0, c1, 2)
#define PMSELR __ACCESS_CP15(c9, 0, c12, 5)
#define PMXEVTYPER __ACCESS_CP15(c9, 0, c13, 1)
#define PMCNTENSET __ACCESS_CP15(c9, 0, c12, 1)
#define PMCCNTR32 __ACCESS_CP15(c9, 0, c13, 0)
#define PMCCNTR64 __ACCESS_CP15_64(0, c9)
static inline uint32_t get_id_dfr0(void) { return read_sysreg(ID_DFR0); }
static inline uint32_t get_pmcr(void) { return read_sysreg(PMCR); }
static inline void set_pmcr(uint32_t v) { write_sysreg(v, PMCR); }
static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, PMCNTENSET); }
static inline uint8_t get_pmu_version(void)
{
return (get_id_dfr0() >> ID_DFR0_PERFMON_SHIFT) & ID_DFR0_PERFMON_MASK;
}
static inline uint64_t get_pmccntr(void)
{
if (pmu_version == 0x3)
return read_sysreg(PMCCNTR64);
else
return read_sysreg(PMCCNTR32);
}
static inline void set_pmccntr(uint64_t value)
{
if (pmu_version == 0x3)
write_sysreg(value, PMCCNTR64);
else
write_sysreg(value & 0xffffffff, PMCCNTR32);
}
/* PMCCFILTR is an obsolete name for PMXEVTYPER31 in ARMv7 */
static inline void set_pmccfiltr(uint32_t value)
{
write_sysreg(PMU_CYCLE_IDX, PMSELR);
write_sysreg(value, PMXEVTYPER);
isb();
}
/*
* Extra instructions inserted by the compiler would be difficult to compensate
* for, so hand assemble everything between, and including, the PMCR accesses
* to start and stop counting. isb instructions were inserted to make sure
* pmccntr read after this function returns the exact instructions executed in
* the controlled block. Total instrs = isb + mcr + 2*loop = 2 + 2*loop.
*/
static inline void precise_instrs_loop(int loop, uint32_t pmcr)
{
asm volatile(
" mcr p15, 0, %[pmcr], c9, c12, 0\n"
" isb\n"
"1: subs %[loop], %[loop], #1\n"
" bgt 1b\n"
" mcr p15, 0, %[z], c9, c12, 0\n"
" isb\n"
: [loop] "+r" (loop)
: [pmcr] "r" (pmcr), [z] "r" (0)
: "cc");
}
#elif defined(__aarch64__)
#define ID_AA64DFR0_PERFMON_SHIFT 8
#define ID_AA64DFR0_PERFMON_MASK 0xf
static inline uint32_t get_id_aa64dfr0(void) { return read_sysreg(id_aa64dfr0_el1); }
static inline uint32_t get_pmcr(void) { return read_sysreg(pmcr_el0); }
static inline void set_pmcr(uint32_t v) { write_sysreg(v, pmcr_el0); }
static inline uint64_t get_pmccntr(void) { return read_sysreg(pmccntr_el0); }
static inline void set_pmccntr(uint64_t v) { write_sysreg(v, pmccntr_el0); }
static inline void set_pmcntenset(uint32_t v) { write_sysreg(v, pmcntenset_el0); }
static inline void set_pmccfiltr(uint32_t v) { write_sysreg(v, pmccfiltr_el0); }
static inline uint8_t get_pmu_version(void)
{
uint8_t ver = (get_id_aa64dfr0() >> ID_AA64DFR0_PERFMON_SHIFT) & ID_AA64DFR0_PERFMON_MASK;
return ver == 1 ? 3 : ver;
}
/*
* Extra instructions inserted by the compiler would be difficult to compensate
* for, so hand assemble everything between, and including, the PMCR accesses
* to start and stop counting. isb instructions are inserted to make sure
* pmccntr read after this function returns the exact instructions executed
* in the controlled block. Total instrs = isb + msr + 2*loop = 2 + 2*loop.
*/
static inline void precise_instrs_loop(int loop, uint32_t pmcr)
{
asm volatile(
" msr pmcr_el0, %[pmcr]\n"
" isb\n"
"1: subs %[loop], %[loop], #1\n"
" b.gt 1b\n"
" msr pmcr_el0, xzr\n"
" isb\n"
: [loop] "+r" (loop)
: [pmcr] "r" (pmcr)
: "cc");
}
#endif
/*
* As a simple sanity check on the PMCR_EL0, ensure the implementer field isn't
* null. Also print out a couple other interesting fields for diagnostic
* purposes. For example, as of fall 2016, QEMU TCG mode doesn't implement
* event counters and therefore reports zero event counters, but hopefully
* support for at least the instructions event will be added in the future and
* the reported number of event counters will become nonzero.
*/
static bool check_pmcr(void)
{
uint32_t pmcr;
pmcr = get_pmcr();
report_info("PMU implementer/ID code/counters: 0x%x(\"%c\")/0x%x/%d",
(pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK,
((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) ? : ' ',
(pmcr >> PMU_PMCR_ID_SHIFT) & PMU_PMCR_ID_MASK,
(pmcr >> PMU_PMCR_N_SHIFT) & PMU_PMCR_N_MASK);
return ((pmcr >> PMU_PMCR_IMP_SHIFT) & PMU_PMCR_IMP_MASK) != 0;
}
/*
* Ensure that the cycle counter progresses between back-to-back reads.
*/
static bool check_cycles_increase(void)
{
bool success = true;
/* init before event access, this test only cares about cycle count */
set_pmcntenset(1 << PMU_CYCLE_IDX);
set_pmccfiltr(0); /* count cycles in EL0, EL1, but not EL2 */
set_pmcr(get_pmcr() | PMU_PMCR_LC | PMU_PMCR_C | PMU_PMCR_E);
for (int i = 0; i < NR_SAMPLES; i++) {
uint64_t a, b;
a = get_pmccntr();
b = get_pmccntr();
if (a >= b) {
printf("Read %"PRId64" then %"PRId64".\n", a, b);
success = false;
break;
}
}
set_pmcr(get_pmcr() & ~PMU_PMCR_E);
return success;
}
/*
* Execute a known number of guest instructions. Only even instruction counts
* greater than or equal to 4 are supported by the in-line assembly code. The
* control register (PMCR_EL0) is initialized with the provided value (allowing
* for example for the cycle counter or event counters to be reset). At the end
* of the exact instruction loop, zero is written to PMCR_EL0 to disable
* counting, allowing the cycle counter or event counters to be read at the
* leisure of the calling code.
*/
static void measure_instrs(int num, uint32_t pmcr)
{
int loop = (num - 2) / 2;
assert(num >= 4 && ((num - 2) % 2 == 0));
precise_instrs_loop(loop, pmcr);
}
/*
* Measure cycle counts for various known instruction counts. Ensure that the
* cycle counter progresses (similar to check_cycles_increase() but with more
* instructions and using reset and stop controls). If supplied a positive,
* nonzero CPI parameter, it also strictly checks that every measurement matches
* it. Strict CPI checking is used to test -icount mode.
*/
static bool check_cpi(int cpi)
{
uint32_t pmcr = get_pmcr() | PMU_PMCR_LC | PMU_PMCR_C | PMU_PMCR_E;
/* init before event access, this test only cares about cycle count */
set_pmcntenset(1 << PMU_CYCLE_IDX);
set_pmccfiltr(0); /* count cycles in EL0, EL1, but not EL2 */
if (cpi > 0)
printf("Checking for CPI=%d.\n", cpi);
printf("instrs : cycles0 cycles1 ...\n");
for (unsigned int i = 4; i < 300; i += 32) {
uint64_t avg, sum = 0;
printf("%4d:", i);
for (int j = 0; j < NR_SAMPLES; j++) {
uint64_t cycles;
set_pmccntr(0);
measure_instrs(i, pmcr);
cycles = get_pmccntr();
printf(" %4"PRId64"", cycles);
if (!cycles) {
printf("\ncycles not incrementing!\n");
return false;
} else if (cpi > 0 && cycles != i * cpi) {
printf("\nunexpected cycle count received!\n");
return false;
} else if ((cycles >> 32) != 0) {
/* The cycles taken by the loop above should
* fit in 32 bits easily. We check the upper
* 32 bits of the cycle counter to make sure
* there is no supprise. */
printf("\ncycle count bigger than 32bit!\n");
return false;
}
sum += cycles;
}
avg = sum / NR_SAMPLES;
printf(" avg=%-4"PRId64" %s=%-3"PRId64"\n", avg,
(avg >= i) ? "cpi" : "ipc",
(avg >= i) ? avg / i : i / avg);
}
return true;
}