Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
linux-arm
kvm-unit-tests-ae
Commits
ea325c68
Commit
ea325c68
authored
Dec 22, 2016
by
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
Changes
39
Hide whitespace changes
Inline
Side-by-side
arm/Makefile.common
View file @
ea325c68
...
...
@@ -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
...
...
arm/cstart.S
View file @
ea325c68
...
...
@@ -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)
...
...
arm/gic.c
0 → 100644
View file @
ea325c68
/*
* 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
();
}
arm/pci-test.c
View file @
ea325c68
...
...
@@ -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
();
}
arm/pmu.c
0 → 100644
View file @
ea325c68
/*
* 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
(
"
\n
cycles not incrementing!
\n
"
);
return
false
;
}
else
if
(
cpi
>
0
&&
cycles
!=
i
*
cpi
)
{
printf
(
"
\n
unexpected 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
(
"
\n
cycle 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
;