mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 14:30:06 -04:00
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull kvm updates from Paolo Bonzini:
"ARM:
- Generalized infrastructure for 'writable' ID registers, effectively
allowing userspace to opt-out of certain vCPU features for its
guest
- Optimization for vSGI injection, opportunistically compressing
MPIDR to vCPU mapping into a table
- Improvements to KVM's PMU emulation, allowing userspace to select
the number of PMCs available to a VM
- Guest support for memory operation instructions (FEAT_MOPS)
- Cleanups to handling feature flags in KVM_ARM_VCPU_INIT, squashing
bugs and getting rid of useless code
- Changes to the way the SMCCC filter is constructed, avoiding wasted
memory allocations when not in use
- Load the stage-2 MMU context at vcpu_load() for VHE systems,
reducing the overhead of errata mitigations
- Miscellaneous kernel and selftest fixes
LoongArch:
- New architecture for kvm.
The hardware uses the same model as x86, s390 and RISC-V, where
guest/host mode is orthogonal to supervisor/user mode. The
virtualization extensions are very similar to MIPS, therefore the
code also has some similarities but it's been cleaned up to avoid
some of the historical bogosities that are found in arch/mips. The
kernel emulates MMU, timer and CSR accesses, while interrupt
controllers are only emulated in userspace, at least for now.
RISC-V:
- Support for the Smstateen and Zicond extensions
- Support for virtualizing senvcfg
- Support for virtualized SBI debug console (DBCN)
S390:
- Nested page table management can be monitored through tracepoints
and statistics
x86:
- Fix incorrect handling of VMX posted interrupt descriptor in
KVM_SET_LAPIC, which could result in a dropped timer IRQ
- Avoid WARN on systems with Intel IPI virtualization
- Add CONFIG_KVM_MAX_NR_VCPUS, to allow supporting up to 4096 vCPUs
without forcing more common use cases to eat the extra memory
overhead.
- Add virtualization support for AMD SRSO mitigation (IBPB_BRTYPE and
SBPB, aka Selective Branch Predictor Barrier).
- Fix a bug where restoring a vCPU snapshot that was taken within 1
second of creating the original vCPU would cause KVM to try to
synchronize the vCPU's TSC and thus clobber the correct TSC being
set by userspace.
- Compute guest wall clock using a single TSC read to avoid
generating an inaccurate time, e.g. if the vCPU is preempted
between multiple TSC reads.
- "Virtualize" HWCR.TscFreqSel to make Linux guests happy, which
complain about a "Firmware Bug" if the bit isn't set for select
F/M/S combos. Likewise "virtualize" (ignore) MSR_AMD64_TW_CFG to
appease Windows Server 2022.
- Don't apply side effects to Hyper-V's synthetic timer on writes
from userspace to fix an issue where the auto-enable behavior can
trigger spurious interrupts, i.e. do auto-enabling only for guest
writes.
- Remove an unnecessary kick of all vCPUs when synchronizing the
dirty log without PML enabled.
- Advertise "support" for non-serializing FS/GS base MSR writes as
appropriate.
- Harden the fast page fault path to guard against encountering an
invalid root when walking SPTEs.
- Omit "struct kvm_vcpu_xen" entirely when CONFIG_KVM_XEN=n.
- Use the fast path directly from the timer callback when delivering
Xen timer events, instead of waiting for the next iteration of the
run loop. This was not done so far because previously proposed code
had races, but now care is taken to stop the hrtimer at critical
points such as restarting the timer or saving the timer information
for userspace.
- Follow the lead of upstream Xen and ignore the VCPU_SSHOTTMR_future
flag.
- Optimize injection of PMU interrupts that are simultaneous with
NMIs.
- Usual handful of fixes for typos and other warts.
x86 - MTRR/PAT fixes and optimizations:
- Clean up code that deals with honoring guest MTRRs when the VM has
non-coherent DMA and host MTRRs are ignored, i.e. EPT is enabled.
- Zap EPT entries when non-coherent DMA assignment stops/start to
prevent using stale entries with the wrong memtype.
- Don't ignore guest PAT for CR0.CD=1 && KVM_X86_QUIRK_CD_NW_CLEARED=y
This was done as a workaround for virtual machine BIOSes that did
not bother to clear CR0.CD (because ancient KVM/QEMU did not bother
to set it, in turn), and there's zero reason to extend the quirk to
also ignore guest PAT.
x86 - SEV fixes:
- Report KVM_EXIT_SHUTDOWN instead of EINVAL if KVM intercepts
SHUTDOWN while running an SEV-ES guest.
- Clean up the recognition of emulation failures on SEV guests, when
KVM would like to "skip" the instruction but it had already been
partially emulated. This makes it possible to drop a hack that
second guessed the (insufficient) information provided by the
emulator, and just do the right thing.
Documentation:
- Various updates and fixes, mostly for x86
- MTRR and PAT fixes and optimizations"
* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (164 commits)
KVM: selftests: Avoid using forced target for generating arm64 headers
tools headers arm64: Fix references to top srcdir in Makefile
KVM: arm64: Add tracepoint for MMIO accesses where ISV==0
KVM: arm64: selftest: Perform ISB before reading PAR_EL1
KVM: arm64: selftest: Add the missing .guest_prepare()
KVM: arm64: Always invalidate TLB for stage-2 permission faults
KVM: x86: Service NMI requests after PMI requests in VM-Enter path
KVM: arm64: Handle AArch32 SPSR_{irq,abt,und,fiq} as RAZ/WI
KVM: arm64: Do not let a L1 hypervisor access the *32_EL2 sysregs
KVM: arm64: Refine _EL2 system register list that require trap reinjection
arm64: Add missing _EL2 encodings
arm64: Add missing _EL12 encodings
KVM: selftests: aarch64: vPMU test for validating user accesses
KVM: selftests: aarch64: vPMU register test for unimplemented counters
KVM: selftests: aarch64: vPMU register test for implemented counters
KVM: selftests: aarch64: Introduce vpmu_counter_access test
tools: Import arm_pmuv3.h
KVM: arm64: PMU: Allow userspace to limit PMCR_EL0.N for the guest
KVM: arm64: Sanitize PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR} before first run
KVM: arm64: Add {get,set}_user for PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR}
...
This commit is contained in:
1
tools/arch/arm64/include/.gitignore
vendored
Normal file
1
tools/arch/arm64/include/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
generated/
|
||||
26
tools/arch/arm64/include/asm/gpr-num.h
Normal file
26
tools/arch/arm64/include/asm/gpr-num.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef __ASM_GPR_NUM_H
|
||||
#define __ASM_GPR_NUM_H
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
.irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
|
||||
.equ .L__gpr_num_x\num, \num
|
||||
.equ .L__gpr_num_w\num, \num
|
||||
.endr
|
||||
.equ .L__gpr_num_xzr, 31
|
||||
.equ .L__gpr_num_wzr, 31
|
||||
|
||||
#else /* __ASSEMBLY__ */
|
||||
|
||||
#define __DEFINE_ASM_GPR_NUMS \
|
||||
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \
|
||||
" .equ .L__gpr_num_x\\num, \\num\n" \
|
||||
" .equ .L__gpr_num_w\\num, \\num\n" \
|
||||
" .endr\n" \
|
||||
" .equ .L__gpr_num_xzr, 31\n" \
|
||||
" .equ .L__gpr_num_wzr, 31\n"
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __ASM_GPR_NUM_H */
|
||||
File diff suppressed because it is too large
Load Diff
38
tools/arch/arm64/tools/Makefile
Normal file
38
tools/arch/arm64/tools/Makefile
Normal file
@@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ifeq ($(top_srcdir),)
|
||||
top_srcdir := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
top_srcdir := $(patsubst %/,%,$(dir $(top_srcdir)))
|
||||
top_srcdir := $(patsubst %/,%,$(dir $(top_srcdir)))
|
||||
top_srcdir := $(patsubst %/,%,$(dir $(top_srcdir)))
|
||||
endif
|
||||
|
||||
include $(top_srcdir)/tools/scripts/Makefile.include
|
||||
|
||||
AWK ?= awk
|
||||
MKDIR ?= mkdir
|
||||
RM ?= rm
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
arm64_tools_dir = $(top_srcdir)/arch/arm64/tools
|
||||
arm64_sysreg_tbl = $(arm64_tools_dir)/sysreg
|
||||
arm64_gen_sysreg = $(arm64_tools_dir)/gen-sysreg.awk
|
||||
arm64_generated_dir = $(top_srcdir)/tools/arch/arm64/include/generated
|
||||
arm64_sysreg_defs = $(arm64_generated_dir)/asm/sysreg-defs.h
|
||||
|
||||
all: $(arm64_sysreg_defs)
|
||||
@:
|
||||
|
||||
$(arm64_sysreg_defs): $(arm64_gen_sysreg) $(arm64_sysreg_tbl)
|
||||
$(Q)$(MKDIR) -p $(dir $@)
|
||||
$(QUIET_GEN)$(AWK) -f $^ > $@
|
||||
|
||||
clean:
|
||||
$(Q)$(RM) -rf $(arm64_generated_dir)
|
||||
|
||||
.PHONY: all clean
|
||||
308
tools/include/perf/arm_pmuv3.h
Normal file
308
tools/include/perf/arm_pmuv3.h
Normal file
@@ -0,0 +1,308 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __PERF_ARM_PMUV3_H
|
||||
#define __PERF_ARM_PMUV3_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <asm/bug.h>
|
||||
|
||||
#define ARMV8_PMU_MAX_COUNTERS 32
|
||||
#define ARMV8_PMU_COUNTER_MASK (ARMV8_PMU_MAX_COUNTERS - 1)
|
||||
|
||||
/*
|
||||
* Common architectural and microarchitectural event numbers.
|
||||
*/
|
||||
#define ARMV8_PMUV3_PERFCTR_SW_INCR 0x0000
|
||||
#define ARMV8_PMUV3_PERFCTR_L1I_CACHE_REFILL 0x0001
|
||||
#define ARMV8_PMUV3_PERFCTR_L1I_TLB_REFILL 0x0002
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_CACHE_REFILL 0x0003
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_CACHE 0x0004
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_TLB_REFILL 0x0005
|
||||
#define ARMV8_PMUV3_PERFCTR_LD_RETIRED 0x0006
|
||||
#define ARMV8_PMUV3_PERFCTR_ST_RETIRED 0x0007
|
||||
#define ARMV8_PMUV3_PERFCTR_INST_RETIRED 0x0008
|
||||
#define ARMV8_PMUV3_PERFCTR_EXC_TAKEN 0x0009
|
||||
#define ARMV8_PMUV3_PERFCTR_EXC_RETURN 0x000A
|
||||
#define ARMV8_PMUV3_PERFCTR_CID_WRITE_RETIRED 0x000B
|
||||
#define ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED 0x000C
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_IMMED_RETIRED 0x000D
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_RETURN_RETIRED 0x000E
|
||||
#define ARMV8_PMUV3_PERFCTR_UNALIGNED_LDST_RETIRED 0x000F
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_MIS_PRED 0x0010
|
||||
#define ARMV8_PMUV3_PERFCTR_CPU_CYCLES 0x0011
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_PRED 0x0012
|
||||
#define ARMV8_PMUV3_PERFCTR_MEM_ACCESS 0x0013
|
||||
#define ARMV8_PMUV3_PERFCTR_L1I_CACHE 0x0014
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_CACHE_WB 0x0015
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_CACHE 0x0016
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_CACHE_REFILL 0x0017
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_CACHE_WB 0x0018
|
||||
#define ARMV8_PMUV3_PERFCTR_BUS_ACCESS 0x0019
|
||||
#define ARMV8_PMUV3_PERFCTR_MEMORY_ERROR 0x001A
|
||||
#define ARMV8_PMUV3_PERFCTR_INST_SPEC 0x001B
|
||||
#define ARMV8_PMUV3_PERFCTR_TTBR_WRITE_RETIRED 0x001C
|
||||
#define ARMV8_PMUV3_PERFCTR_BUS_CYCLES 0x001D
|
||||
#define ARMV8_PMUV3_PERFCTR_CHAIN 0x001E
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_CACHE_ALLOCATE 0x001F
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_CACHE_ALLOCATE 0x0020
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_RETIRED 0x0021
|
||||
#define ARMV8_PMUV3_PERFCTR_BR_MIS_PRED_RETIRED 0x0022
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL_FRONTEND 0x0023
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL_BACKEND 0x0024
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_TLB 0x0025
|
||||
#define ARMV8_PMUV3_PERFCTR_L1I_TLB 0x0026
|
||||
#define ARMV8_PMUV3_PERFCTR_L2I_CACHE 0x0027
|
||||
#define ARMV8_PMUV3_PERFCTR_L2I_CACHE_REFILL 0x0028
|
||||
#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_ALLOCATE 0x0029
|
||||
#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_REFILL 0x002A
|
||||
#define ARMV8_PMUV3_PERFCTR_L3D_CACHE 0x002B
|
||||
#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_WB 0x002C
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_TLB_REFILL 0x002D
|
||||
#define ARMV8_PMUV3_PERFCTR_L2I_TLB_REFILL 0x002E
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_TLB 0x002F
|
||||
#define ARMV8_PMUV3_PERFCTR_L2I_TLB 0x0030
|
||||
#define ARMV8_PMUV3_PERFCTR_REMOTE_ACCESS 0x0031
|
||||
#define ARMV8_PMUV3_PERFCTR_LL_CACHE 0x0032
|
||||
#define ARMV8_PMUV3_PERFCTR_LL_CACHE_MISS 0x0033
|
||||
#define ARMV8_PMUV3_PERFCTR_DTLB_WALK 0x0034
|
||||
#define ARMV8_PMUV3_PERFCTR_ITLB_WALK 0x0035
|
||||
#define ARMV8_PMUV3_PERFCTR_LL_CACHE_RD 0x0036
|
||||
#define ARMV8_PMUV3_PERFCTR_LL_CACHE_MISS_RD 0x0037
|
||||
#define ARMV8_PMUV3_PERFCTR_REMOTE_ACCESS_RD 0x0038
|
||||
#define ARMV8_PMUV3_PERFCTR_L1D_CACHE_LMISS_RD 0x0039
|
||||
#define ARMV8_PMUV3_PERFCTR_OP_RETIRED 0x003A
|
||||
#define ARMV8_PMUV3_PERFCTR_OP_SPEC 0x003B
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL 0x003C
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND 0x003D
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND 0x003E
|
||||
#define ARMV8_PMUV3_PERFCTR_STALL_SLOT 0x003F
|
||||
|
||||
/* Statistical profiling extension microarchitectural events */
|
||||
#define ARMV8_SPE_PERFCTR_SAMPLE_POP 0x4000
|
||||
#define ARMV8_SPE_PERFCTR_SAMPLE_FEED 0x4001
|
||||
#define ARMV8_SPE_PERFCTR_SAMPLE_FILTRATE 0x4002
|
||||
#define ARMV8_SPE_PERFCTR_SAMPLE_COLLISION 0x4003
|
||||
|
||||
/* AMUv1 architecture events */
|
||||
#define ARMV8_AMU_PERFCTR_CNT_CYCLES 0x4004
|
||||
#define ARMV8_AMU_PERFCTR_STALL_BACKEND_MEM 0x4005
|
||||
|
||||
/* long-latency read miss events */
|
||||
#define ARMV8_PMUV3_PERFCTR_L1I_CACHE_LMISS 0x4006
|
||||
#define ARMV8_PMUV3_PERFCTR_L2D_CACHE_LMISS_RD 0x4009
|
||||
#define ARMV8_PMUV3_PERFCTR_L2I_CACHE_LMISS 0x400A
|
||||
#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_LMISS_RD 0x400B
|
||||
|
||||
/* Trace buffer events */
|
||||
#define ARMV8_PMUV3_PERFCTR_TRB_WRAP 0x400C
|
||||
#define ARMV8_PMUV3_PERFCTR_TRB_TRIG 0x400E
|
||||
|
||||
/* Trace unit events */
|
||||
#define ARMV8_PMUV3_PERFCTR_TRCEXTOUT0 0x4010
|
||||
#define ARMV8_PMUV3_PERFCTR_TRCEXTOUT1 0x4011
|
||||
#define ARMV8_PMUV3_PERFCTR_TRCEXTOUT2 0x4012
|
||||
#define ARMV8_PMUV3_PERFCTR_TRCEXTOUT3 0x4013
|
||||
#define ARMV8_PMUV3_PERFCTR_CTI_TRIGOUT4 0x4018
|
||||
#define ARMV8_PMUV3_PERFCTR_CTI_TRIGOUT5 0x4019
|
||||
#define ARMV8_PMUV3_PERFCTR_CTI_TRIGOUT6 0x401A
|
||||
#define ARMV8_PMUV3_PERFCTR_CTI_TRIGOUT7 0x401B
|
||||
|
||||
/* additional latency from alignment events */
|
||||
#define ARMV8_PMUV3_PERFCTR_LDST_ALIGN_LAT 0x4020
|
||||
#define ARMV8_PMUV3_PERFCTR_LD_ALIGN_LAT 0x4021
|
||||
#define ARMV8_PMUV3_PERFCTR_ST_ALIGN_LAT 0x4022
|
||||
|
||||
/* Armv8.5 Memory Tagging Extension events */
|
||||
#define ARMV8_MTE_PERFCTR_MEM_ACCESS_CHECKED 0x4024
|
||||
#define ARMV8_MTE_PERFCTR_MEM_ACCESS_CHECKED_RD 0x4025
|
||||
#define ARMV8_MTE_PERFCTR_MEM_ACCESS_CHECKED_WR 0x4026
|
||||
|
||||
/* ARMv8 recommended implementation defined event types */
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_RD 0x0040
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_WR 0x0041
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_REFILL_RD 0x0042
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_REFILL_WR 0x0043
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_REFILL_INNER 0x0044
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_REFILL_OUTER 0x0045
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_WB_VICTIM 0x0046
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_WB_CLEAN 0x0047
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_CACHE_INVAL 0x0048
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_TLB_REFILL_RD 0x004C
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_TLB_REFILL_WR 0x004D
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_TLB_RD 0x004E
|
||||
#define ARMV8_IMPDEF_PERFCTR_L1D_TLB_WR 0x004F
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_RD 0x0050
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_WR 0x0051
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_REFILL_RD 0x0052
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_REFILL_WR 0x0053
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_WB_VICTIM 0x0056
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_WB_CLEAN 0x0057
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_CACHE_INVAL 0x0058
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_TLB_REFILL_RD 0x005C
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_TLB_REFILL_WR 0x005D
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_TLB_RD 0x005E
|
||||
#define ARMV8_IMPDEF_PERFCTR_L2D_TLB_WR 0x005F
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_RD 0x0060
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_WR 0x0061
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_SHARED 0x0062
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_NOT_SHARED 0x0063
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_NORMAL 0x0064
|
||||
#define ARMV8_IMPDEF_PERFCTR_BUS_ACCESS_PERIPH 0x0065
|
||||
#define ARMV8_IMPDEF_PERFCTR_MEM_ACCESS_RD 0x0066
|
||||
#define ARMV8_IMPDEF_PERFCTR_MEM_ACCESS_WR 0x0067
|
||||
#define ARMV8_IMPDEF_PERFCTR_UNALIGNED_LD_SPEC 0x0068
|
||||
#define ARMV8_IMPDEF_PERFCTR_UNALIGNED_ST_SPEC 0x0069
|
||||
#define ARMV8_IMPDEF_PERFCTR_UNALIGNED_LDST_SPEC 0x006A
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_LDREX_SPEC 0x006C
|
||||
#define ARMV8_IMPDEF_PERFCTR_STREX_PASS_SPEC 0x006D
|
||||
#define ARMV8_IMPDEF_PERFCTR_STREX_FAIL_SPEC 0x006E
|
||||
#define ARMV8_IMPDEF_PERFCTR_STREX_SPEC 0x006F
|
||||
#define ARMV8_IMPDEF_PERFCTR_LD_SPEC 0x0070
|
||||
#define ARMV8_IMPDEF_PERFCTR_ST_SPEC 0x0071
|
||||
#define ARMV8_IMPDEF_PERFCTR_LDST_SPEC 0x0072
|
||||
#define ARMV8_IMPDEF_PERFCTR_DP_SPEC 0x0073
|
||||
#define ARMV8_IMPDEF_PERFCTR_ASE_SPEC 0x0074
|
||||
#define ARMV8_IMPDEF_PERFCTR_VFP_SPEC 0x0075
|
||||
#define ARMV8_IMPDEF_PERFCTR_PC_WRITE_SPEC 0x0076
|
||||
#define ARMV8_IMPDEF_PERFCTR_CRYPTO_SPEC 0x0077
|
||||
#define ARMV8_IMPDEF_PERFCTR_BR_IMMED_SPEC 0x0078
|
||||
#define ARMV8_IMPDEF_PERFCTR_BR_RETURN_SPEC 0x0079
|
||||
#define ARMV8_IMPDEF_PERFCTR_BR_INDIRECT_SPEC 0x007A
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_ISB_SPEC 0x007C
|
||||
#define ARMV8_IMPDEF_PERFCTR_DSB_SPEC 0x007D
|
||||
#define ARMV8_IMPDEF_PERFCTR_DMB_SPEC 0x007E
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_UNDEF 0x0081
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_SVC 0x0082
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_PABORT 0x0083
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_DABORT 0x0084
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_IRQ 0x0086
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_FIQ 0x0087
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_SMC 0x0088
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_HVC 0x008A
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_TRAP_PABORT 0x008B
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_TRAP_DABORT 0x008C
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_TRAP_OTHER 0x008D
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_TRAP_IRQ 0x008E
|
||||
#define ARMV8_IMPDEF_PERFCTR_EXC_TRAP_FIQ 0x008F
|
||||
#define ARMV8_IMPDEF_PERFCTR_RC_LD_SPEC 0x0090
|
||||
#define ARMV8_IMPDEF_PERFCTR_RC_ST_SPEC 0x0091
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_RD 0x00A0
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_WR 0x00A1
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_REFILL_RD 0x00A2
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_REFILL_WR 0x00A3
|
||||
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_WB_VICTIM 0x00A6
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_WB_CLEAN 0x00A7
|
||||
#define ARMV8_IMPDEF_PERFCTR_L3D_CACHE_INVAL 0x00A8
|
||||
|
||||
/*
|
||||
* Per-CPU PMCR: config reg
|
||||
*/
|
||||
#define ARMV8_PMU_PMCR_E (1 << 0) /* Enable all counters */
|
||||
#define ARMV8_PMU_PMCR_P (1 << 1) /* Reset all counters */
|
||||
#define ARMV8_PMU_PMCR_C (1 << 2) /* Cycle counter reset */
|
||||
#define ARMV8_PMU_PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */
|
||||
#define ARMV8_PMU_PMCR_X (1 << 4) /* Export to ETM */
|
||||
#define ARMV8_PMU_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
|
||||
#define ARMV8_PMU_PMCR_LC (1 << 6) /* Overflow on 64 bit cycle counter */
|
||||
#define ARMV8_PMU_PMCR_LP (1 << 7) /* Long event counter enable */
|
||||
#define ARMV8_PMU_PMCR_N_SHIFT 11 /* Number of counters supported */
|
||||
#define ARMV8_PMU_PMCR_N_MASK 0x1f
|
||||
#define ARMV8_PMU_PMCR_MASK 0xff /* Mask for writable bits */
|
||||
|
||||
/*
|
||||
* PMOVSR: counters overflow flag status reg
|
||||
*/
|
||||
#define ARMV8_PMU_OVSR_MASK 0xffffffff /* Mask for writable bits */
|
||||
#define ARMV8_PMU_OVERFLOWED_MASK ARMV8_PMU_OVSR_MASK
|
||||
|
||||
/*
|
||||
* PMXEVTYPER: Event selection reg
|
||||
*/
|
||||
#define ARMV8_PMU_EVTYPE_MASK 0xc800ffff /* Mask for writable bits */
|
||||
#define ARMV8_PMU_EVTYPE_EVENT 0xffff /* Mask for EVENT bits */
|
||||
|
||||
/*
|
||||
* Event filters for PMUv3
|
||||
*/
|
||||
#define ARMV8_PMU_EXCLUDE_EL1 (1U << 31)
|
||||
#define ARMV8_PMU_EXCLUDE_EL0 (1U << 30)
|
||||
#define ARMV8_PMU_INCLUDE_EL2 (1U << 27)
|
||||
|
||||
/*
|
||||
* PMUSERENR: user enable reg
|
||||
*/
|
||||
#define ARMV8_PMU_USERENR_MASK 0xf /* Mask for writable bits */
|
||||
#define ARMV8_PMU_USERENR_EN (1 << 0) /* PMU regs can be accessed at EL0 */
|
||||
#define ARMV8_PMU_USERENR_SW (1 << 1) /* PMSWINC can be written at EL0 */
|
||||
#define ARMV8_PMU_USERENR_CR (1 << 2) /* Cycle counter can be read at EL0 */
|
||||
#define ARMV8_PMU_USERENR_ER (1 << 3) /* Event counter can be read at EL0 */
|
||||
|
||||
/* PMMIR_EL1.SLOTS mask */
|
||||
#define ARMV8_PMU_SLOTS_MASK 0xff
|
||||
|
||||
#define ARMV8_PMU_BUS_SLOTS_SHIFT 8
|
||||
#define ARMV8_PMU_BUS_SLOTS_MASK 0xff
|
||||
#define ARMV8_PMU_BUS_WIDTH_SHIFT 16
|
||||
#define ARMV8_PMU_BUS_WIDTH_MASK 0xf
|
||||
|
||||
/*
|
||||
* This code is really good
|
||||
*/
|
||||
|
||||
#define PMEVN_CASE(n, case_macro) \
|
||||
case n: case_macro(n); break
|
||||
|
||||
#define PMEVN_SWITCH(x, case_macro) \
|
||||
do { \
|
||||
switch (x) { \
|
||||
PMEVN_CASE(0, case_macro); \
|
||||
PMEVN_CASE(1, case_macro); \
|
||||
PMEVN_CASE(2, case_macro); \
|
||||
PMEVN_CASE(3, case_macro); \
|
||||
PMEVN_CASE(4, case_macro); \
|
||||
PMEVN_CASE(5, case_macro); \
|
||||
PMEVN_CASE(6, case_macro); \
|
||||
PMEVN_CASE(7, case_macro); \
|
||||
PMEVN_CASE(8, case_macro); \
|
||||
PMEVN_CASE(9, case_macro); \
|
||||
PMEVN_CASE(10, case_macro); \
|
||||
PMEVN_CASE(11, case_macro); \
|
||||
PMEVN_CASE(12, case_macro); \
|
||||
PMEVN_CASE(13, case_macro); \
|
||||
PMEVN_CASE(14, case_macro); \
|
||||
PMEVN_CASE(15, case_macro); \
|
||||
PMEVN_CASE(16, case_macro); \
|
||||
PMEVN_CASE(17, case_macro); \
|
||||
PMEVN_CASE(18, case_macro); \
|
||||
PMEVN_CASE(19, case_macro); \
|
||||
PMEVN_CASE(20, case_macro); \
|
||||
PMEVN_CASE(21, case_macro); \
|
||||
PMEVN_CASE(22, case_macro); \
|
||||
PMEVN_CASE(23, case_macro); \
|
||||
PMEVN_CASE(24, case_macro); \
|
||||
PMEVN_CASE(25, case_macro); \
|
||||
PMEVN_CASE(26, case_macro); \
|
||||
PMEVN_CASE(27, case_macro); \
|
||||
PMEVN_CASE(28, case_macro); \
|
||||
PMEVN_CASE(29, case_macro); \
|
||||
PMEVN_CASE(30, case_macro); \
|
||||
default: \
|
||||
WARN(1, "Invalid PMEV* index\n"); \
|
||||
assert(0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -443,6 +443,15 @@ drm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/drm_ioctl.sh
|
||||
# Create output directory if not already present
|
||||
_dummy := $(shell [ -d '$(beauty_ioctl_outdir)' ] || mkdir -p '$(beauty_ioctl_outdir)')
|
||||
|
||||
arm64_gen_sysreg_dir := $(srctree)/tools/arch/arm64/tools
|
||||
|
||||
arm64-sysreg-defs: FORCE
|
||||
$(Q)$(MAKE) -C $(arm64_gen_sysreg_dir)
|
||||
|
||||
arm64-sysreg-defs-clean:
|
||||
$(call QUIET_CLEAN,arm64-sysreg-defs)
|
||||
$(Q)$(MAKE) -C $(arm64_gen_sysreg_dir) clean > /dev/null
|
||||
|
||||
$(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_tbl)
|
||||
$(Q)$(SHELL) '$(drm_ioctl_tbl)' $(drm_hdr_dir) > $@
|
||||
|
||||
@@ -716,7 +725,9 @@ endif
|
||||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||
build-dir = $(or $(__build-dir),.)
|
||||
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioctl_array) \
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders \
|
||||
arm64-sysreg-defs \
|
||||
$(drm_ioctl_array) \
|
||||
$(fadvise_advice_array) \
|
||||
$(fsconfig_arrays) \
|
||||
$(fsmount_arrays) \
|
||||
@@ -1125,7 +1136,7 @@ endif # BUILD_BPF_SKEL
|
||||
bpf-skel-clean:
|
||||
$(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKELETONS)
|
||||
|
||||
clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean fixdep-clean python-clean bpf-skel-clean tests-coresight-targets-clean
|
||||
clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean arm64-sysreg-defs-clean fixdep-clean python-clean bpf-skel-clean tests-coresight-targets-clean
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive $(OUTPUT)perf-iostat $(LANG_BINDINGS)
|
||||
$(Q)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT).config-detected
|
||||
|
||||
@@ -345,7 +345,7 @@ CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET
|
||||
CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
|
||||
CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/
|
||||
CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/ -I$(srctree)/tools/arch/arm64/include/generated/
|
||||
|
||||
$(OUTPUT)util/argv_split.o: ../lib/argv_split.c FORCE
|
||||
$(call rule_mkdir)
|
||||
|
||||
@@ -17,6 +17,15 @@ else
|
||||
ARCH_DIR := $(ARCH)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
arm64_tools_dir := $(top_srcdir)/tools/arch/arm64/tools/
|
||||
GEN_HDRS := $(top_srcdir)/tools/arch/arm64/include/generated/
|
||||
CFLAGS += -I$(GEN_HDRS)
|
||||
|
||||
$(GEN_HDRS): $(wildcard $(arm64_tools_dir)/*)
|
||||
$(MAKE) -C $(arm64_tools_dir)
|
||||
endif
|
||||
|
||||
LIBKVM += lib/assert.c
|
||||
LIBKVM += lib/elf.c
|
||||
LIBKVM += lib/guest_modes.c
|
||||
@@ -66,6 +75,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/dirty_log_page_splitting_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/exit_on_emulation_failure_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/fix_hypercall_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hwcr_msr_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_clock
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_evmcs
|
||||
@@ -145,10 +155,12 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/psci_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/set_id_regs
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/smccc_filter
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vpmu_counter_access
|
||||
TEST_GEN_PROGS_aarch64 += access_tracking_perf_test
|
||||
TEST_GEN_PROGS_aarch64 += demand_paging_test
|
||||
TEST_GEN_PROGS_aarch64 += dirty_log_test
|
||||
@@ -256,13 +268,18 @@ $(TEST_GEN_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(SPLIT_TESTS_TARGETS): %: %.o $(SPLIT_TESTS_OBJS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
|
||||
|
||||
EXTRA_CLEAN += $(LIBKVM_OBJS) $(TEST_DEP_FILES) $(TEST_GEN_OBJ) $(SPLIT_TESTS_OBJS) cscope.*
|
||||
EXTRA_CLEAN += $(GEN_HDRS) \
|
||||
$(LIBKVM_OBJS) \
|
||||
$(SPLIT_TESTS_OBJS) \
|
||||
$(TEST_DEP_FILES) \
|
||||
$(TEST_GEN_OBJ) \
|
||||
cscope.*
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ))))
|
||||
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c $(GEN_HDRS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
|
||||
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(GEN_HDRS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
# Compile the string overrides as freestanding to prevent the compiler from
|
||||
@@ -272,8 +289,10 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
|
||||
$(SPLIT_TESTS_OBJS): $(GEN_HDRS)
|
||||
$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
|
||||
$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)
|
||||
$(TEST_GEN_OBJ): $(GEN_HDRS)
|
||||
|
||||
cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib ..
|
||||
cscope:
|
||||
|
||||
@@ -146,8 +146,8 @@ static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu)
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);
|
||||
|
||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL0), val);
|
||||
return el0 == ID_AA64PFR0_ELx_64BIT_ONLY;
|
||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val);
|
||||
return el0 == ID_AA64PFR0_EL1_ELx_64BIT_ONLY;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
||||
@@ -116,12 +116,12 @@ static void reset_debug_state(void)
|
||||
|
||||
/* Reset all bcr/bvr/wcr/wvr registers */
|
||||
dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), dfr0);
|
||||
brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), dfr0);
|
||||
for (i = 0; i <= brps; i++) {
|
||||
write_dbgbcr(i, 0);
|
||||
write_dbgbvr(i, 0);
|
||||
}
|
||||
wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), dfr0);
|
||||
wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), dfr0);
|
||||
for (i = 0; i <= wrps; i++) {
|
||||
write_dbgwcr(i, 0);
|
||||
write_dbgwvr(i, 0);
|
||||
@@ -418,7 +418,7 @@ static void guest_code_ss(int test_cnt)
|
||||
|
||||
static int debug_version(uint64_t id_aa64dfr0)
|
||||
{
|
||||
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), id_aa64dfr0);
|
||||
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), id_aa64dfr0);
|
||||
}
|
||||
|
||||
static void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)
|
||||
@@ -539,14 +539,14 @@ void test_guest_debug_exceptions_all(uint64_t aa64dfr0)
|
||||
int b, w, c;
|
||||
|
||||
/* Number of breakpoints */
|
||||
brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), aa64dfr0) + 1;
|
||||
brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), aa64dfr0) + 1;
|
||||
__TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required");
|
||||
|
||||
/* Number of watchpoints */
|
||||
wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), aa64dfr0) + 1;
|
||||
wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), aa64dfr0) + 1;
|
||||
|
||||
/* Number of context aware breakpoints */
|
||||
ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_CTX_CMPS), aa64dfr0) + 1;
|
||||
ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_CTX_CMPs), aa64dfr0) + 1;
|
||||
|
||||
pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__,
|
||||
brp_num, wrp_num, ctx_brp_num);
|
||||
|
||||
@@ -96,14 +96,14 @@ static bool guest_check_lse(void)
|
||||
uint64_t isar0 = read_sysreg(id_aa64isar0_el1);
|
||||
uint64_t atomic;
|
||||
|
||||
atomic = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR0_ATOMICS), isar0);
|
||||
atomic = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR0_EL1_ATOMIC), isar0);
|
||||
return atomic >= 2;
|
||||
}
|
||||
|
||||
static bool guest_check_dc_zva(void)
|
||||
{
|
||||
uint64_t dczid = read_sysreg(dczid_el0);
|
||||
uint64_t dzp = FIELD_GET(ARM64_FEATURE_MASK(DCZID_DZP), dczid);
|
||||
uint64_t dzp = FIELD_GET(ARM64_FEATURE_MASK(DCZID_EL0_DZP), dczid);
|
||||
|
||||
return dzp == 0;
|
||||
}
|
||||
@@ -135,8 +135,8 @@ static void guest_at(void)
|
||||
uint64_t par;
|
||||
|
||||
asm volatile("at s1e1r, %0" :: "r" (guest_test_memory));
|
||||
par = read_sysreg(par_el1);
|
||||
isb();
|
||||
par = read_sysreg(par_el1);
|
||||
|
||||
/* Bit 1 indicates whether the AT was successful */
|
||||
GUEST_ASSERT_EQ(par & 1, 0);
|
||||
@@ -196,7 +196,7 @@ static bool guest_set_ha(void)
|
||||
uint64_t hadbs, tcr;
|
||||
|
||||
/* Skip if HA is not supported. */
|
||||
hadbs = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_HADBS), mmfr1);
|
||||
hadbs = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_EL1_HAFDBS), mmfr1);
|
||||
if (hadbs == 0)
|
||||
return false;
|
||||
|
||||
@@ -842,6 +842,7 @@ static void help(char *name)
|
||||
.name = SCAT2(ro_memslot_no_syndrome, _access), \
|
||||
.data_memslot_flags = KVM_MEM_READONLY, \
|
||||
.pt_memslot_flags = KVM_MEM_READONLY, \
|
||||
.guest_prepare = { _PREPARE(_access) }, \
|
||||
.guest_test = _access, \
|
||||
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
|
||||
.expected_events = { .fail_vcpu_runs = 1 }, \
|
||||
@@ -865,6 +866,7 @@ static void help(char *name)
|
||||
.name = SCAT2(ro_memslot_no_syn_and_dlog, _access), \
|
||||
.data_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
|
||||
.pt_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
|
||||
.guest_prepare = { _PREPARE(_access) }, \
|
||||
.guest_test = _access, \
|
||||
.guest_test_check = { _test_check }, \
|
||||
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
|
||||
@@ -894,6 +896,7 @@ static void help(char *name)
|
||||
.data_memslot_flags = KVM_MEM_READONLY, \
|
||||
.pt_memslot_flags = KVM_MEM_READONLY, \
|
||||
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
|
||||
.guest_prepare = { _PREPARE(_access) }, \
|
||||
.guest_test = _access, \
|
||||
.uffd_data_handler = _uffd_data_handler, \
|
||||
.uffd_pt_handler = uffd_pt_handler, \
|
||||
|
||||
481
tools/testing/selftests/kvm/aarch64/set_id_regs.c
Normal file
481
tools/testing/selftests/kvm/aarch64/set_id_regs.c
Normal file
@@ -0,0 +1,481 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* set_id_regs - Test for setting ID register from usersapce.
|
||||
*
|
||||
* Copyright (c) 2023 Google LLC.
|
||||
*
|
||||
*
|
||||
* Test that KVM supports setting ID registers from userspace and handles the
|
||||
* feature set correctly.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "test_util.h"
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
enum ftr_type {
|
||||
FTR_EXACT, /* Use a predefined safe value */
|
||||
FTR_LOWER_SAFE, /* Smaller value is safe */
|
||||
FTR_HIGHER_SAFE, /* Bigger value is safe */
|
||||
FTR_HIGHER_OR_ZERO_SAFE, /* Bigger value is safe, but 0 is biggest */
|
||||
FTR_END, /* Mark the last ftr bits */
|
||||
};
|
||||
|
||||
#define FTR_SIGNED true /* Value should be treated as signed */
|
||||
#define FTR_UNSIGNED false /* Value should be treated as unsigned */
|
||||
|
||||
struct reg_ftr_bits {
|
||||
char *name;
|
||||
bool sign;
|
||||
enum ftr_type type;
|
||||
uint8_t shift;
|
||||
uint64_t mask;
|
||||
int64_t safe_val;
|
||||
};
|
||||
|
||||
struct test_feature_reg {
|
||||
uint32_t reg;
|
||||
const struct reg_ftr_bits *ftr_bits;
|
||||
};
|
||||
|
||||
#define __REG_FTR_BITS(NAME, SIGNED, TYPE, SHIFT, MASK, SAFE_VAL) \
|
||||
{ \
|
||||
.name = #NAME, \
|
||||
.sign = SIGNED, \
|
||||
.type = TYPE, \
|
||||
.shift = SHIFT, \
|
||||
.mask = MASK, \
|
||||
.safe_val = SAFE_VAL, \
|
||||
}
|
||||
|
||||
#define REG_FTR_BITS(type, reg, field, safe_val) \
|
||||
__REG_FTR_BITS(reg##_##field, FTR_UNSIGNED, type, reg##_##field##_SHIFT, \
|
||||
reg##_##field##_MASK, safe_val)
|
||||
|
||||
#define S_REG_FTR_BITS(type, reg, field, safe_val) \
|
||||
__REG_FTR_BITS(reg##_##field, FTR_SIGNED, type, reg##_##field##_SHIFT, \
|
||||
reg##_##field##_MASK, safe_val)
|
||||
|
||||
#define REG_FTR_END \
|
||||
{ \
|
||||
.type = FTR_END, \
|
||||
}
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64dfr0_el1[] = {
|
||||
S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, PMUVer, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, DebugVer, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_dfr0_el1[] = {
|
||||
S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_DFR0_EL1, PerfMon, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_DFR0_EL1, CopDbg, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64isar0_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, RNDR, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, TLB, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, TS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, FHM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, DP, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, SM4, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, SM3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, SHA3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, RDM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, TME, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, ATOMIC, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, CRC32, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, SHA2, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, SHA1, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR0_EL1, AES, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64isar1_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, LS64, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, XS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, I8MM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, DGH, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, BF16, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, SPECRES, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, SB, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, FRINTTS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, LRCPC, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, FCMA, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, JSCVT, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR1_EL1, DPB, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64isar2_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR2_EL1, BC, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR2_EL1, RPRES, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ISAR2_EL1, WFxT, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV2, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, DIT, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, SEL2, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL2, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL1, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL0, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64mmfr0_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, ECV, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, EXS, 0),
|
||||
S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, TGRAN4, 0),
|
||||
S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, TGRAN64, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, TGRAN16, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, BIGENDEL0, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, SNSMEM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, BIGEND, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, ASIDBITS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, PARANGE, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64mmfr1_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, TIDCP1, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, AFP, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, ETS, 0),
|
||||
REG_FTR_BITS(FTR_HIGHER_SAFE, ID_AA64MMFR1_EL1, SpecSEI, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, PAN, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, LO, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, HPDS, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR1_EL1, HAFDBS, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64mmfr2_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, E0PD, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, BBM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, TTL, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, AT, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, ST, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, VARange, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, IESB, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, LSM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, UAO, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR2_EL1, CnP, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
static const struct reg_ftr_bits ftr_id_aa64zfr0_el1[] = {
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, F64MM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, F32MM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, I8MM, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, SM4, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, SHA3, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, BF16, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, BitPerm, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, AES, 0),
|
||||
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64ZFR0_EL1, SVEver, 0),
|
||||
REG_FTR_END,
|
||||
};
|
||||
|
||||
#define TEST_REG(id, table) \
|
||||
{ \
|
||||
.reg = id, \
|
||||
.ftr_bits = &((table)[0]), \
|
||||
}
|
||||
|
||||
static struct test_feature_reg test_regs[] = {
|
||||
TEST_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0_el1),
|
||||
TEST_REG(SYS_ID_DFR0_EL1, ftr_id_dfr0_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR0_EL1, ftr_id_aa64isar0_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR1_EL1, ftr_id_aa64isar1_el1),
|
||||
TEST_REG(SYS_ID_AA64ISAR2_EL1, ftr_id_aa64isar2_el1),
|
||||
TEST_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0_el1),
|
||||
TEST_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1),
|
||||
TEST_REG(SYS_ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1_el1),
|
||||
TEST_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2_el1),
|
||||
TEST_REG(SYS_ID_AA64ZFR0_EL1, ftr_id_aa64zfr0_el1),
|
||||
};
|
||||
|
||||
#define GUEST_REG_SYNC(id) GUEST_SYNC_ARGS(0, id, read_sysreg_s(id), 0, 0);
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
GUEST_REG_SYNC(SYS_ID_AA64DFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_DFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR1_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ISAR2_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64PFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64MMFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64MMFR1_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64MMFR2_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ZFR0_EL1);
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
/* Return a safe value to a given ftr_bits an ftr value */
|
||||
uint64_t get_safe_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr)
|
||||
{
|
||||
uint64_t ftr_max = GENMASK_ULL(ARM64_FEATURE_FIELD_BITS - 1, 0);
|
||||
|
||||
if (ftr_bits->type == FTR_UNSIGNED) {
|
||||
switch (ftr_bits->type) {
|
||||
case FTR_EXACT:
|
||||
ftr = ftr_bits->safe_val;
|
||||
break;
|
||||
case FTR_LOWER_SAFE:
|
||||
if (ftr > 0)
|
||||
ftr--;
|
||||
break;
|
||||
case FTR_HIGHER_SAFE:
|
||||
if (ftr < ftr_max)
|
||||
ftr++;
|
||||
break;
|
||||
case FTR_HIGHER_OR_ZERO_SAFE:
|
||||
if (ftr == ftr_max)
|
||||
ftr = 0;
|
||||
else if (ftr != 0)
|
||||
ftr++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (ftr != ftr_max) {
|
||||
switch (ftr_bits->type) {
|
||||
case FTR_EXACT:
|
||||
ftr = ftr_bits->safe_val;
|
||||
break;
|
||||
case FTR_LOWER_SAFE:
|
||||
if (ftr > 0)
|
||||
ftr--;
|
||||
break;
|
||||
case FTR_HIGHER_SAFE:
|
||||
if (ftr < ftr_max - 1)
|
||||
ftr++;
|
||||
break;
|
||||
case FTR_HIGHER_OR_ZERO_SAFE:
|
||||
if (ftr != 0 && ftr != ftr_max - 1)
|
||||
ftr++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ftr;
|
||||
}
|
||||
|
||||
/* Return an invalid value to a given ftr_bits an ftr value */
|
||||
uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr)
|
||||
{
|
||||
uint64_t ftr_max = GENMASK_ULL(ARM64_FEATURE_FIELD_BITS - 1, 0);
|
||||
|
||||
if (ftr_bits->type == FTR_UNSIGNED) {
|
||||
switch (ftr_bits->type) {
|
||||
case FTR_EXACT:
|
||||
ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1);
|
||||
break;
|
||||
case FTR_LOWER_SAFE:
|
||||
ftr++;
|
||||
break;
|
||||
case FTR_HIGHER_SAFE:
|
||||
ftr--;
|
||||
break;
|
||||
case FTR_HIGHER_OR_ZERO_SAFE:
|
||||
if (ftr == 0)
|
||||
ftr = ftr_max;
|
||||
else
|
||||
ftr--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (ftr != ftr_max) {
|
||||
switch (ftr_bits->type) {
|
||||
case FTR_EXACT:
|
||||
ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1);
|
||||
break;
|
||||
case FTR_LOWER_SAFE:
|
||||
ftr++;
|
||||
break;
|
||||
case FTR_HIGHER_SAFE:
|
||||
ftr--;
|
||||
break;
|
||||
case FTR_HIGHER_OR_ZERO_SAFE:
|
||||
if (ftr == 0)
|
||||
ftr = ftr_max - 1;
|
||||
else
|
||||
ftr--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ftr = 0;
|
||||
}
|
||||
|
||||
return ftr;
|
||||
}
|
||||
|
||||
static void test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
|
||||
const struct reg_ftr_bits *ftr_bits)
|
||||
{
|
||||
uint8_t shift = ftr_bits->shift;
|
||||
uint64_t mask = ftr_bits->mask;
|
||||
uint64_t val, new_val, ftr;
|
||||
|
||||
vcpu_get_reg(vcpu, reg, &val);
|
||||
ftr = (val & mask) >> shift;
|
||||
|
||||
ftr = get_safe_value(ftr_bits, ftr);
|
||||
|
||||
ftr <<= shift;
|
||||
val &= ~mask;
|
||||
val |= ftr;
|
||||
|
||||
vcpu_set_reg(vcpu, reg, val);
|
||||
vcpu_get_reg(vcpu, reg, &new_val);
|
||||
TEST_ASSERT_EQ(new_val, val);
|
||||
}
|
||||
|
||||
static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
|
||||
const struct reg_ftr_bits *ftr_bits)
|
||||
{
|
||||
uint8_t shift = ftr_bits->shift;
|
||||
uint64_t mask = ftr_bits->mask;
|
||||
uint64_t val, old_val, ftr;
|
||||
int r;
|
||||
|
||||
vcpu_get_reg(vcpu, reg, &val);
|
||||
ftr = (val & mask) >> shift;
|
||||
|
||||
ftr = get_invalid_value(ftr_bits, ftr);
|
||||
|
||||
old_val = val;
|
||||
ftr <<= shift;
|
||||
val &= ~mask;
|
||||
val |= ftr;
|
||||
|
||||
r = __vcpu_set_reg(vcpu, reg, val);
|
||||
TEST_ASSERT(r < 0 && errno == EINVAL,
|
||||
"Unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);
|
||||
|
||||
vcpu_get_reg(vcpu, reg, &val);
|
||||
TEST_ASSERT_EQ(val, old_val);
|
||||
}
|
||||
|
||||
static void test_user_set_reg(struct kvm_vcpu *vcpu, bool aarch64_only)
|
||||
{
|
||||
uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE];
|
||||
struct reg_mask_range range = {
|
||||
.addr = (__u64)masks,
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* KVM should return error when reserved field is not zero */
|
||||
range.reserved[0] = 1;
|
||||
ret = __vm_ioctl(vcpu->vm, KVM_ARM_GET_REG_WRITABLE_MASKS, &range);
|
||||
TEST_ASSERT(ret, "KVM doesn't check invalid parameters.");
|
||||
|
||||
/* Get writable masks for feature ID registers */
|
||||
memset(range.reserved, 0, sizeof(range.reserved));
|
||||
vm_ioctl(vcpu->vm, KVM_ARM_GET_REG_WRITABLE_MASKS, &range);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(test_regs); i++) {
|
||||
const struct reg_ftr_bits *ftr_bits = test_regs[i].ftr_bits;
|
||||
uint32_t reg_id = test_regs[i].reg;
|
||||
uint64_t reg = KVM_ARM64_SYS_REG(reg_id);
|
||||
int idx;
|
||||
|
||||
/* Get the index to masks array for the idreg */
|
||||
idx = KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(reg_id), sys_reg_Op1(reg_id),
|
||||
sys_reg_CRn(reg_id), sys_reg_CRm(reg_id),
|
||||
sys_reg_Op2(reg_id));
|
||||
|
||||
for (int j = 0; ftr_bits[j].type != FTR_END; j++) {
|
||||
/* Skip aarch32 reg on aarch64 only system, since they are RAZ/WI. */
|
||||
if (aarch64_only && sys_reg_CRm(reg_id) < 4) {
|
||||
ksft_test_result_skip("%s on AARCH64 only system\n",
|
||||
ftr_bits[j].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure the feature field is writable */
|
||||
TEST_ASSERT_EQ(masks[idx] & ftr_bits[j].mask, ftr_bits[j].mask);
|
||||
|
||||
test_reg_set_fail(vcpu, reg, &ftr_bits[j]);
|
||||
test_reg_set_success(vcpu, reg, &ftr_bits[j]);
|
||||
|
||||
ksft_test_result_pass("%s\n", ftr_bits[j].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_guest_reg_read(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
bool done = false;
|
||||
struct ucall uc;
|
||||
uint64_t val;
|
||||
|
||||
while (!done) {
|
||||
vcpu_run(vcpu);
|
||||
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
case UCALL_SYNC:
|
||||
/* Make sure the written values are seen by guest */
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(uc.args[2]), &val);
|
||||
TEST_ASSERT_EQ(val, uc.args[3]);
|
||||
break;
|
||||
case UCALL_DONE:
|
||||
done = true;
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
bool aarch64_only;
|
||||
uint64_t val, el0;
|
||||
int ftr_cnt;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES));
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
|
||||
/* Check for AARCH64 only system */
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);
|
||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val);
|
||||
aarch64_only = (el0 == ID_AA64PFR0_EL1_ELx_64BIT_ONLY);
|
||||
|
||||
ksft_print_header();
|
||||
|
||||
ftr_cnt = ARRAY_SIZE(ftr_id_aa64dfr0_el1) + ARRAY_SIZE(ftr_id_dfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64isar0_el1) + ARRAY_SIZE(ftr_id_aa64isar1_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
|
||||
ARRAY_SIZE(test_regs);
|
||||
|
||||
ksft_set_plan(ftr_cnt);
|
||||
|
||||
test_user_set_reg(vcpu, aarch64_only);
|
||||
test_guest_reg_read(vcpu);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
|
||||
ksft_finished();
|
||||
}
|
||||
670
tools/testing/selftests/kvm/aarch64/vpmu_counter_access.c
Normal file
670
tools/testing/selftests/kvm/aarch64/vpmu_counter_access.c
Normal file
@@ -0,0 +1,670 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* vpmu_counter_access - Test vPMU event counter access
|
||||
*
|
||||
* Copyright (c) 2023 Google LLC.
|
||||
*
|
||||
* This test checks if the guest can see the same number of the PMU event
|
||||
* counters (PMCR_EL0.N) that userspace sets, if the guest can access
|
||||
* those counters, and if the guest is prevented from accessing any
|
||||
* other counters.
|
||||
* It also checks if the userspace accesses to the PMU regsisters honor the
|
||||
* PMCR.N value that's set for the guest.
|
||||
* This test runs only when KVM_CAP_ARM_PMU_V3 is supported on the host.
|
||||
*/
|
||||
#include <kvm_util.h>
|
||||
#include <processor.h>
|
||||
#include <test_util.h>
|
||||
#include <vgic.h>
|
||||
#include <perf/arm_pmuv3.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
/* The max number of the PMU event counters (excluding the cycle counter) */
|
||||
#define ARMV8_PMU_MAX_GENERAL_COUNTERS (ARMV8_PMU_MAX_COUNTERS - 1)
|
||||
|
||||
/* The cycle counter bit position that's common among the PMU registers */
|
||||
#define ARMV8_PMU_CYCLE_IDX 31
|
||||
|
||||
struct vpmu_vm {
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_vcpu *vcpu;
|
||||
int gic_fd;
|
||||
};
|
||||
|
||||
static struct vpmu_vm vpmu_vm;
|
||||
|
||||
struct pmreg_sets {
|
||||
uint64_t set_reg_id;
|
||||
uint64_t clr_reg_id;
|
||||
};
|
||||
|
||||
#define PMREG_SET(set, clr) {.set_reg_id = set, .clr_reg_id = clr}
|
||||
|
||||
static uint64_t get_pmcr_n(uint64_t pmcr)
|
||||
{
|
||||
return (pmcr >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK;
|
||||
}
|
||||
|
||||
static void set_pmcr_n(uint64_t *pmcr, uint64_t pmcr_n)
|
||||
{
|
||||
*pmcr = *pmcr & ~(ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
|
||||
*pmcr |= (pmcr_n << ARMV8_PMU_PMCR_N_SHIFT);
|
||||
}
|
||||
|
||||
static uint64_t get_counters_mask(uint64_t n)
|
||||
{
|
||||
uint64_t mask = BIT(ARMV8_PMU_CYCLE_IDX);
|
||||
|
||||
if (n)
|
||||
mask |= GENMASK(n - 1, 0);
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* Read PMEVTCNTR<n>_EL0 through PMXEVCNTR_EL0 */
|
||||
static inline unsigned long read_sel_evcntr(int sel)
|
||||
{
|
||||
write_sysreg(sel, pmselr_el0);
|
||||
isb();
|
||||
return read_sysreg(pmxevcntr_el0);
|
||||
}
|
||||
|
||||
/* Write PMEVTCNTR<n>_EL0 through PMXEVCNTR_EL0 */
|
||||
static inline void write_sel_evcntr(int sel, unsigned long val)
|
||||
{
|
||||
write_sysreg(sel, pmselr_el0);
|
||||
isb();
|
||||
write_sysreg(val, pmxevcntr_el0);
|
||||
isb();
|
||||
}
|
||||
|
||||
/* Read PMEVTYPER<n>_EL0 through PMXEVTYPER_EL0 */
|
||||
static inline unsigned long read_sel_evtyper(int sel)
|
||||
{
|
||||
write_sysreg(sel, pmselr_el0);
|
||||
isb();
|
||||
return read_sysreg(pmxevtyper_el0);
|
||||
}
|
||||
|
||||
/* Write PMEVTYPER<n>_EL0 through PMXEVTYPER_EL0 */
|
||||
static inline void write_sel_evtyper(int sel, unsigned long val)
|
||||
{
|
||||
write_sysreg(sel, pmselr_el0);
|
||||
isb();
|
||||
write_sysreg(val, pmxevtyper_el0);
|
||||
isb();
|
||||
}
|
||||
|
||||
static inline void enable_counter(int idx)
|
||||
{
|
||||
uint64_t v = read_sysreg(pmcntenset_el0);
|
||||
|
||||
write_sysreg(BIT(idx) | v, pmcntenset_el0);
|
||||
isb();
|
||||
}
|
||||
|
||||
static inline void disable_counter(int idx)
|
||||
{
|
||||
uint64_t v = read_sysreg(pmcntenset_el0);
|
||||
|
||||
write_sysreg(BIT(idx) | v, pmcntenclr_el0);
|
||||
isb();
|
||||
}
|
||||
|
||||
static void pmu_disable_reset(void)
|
||||
{
|
||||
uint64_t pmcr = read_sysreg(pmcr_el0);
|
||||
|
||||
/* Reset all counters, disabling them */
|
||||
pmcr &= ~ARMV8_PMU_PMCR_E;
|
||||
write_sysreg(pmcr | ARMV8_PMU_PMCR_P, pmcr_el0);
|
||||
isb();
|
||||
}
|
||||
|
||||
#define RETURN_READ_PMEVCNTRN(n) \
|
||||
return read_sysreg(pmevcntr##n##_el0)
|
||||
static unsigned long read_pmevcntrn(int n)
|
||||
{
|
||||
PMEVN_SWITCH(n, RETURN_READ_PMEVCNTRN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WRITE_PMEVCNTRN(n) \
|
||||
write_sysreg(val, pmevcntr##n##_el0)
|
||||
static void write_pmevcntrn(int n, unsigned long val)
|
||||
{
|
||||
PMEVN_SWITCH(n, WRITE_PMEVCNTRN);
|
||||
isb();
|
||||
}
|
||||
|
||||
#define READ_PMEVTYPERN(n) \
|
||||
return read_sysreg(pmevtyper##n##_el0)
|
||||
static unsigned long read_pmevtypern(int n)
|
||||
{
|
||||
PMEVN_SWITCH(n, READ_PMEVTYPERN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WRITE_PMEVTYPERN(n) \
|
||||
write_sysreg(val, pmevtyper##n##_el0)
|
||||
static void write_pmevtypern(int n, unsigned long val)
|
||||
{
|
||||
PMEVN_SWITCH(n, WRITE_PMEVTYPERN);
|
||||
isb();
|
||||
}
|
||||
|
||||
/*
|
||||
* The pmc_accessor structure has pointers to PMEV{CNTR,TYPER}<n>_EL0
|
||||
* accessors that test cases will use. Each of the accessors will
|
||||
* either directly reads/writes PMEV{CNTR,TYPER}<n>_EL0
|
||||
* (i.e. {read,write}_pmev{cnt,type}rn()), or reads/writes them through
|
||||
* PMXEV{CNTR,TYPER}_EL0 (i.e. {read,write}_sel_ev{cnt,type}r()).
|
||||
*
|
||||
* This is used to test that combinations of those accessors provide
|
||||
* the consistent behavior.
|
||||
*/
|
||||
struct pmc_accessor {
|
||||
/* A function to be used to read PMEVTCNTR<n>_EL0 */
|
||||
unsigned long (*read_cntr)(int idx);
|
||||
/* A function to be used to write PMEVTCNTR<n>_EL0 */
|
||||
void (*write_cntr)(int idx, unsigned long val);
|
||||
/* A function to be used to read PMEVTYPER<n>_EL0 */
|
||||
unsigned long (*read_typer)(int idx);
|
||||
/* A function to be used to write PMEVTYPER<n>_EL0 */
|
||||
void (*write_typer)(int idx, unsigned long val);
|
||||
};
|
||||
|
||||
struct pmc_accessor pmc_accessors[] = {
|
||||
/* test with all direct accesses */
|
||||
{ read_pmevcntrn, write_pmevcntrn, read_pmevtypern, write_pmevtypern },
|
||||
/* test with all indirect accesses */
|
||||
{ read_sel_evcntr, write_sel_evcntr, read_sel_evtyper, write_sel_evtyper },
|
||||
/* read with direct accesses, and write with indirect accesses */
|
||||
{ read_pmevcntrn, write_sel_evcntr, read_pmevtypern, write_sel_evtyper },
|
||||
/* read with indirect accesses, and write with direct accesses */
|
||||
{ read_sel_evcntr, write_pmevcntrn, read_sel_evtyper, write_pmevtypern },
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert a pointer of pmc_accessor to an index in pmc_accessors[],
|
||||
* assuming that the pointer is one of the entries in pmc_accessors[].
|
||||
*/
|
||||
#define PMC_ACC_TO_IDX(acc) (acc - &pmc_accessors[0])
|
||||
|
||||
#define GUEST_ASSERT_BITMAP_REG(regname, mask, set_expected) \
|
||||
{ \
|
||||
uint64_t _tval = read_sysreg(regname); \
|
||||
\
|
||||
if (set_expected) \
|
||||
__GUEST_ASSERT((_tval & mask), \
|
||||
"tval: 0x%lx; mask: 0x%lx; set_expected: 0x%lx", \
|
||||
_tval, mask, set_expected); \
|
||||
else \
|
||||
__GUEST_ASSERT(!(_tval & mask), \
|
||||
"tval: 0x%lx; mask: 0x%lx; set_expected: 0x%lx", \
|
||||
_tval, mask, set_expected); \
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if @mask bits in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers
|
||||
* are set or cleared as specified in @set_expected.
|
||||
*/
|
||||
static void check_bitmap_pmu_regs(uint64_t mask, bool set_expected)
|
||||
{
|
||||
GUEST_ASSERT_BITMAP_REG(pmcntenset_el0, mask, set_expected);
|
||||
GUEST_ASSERT_BITMAP_REG(pmcntenclr_el0, mask, set_expected);
|
||||
GUEST_ASSERT_BITMAP_REG(pmintenset_el1, mask, set_expected);
|
||||
GUEST_ASSERT_BITMAP_REG(pmintenclr_el1, mask, set_expected);
|
||||
GUEST_ASSERT_BITMAP_REG(pmovsset_el0, mask, set_expected);
|
||||
GUEST_ASSERT_BITMAP_REG(pmovsclr_el0, mask, set_expected);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the bit in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers corresponding
|
||||
* to the specified counter (@pmc_idx) can be read/written as expected.
|
||||
* When @set_op is true, it tries to set the bit for the counter in
|
||||
* those registers by writing the SET registers (the bit won't be set
|
||||
* if the counter is not implemented though).
|
||||
* Otherwise, it tries to clear the bits in the registers by writing
|
||||
* the CLR registers.
|
||||
* Then, it checks if the values indicated in the registers are as expected.
|
||||
*/
|
||||
static void test_bitmap_pmu_regs(int pmc_idx, bool set_op)
|
||||
{
|
||||
uint64_t pmcr_n, test_bit = BIT(pmc_idx);
|
||||
bool set_expected = false;
|
||||
|
||||
if (set_op) {
|
||||
write_sysreg(test_bit, pmcntenset_el0);
|
||||
write_sysreg(test_bit, pmintenset_el1);
|
||||
write_sysreg(test_bit, pmovsset_el0);
|
||||
|
||||
/* The bit will be set only if the counter is implemented */
|
||||
pmcr_n = get_pmcr_n(read_sysreg(pmcr_el0));
|
||||
set_expected = (pmc_idx < pmcr_n) ? true : false;
|
||||
} else {
|
||||
write_sysreg(test_bit, pmcntenclr_el0);
|
||||
write_sysreg(test_bit, pmintenclr_el1);
|
||||
write_sysreg(test_bit, pmovsclr_el0);
|
||||
}
|
||||
check_bitmap_pmu_regs(test_bit, set_expected);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for reading/writing registers for the (implemented) event counter
|
||||
* specified by @pmc_idx.
|
||||
*/
|
||||
static void test_access_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
|
||||
{
|
||||
uint64_t write_data, read_data;
|
||||
|
||||
/* Disable all PMCs and reset all PMCs to zero. */
|
||||
pmu_disable_reset();
|
||||
|
||||
/*
|
||||
* Tests for reading/writing {PMCNTEN,PMINTEN,PMOVS}{SET,CLR}_EL1.
|
||||
*/
|
||||
|
||||
/* Make sure that the bit in those registers are set to 0 */
|
||||
test_bitmap_pmu_regs(pmc_idx, false);
|
||||
/* Test if setting the bit in those registers works */
|
||||
test_bitmap_pmu_regs(pmc_idx, true);
|
||||
/* Test if clearing the bit in those registers works */
|
||||
test_bitmap_pmu_regs(pmc_idx, false);
|
||||
|
||||
/*
|
||||
* Tests for reading/writing the event type register.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set the event type register to an arbitrary value just for testing
|
||||
* of reading/writing the register.
|
||||
* Arm ARM says that for the event from 0x0000 to 0x003F,
|
||||
* the value indicated in the PMEVTYPER<n>_EL0.evtCount field is
|
||||
* the value written to the field even when the specified event
|
||||
* is not supported.
|
||||
*/
|
||||
write_data = (ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMUV3_PERFCTR_INST_RETIRED);
|
||||
acc->write_typer(pmc_idx, write_data);
|
||||
read_data = acc->read_typer(pmc_idx);
|
||||
__GUEST_ASSERT(read_data == write_data,
|
||||
"pmc_idx: 0x%lx; acc_idx: 0x%lx; read_data: 0x%lx; write_data: 0x%lx",
|
||||
pmc_idx, PMC_ACC_TO_IDX(acc), read_data, write_data);
|
||||
|
||||
/*
|
||||
* Tests for reading/writing the event count register.
|
||||
*/
|
||||
|
||||
read_data = acc->read_cntr(pmc_idx);
|
||||
|
||||
/* The count value must be 0, as it is disabled and reset */
|
||||
__GUEST_ASSERT(read_data == 0,
|
||||
"pmc_idx: 0x%lx; acc_idx: 0x%lx; read_data: 0x%lx",
|
||||
pmc_idx, PMC_ACC_TO_IDX(acc), read_data);
|
||||
|
||||
write_data = read_data + pmc_idx + 0x12345;
|
||||
acc->write_cntr(pmc_idx, write_data);
|
||||
read_data = acc->read_cntr(pmc_idx);
|
||||
__GUEST_ASSERT(read_data == write_data,
|
||||
"pmc_idx: 0x%lx; acc_idx: 0x%lx; read_data: 0x%lx; write_data: 0x%lx",
|
||||
pmc_idx, PMC_ACC_TO_IDX(acc), read_data, write_data);
|
||||
}
|
||||
|
||||
#define INVALID_EC (-1ul)
|
||||
uint64_t expected_ec = INVALID_EC;
|
||||
|
||||
static void guest_sync_handler(struct ex_regs *regs)
|
||||
{
|
||||
uint64_t esr, ec;
|
||||
|
||||
esr = read_sysreg(esr_el1);
|
||||
ec = (esr >> ESR_EC_SHIFT) & ESR_EC_MASK;
|
||||
|
||||
__GUEST_ASSERT(expected_ec == ec,
|
||||
"PC: 0x%lx; ESR: 0x%lx; EC: 0x%lx; EC expected: 0x%lx",
|
||||
regs->pc, esr, ec, expected_ec);
|
||||
|
||||
/* skip the trapping instruction */
|
||||
regs->pc += 4;
|
||||
|
||||
/* Use INVALID_EC to indicate an exception occurred */
|
||||
expected_ec = INVALID_EC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the given operation that should trigger an exception with the
|
||||
* given exception class. The exception handler (guest_sync_handler)
|
||||
* will reset op_end_addr to 0, expected_ec to INVALID_EC, and skip
|
||||
* the instruction that trapped.
|
||||
*/
|
||||
#define TEST_EXCEPTION(ec, ops) \
|
||||
({ \
|
||||
GUEST_ASSERT(ec != INVALID_EC); \
|
||||
WRITE_ONCE(expected_ec, ec); \
|
||||
dsb(ish); \
|
||||
ops; \
|
||||
GUEST_ASSERT(expected_ec == INVALID_EC); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Tests for reading/writing registers for the unimplemented event counter
|
||||
* specified by @pmc_idx (>= PMCR_EL0.N).
|
||||
*/
|
||||
static void test_access_invalid_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
|
||||
{
|
||||
/*
|
||||
* Reading/writing the event count/type registers should cause
|
||||
* an UNDEFINED exception.
|
||||
*/
|
||||
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_cntr(pmc_idx));
|
||||
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_cntr(pmc_idx, 0));
|
||||
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_typer(pmc_idx));
|
||||
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_typer(pmc_idx, 0));
|
||||
/*
|
||||
* The bit corresponding to the (unimplemented) counter in
|
||||
* {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers should be RAZ.
|
||||
*/
|
||||
test_bitmap_pmu_regs(pmc_idx, 1);
|
||||
test_bitmap_pmu_regs(pmc_idx, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The guest is configured with PMUv3 with @expected_pmcr_n number of
|
||||
* event counters.
|
||||
* Check if @expected_pmcr_n is consistent with PMCR_EL0.N, and
|
||||
* if reading/writing PMU registers for implemented or unimplemented
|
||||
* counters works as expected.
|
||||
*/
|
||||
static void guest_code(uint64_t expected_pmcr_n)
|
||||
{
|
||||
uint64_t pmcr, pmcr_n, unimp_mask;
|
||||
int i, pmc;
|
||||
|
||||
__GUEST_ASSERT(expected_pmcr_n <= ARMV8_PMU_MAX_GENERAL_COUNTERS,
|
||||
"Expected PMCR.N: 0x%lx; ARMv8 general counters: 0x%lx",
|
||||
expected_pmcr_n, ARMV8_PMU_MAX_GENERAL_COUNTERS);
|
||||
|
||||
pmcr = read_sysreg(pmcr_el0);
|
||||
pmcr_n = get_pmcr_n(pmcr);
|
||||
|
||||
/* Make sure that PMCR_EL0.N indicates the value userspace set */
|
||||
__GUEST_ASSERT(pmcr_n == expected_pmcr_n,
|
||||
"Expected PMCR.N: 0x%lx, PMCR.N: 0x%lx",
|
||||
expected_pmcr_n, pmcr_n);
|
||||
|
||||
/*
|
||||
* Make sure that (RAZ) bits corresponding to unimplemented event
|
||||
* counters in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers are reset
|
||||
* to zero.
|
||||
* (NOTE: bits for implemented event counters are reset to UNKNOWN)
|
||||
*/
|
||||
unimp_mask = GENMASK_ULL(ARMV8_PMU_MAX_GENERAL_COUNTERS - 1, pmcr_n);
|
||||
check_bitmap_pmu_regs(unimp_mask, false);
|
||||
|
||||
/*
|
||||
* Tests for reading/writing PMU registers for implemented counters.
|
||||
* Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
|
||||
for (pmc = 0; pmc < pmcr_n; pmc++)
|
||||
test_access_pmc_regs(&pmc_accessors[i], pmc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for reading/writing PMU registers for unimplemented counters.
|
||||
* Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
|
||||
for (pmc = pmcr_n; pmc < ARMV8_PMU_MAX_GENERAL_COUNTERS; pmc++)
|
||||
test_access_invalid_pmc_regs(&pmc_accessors[i], pmc);
|
||||
}
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
#define GICD_BASE_GPA 0x8000000ULL
|
||||
#define GICR_BASE_GPA 0x80A0000ULL
|
||||
|
||||
/* Create a VM that has one vCPU with PMUv3 configured. */
|
||||
static void create_vpmu_vm(void *guest_code)
|
||||
{
|
||||
struct kvm_vcpu_init init;
|
||||
uint8_t pmuver, ec;
|
||||
uint64_t dfr0, irq = 23;
|
||||
struct kvm_device_attr irq_attr = {
|
||||
.group = KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
.attr = KVM_ARM_VCPU_PMU_V3_IRQ,
|
||||
.addr = (uint64_t)&irq,
|
||||
};
|
||||
struct kvm_device_attr init_attr = {
|
||||
.group = KVM_ARM_VCPU_PMU_V3_CTRL,
|
||||
.attr = KVM_ARM_VCPU_PMU_V3_INIT,
|
||||
};
|
||||
|
||||
/* The test creates the vpmu_vm multiple times. Ensure a clean state */
|
||||
memset(&vpmu_vm, 0, sizeof(vpmu_vm));
|
||||
|
||||
vpmu_vm.vm = vm_create(1);
|
||||
vm_init_descriptor_tables(vpmu_vm.vm);
|
||||
for (ec = 0; ec < ESR_EC_NUM; ec++) {
|
||||
vm_install_sync_handler(vpmu_vm.vm, VECTOR_SYNC_CURRENT, ec,
|
||||
guest_sync_handler);
|
||||
}
|
||||
|
||||
/* Create vCPU with PMUv3 */
|
||||
vm_ioctl(vpmu_vm.vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
|
||||
vpmu_vm.vcpu = aarch64_vcpu_add(vpmu_vm.vm, 0, &init, guest_code);
|
||||
vcpu_init_descriptor_tables(vpmu_vm.vcpu);
|
||||
vpmu_vm.gic_fd = vgic_v3_setup(vpmu_vm.vm, 1, 64,
|
||||
GICD_BASE_GPA, GICR_BASE_GPA);
|
||||
__TEST_REQUIRE(vpmu_vm.gic_fd >= 0,
|
||||
"Failed to create vgic-v3, skipping");
|
||||
|
||||
/* Make sure that PMUv3 support is indicated in the ID register */
|
||||
vcpu_get_reg(vpmu_vm.vcpu,
|
||||
KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &dfr0);
|
||||
pmuver = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), dfr0);
|
||||
TEST_ASSERT(pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF &&
|
||||
pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP,
|
||||
"Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver);
|
||||
|
||||
/* Initialize vPMU */
|
||||
vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr);
|
||||
vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &init_attr);
|
||||
}
|
||||
|
||||
static void destroy_vpmu_vm(void)
|
||||
{
|
||||
close(vpmu_vm.gic_fd);
|
||||
kvm_vm_free(vpmu_vm.vm);
|
||||
}
|
||||
|
||||
static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t pmcr_n)
|
||||
{
|
||||
struct ucall uc;
|
||||
|
||||
vcpu_args_set(vcpu, 1, pmcr_n);
|
||||
vcpu_run(vcpu);
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
case UCALL_DONE:
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unknown ucall %lu", uc.cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_create_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool expect_fail)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
uint64_t pmcr, pmcr_orig;
|
||||
|
||||
create_vpmu_vm(guest_code);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), &pmcr_orig);
|
||||
pmcr = pmcr_orig;
|
||||
|
||||
/*
|
||||
* Setting a larger value of PMCR.N should not modify the field, and
|
||||
* return a success.
|
||||
*/
|
||||
set_pmcr_n(&pmcr, pmcr_n);
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), pmcr);
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), &pmcr);
|
||||
|
||||
if (expect_fail)
|
||||
TEST_ASSERT(pmcr_orig == pmcr,
|
||||
"PMCR.N modified by KVM to a larger value (PMCR: 0x%lx) for pmcr_n: 0x%lx\n",
|
||||
pmcr, pmcr_n);
|
||||
else
|
||||
TEST_ASSERT(pmcr_n == get_pmcr_n(pmcr),
|
||||
"Failed to update PMCR.N to %lu (received: %lu)\n",
|
||||
pmcr_n, get_pmcr_n(pmcr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_n,
|
||||
* and run the test.
|
||||
*/
|
||||
static void run_access_test(uint64_t pmcr_n)
|
||||
{
|
||||
uint64_t sp;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vcpu_init init;
|
||||
|
||||
pr_debug("Test with pmcr_n %lu\n", pmcr_n);
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
/* Save the initial sp to restore them later to run the guest again */
|
||||
vcpu_get_reg(vcpu, ARM64_CORE_REG(sp_el1), &sp);
|
||||
|
||||
run_vcpu(vcpu, pmcr_n);
|
||||
|
||||
/*
|
||||
* Reset and re-initialize the vCPU, and run the guest code again to
|
||||
* check if PMCR_EL0.N is preserved.
|
||||
*/
|
||||
vm_ioctl(vpmu_vm.vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
|
||||
aarch64_vcpu_setup(vcpu, &init);
|
||||
vcpu_init_descriptor_tables(vcpu);
|
||||
vcpu_set_reg(vcpu, ARM64_CORE_REG(sp_el1), sp);
|
||||
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code);
|
||||
|
||||
run_vcpu(vcpu, pmcr_n);
|
||||
|
||||
destroy_vpmu_vm();
|
||||
}
|
||||
|
||||
static struct pmreg_sets validity_check_reg_sets[] = {
|
||||
PMREG_SET(SYS_PMCNTENSET_EL0, SYS_PMCNTENCLR_EL0),
|
||||
PMREG_SET(SYS_PMINTENSET_EL1, SYS_PMINTENCLR_EL1),
|
||||
PMREG_SET(SYS_PMOVSSET_EL0, SYS_PMOVSCLR_EL0),
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a VM, and check if KVM handles the userspace accesses of
|
||||
* the PMU register sets in @validity_check_reg_sets[] correctly.
|
||||
*/
|
||||
static void run_pmregs_validity_test(uint64_t pmcr_n)
|
||||
{
|
||||
int i;
|
||||
struct kvm_vcpu *vcpu;
|
||||
uint64_t set_reg_id, clr_reg_id, reg_val;
|
||||
uint64_t valid_counters_mask, max_counters_mask;
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, false);
|
||||
vcpu = vpmu_vm.vcpu;
|
||||
|
||||
valid_counters_mask = get_counters_mask(pmcr_n);
|
||||
max_counters_mask = get_counters_mask(ARMV8_PMU_MAX_COUNTERS);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(validity_check_reg_sets); i++) {
|
||||
set_reg_id = validity_check_reg_sets[i].set_reg_id;
|
||||
clr_reg_id = validity_check_reg_sets[i].clr_reg_id;
|
||||
|
||||
/*
|
||||
* Test if the 'set' and 'clr' variants of the registers
|
||||
* are initialized based on the number of valid counters.
|
||||
*/
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(set_reg_id), ®_val);
|
||||
TEST_ASSERT((reg_val & (~valid_counters_mask)) == 0,
|
||||
"Initial read of set_reg: 0x%llx has unimplemented counters enabled: 0x%lx\n",
|
||||
KVM_ARM64_SYS_REG(set_reg_id), reg_val);
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(clr_reg_id), ®_val);
|
||||
TEST_ASSERT((reg_val & (~valid_counters_mask)) == 0,
|
||||
"Initial read of clr_reg: 0x%llx has unimplemented counters enabled: 0x%lx\n",
|
||||
KVM_ARM64_SYS_REG(clr_reg_id), reg_val);
|
||||
|
||||
/*
|
||||
* Using the 'set' variant, force-set the register to the
|
||||
* max number of possible counters and test if KVM discards
|
||||
* the bits for unimplemented counters as it should.
|
||||
*/
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(set_reg_id), max_counters_mask);
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(set_reg_id), ®_val);
|
||||
TEST_ASSERT((reg_val & (~valid_counters_mask)) == 0,
|
||||
"Read of set_reg: 0x%llx has unimplemented counters enabled: 0x%lx\n",
|
||||
KVM_ARM64_SYS_REG(set_reg_id), reg_val);
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(clr_reg_id), ®_val);
|
||||
TEST_ASSERT((reg_val & (~valid_counters_mask)) == 0,
|
||||
"Read of clr_reg: 0x%llx has unimplemented counters enabled: 0x%lx\n",
|
||||
KVM_ARM64_SYS_REG(clr_reg_id), reg_val);
|
||||
}
|
||||
|
||||
destroy_vpmu_vm();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a guest with one vCPU, and attempt to set the PMCR_EL0.N for
|
||||
* the vCPU to @pmcr_n, which is larger than the host value.
|
||||
* The attempt should fail as @pmcr_n is too big to set for the vCPU.
|
||||
*/
|
||||
static void run_error_test(uint64_t pmcr_n)
|
||||
{
|
||||
pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n);
|
||||
|
||||
test_create_vpmu_vm_with_pmcr_n(pmcr_n, true);
|
||||
destroy_vpmu_vm();
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the default number of implemented PMU event counters excluding
|
||||
* the cycle counter (i.e. PMCR_EL0.N value) for the guest.
|
||||
*/
|
||||
static uint64_t get_pmcr_n_limit(void)
|
||||
{
|
||||
uint64_t pmcr;
|
||||
|
||||
create_vpmu_vm(guest_code);
|
||||
vcpu_get_reg(vpmu_vm.vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0), &pmcr);
|
||||
destroy_vpmu_vm();
|
||||
return get_pmcr_n(pmcr);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint64_t i, pmcr_n;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3));
|
||||
|
||||
pmcr_n = get_pmcr_n_limit();
|
||||
for (i = 0; i <= pmcr_n; i++) {
|
||||
run_access_test(i);
|
||||
run_pmregs_validity_test(i);
|
||||
}
|
||||
|
||||
for (i = pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++)
|
||||
run_error_test(i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -104,6 +104,7 @@ enum {
|
||||
#define ESR_EC_SHIFT 26
|
||||
#define ESR_EC_MASK (ESR_EC_NUM - 1)
|
||||
|
||||
#define ESR_EC_UNKNOWN 0x0
|
||||
#define ESR_EC_SVC64 0x15
|
||||
#define ESR_EC_IABT 0x21
|
||||
#define ESR_EC_DABT 0x25
|
||||
|
||||
@@ -518,9 +518,9 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
|
||||
err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®);
|
||||
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
|
||||
|
||||
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4), val) != 0xf;
|
||||
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64), val) == 0;
|
||||
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16), val) != 0;
|
||||
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val) != 0xf;
|
||||
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val) == 0;
|
||||
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val) != 0;
|
||||
|
||||
close(vcpu_fd);
|
||||
close(vm_fd);
|
||||
|
||||
@@ -25,6 +25,8 @@ bool filter_reg(__u64 reg)
|
||||
* the visibility of the ISA_EXT register itself.
|
||||
*
|
||||
* Based on above, we should filter-out all ISA_EXT registers.
|
||||
*
|
||||
* Note: The below list is alphabetically sorted.
|
||||
*/
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_A:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_C:
|
||||
@@ -33,21 +35,23 @@ bool filter_reg(__u64 reg)
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_H:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_I:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_M:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVPBMT:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_V:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SMSTATEEN:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSTC:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVINVAL:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHINTPAUSE:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVNAPOT:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVPBMT:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBA:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBB:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBS:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOM:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOZ:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBB:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_V:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVNAPOT:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBA:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBS:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICNTR:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICOND:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICSR:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIFENCEI:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHINTPAUSE:
|
||||
case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHPM:
|
||||
return true;
|
||||
/* AIA registers are always available when Ssaia can't be disabled */
|
||||
@@ -112,11 +116,13 @@ void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *config_id_to_str(__u64 id)
|
||||
static const char *config_id_to_str(const char *prefix, __u64 id)
|
||||
{
|
||||
/* reg_off is the offset into struct kvm_riscv_config */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CONFIG);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_CONFIG);
|
||||
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_CONFIG_REG(isa):
|
||||
return "KVM_REG_RISCV_CONFIG_REG(isa)";
|
||||
@@ -134,11 +140,7 @@ static const char *config_id_to_str(__u64 id)
|
||||
return "KVM_REG_RISCV_CONFIG_REG(satp_mode)";
|
||||
}
|
||||
|
||||
/*
|
||||
* Config regs would grow regularly with new pseudo reg added, so
|
||||
* just show raw id to indicate a new pseudo config reg.
|
||||
*/
|
||||
return strdup_printf("KVM_REG_RISCV_CONFIG_REG(%lld) /* UNKNOWN */", reg_off);
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *core_id_to_str(const char *prefix, __u64 id)
|
||||
@@ -146,6 +148,8 @@ static const char *core_id_to_str(const char *prefix, __u64 id)
|
||||
/* reg_off is the offset into struct kvm_riscv_core */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CORE);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_CORE);
|
||||
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_CORE_REG(regs.pc):
|
||||
return "KVM_REG_RISCV_CORE_REG(regs.pc)";
|
||||
@@ -176,14 +180,15 @@ static const char *core_id_to_str(const char *prefix, __u64 id)
|
||||
return "KVM_REG_RISCV_CORE_REG(mode)";
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown core reg id: 0x%llx", prefix, id);
|
||||
return NULL;
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
#define RISCV_CSR_GENERAL(csr) \
|
||||
"KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(" #csr ")"
|
||||
#define RISCV_CSR_AIA(csr) \
|
||||
"KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_REG(" #csr ")"
|
||||
#define RISCV_CSR_SMSTATEEN(csr) \
|
||||
"KVM_REG_RISCV_CSR_SMSTATEEN | KVM_REG_RISCV_CSR_REG(" #csr ")"
|
||||
|
||||
static const char *general_csr_id_to_str(__u64 reg_off)
|
||||
{
|
||||
@@ -209,10 +214,11 @@ static const char *general_csr_id_to_str(__u64 reg_off)
|
||||
return RISCV_CSR_GENERAL(satp);
|
||||
case KVM_REG_RISCV_CSR_REG(scounteren):
|
||||
return RISCV_CSR_GENERAL(scounteren);
|
||||
case KVM_REG_RISCV_CSR_REG(senvcfg):
|
||||
return RISCV_CSR_GENERAL(senvcfg);
|
||||
}
|
||||
|
||||
TEST_FAIL("Unknown general csr reg: 0x%llx", reg_off);
|
||||
return NULL;
|
||||
return strdup_printf("KVM_REG_RISCV_CSR_GENERAL | %lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *aia_csr_id_to_str(__u64 reg_off)
|
||||
@@ -235,7 +241,18 @@ static const char *aia_csr_id_to_str(__u64 reg_off)
|
||||
return RISCV_CSR_AIA(iprio2h);
|
||||
}
|
||||
|
||||
TEST_FAIL("Unknown aia csr reg: 0x%llx", reg_off);
|
||||
return strdup_printf("KVM_REG_RISCV_CSR_AIA | %lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *smstateen_csr_id_to_str(__u64 reg_off)
|
||||
{
|
||||
/* reg_off is the offset into struct kvm_riscv_smstateen_csr */
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_CSR_SMSTATEEN_REG(sstateen0):
|
||||
return RISCV_CSR_SMSTATEEN(sstateen0);
|
||||
}
|
||||
|
||||
TEST_FAIL("Unknown smstateen csr reg: 0x%llx", reg_off);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -244,6 +261,8 @@ static const char *csr_id_to_str(const char *prefix, __u64 id)
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CSR);
|
||||
__u64 reg_subtype = reg_off & KVM_REG_RISCV_SUBTYPE_MASK;
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_CSR);
|
||||
|
||||
reg_off &= ~KVM_REG_RISCV_SUBTYPE_MASK;
|
||||
|
||||
switch (reg_subtype) {
|
||||
@@ -251,10 +270,11 @@ static const char *csr_id_to_str(const char *prefix, __u64 id)
|
||||
return general_csr_id_to_str(reg_off);
|
||||
case KVM_REG_RISCV_CSR_AIA:
|
||||
return aia_csr_id_to_str(reg_off);
|
||||
case KVM_REG_RISCV_CSR_SMSTATEEN:
|
||||
return smstateen_csr_id_to_str(reg_off);
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown csr subtype: 0x%llx", prefix, reg_subtype);
|
||||
return NULL;
|
||||
return strdup_printf("%lld | %lld /* UNKNOWN */", reg_subtype, reg_off);
|
||||
}
|
||||
|
||||
static const char *timer_id_to_str(const char *prefix, __u64 id)
|
||||
@@ -262,6 +282,8 @@ static const char *timer_id_to_str(const char *prefix, __u64 id)
|
||||
/* reg_off is the offset into struct kvm_riscv_timer */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_TIMER);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_TIMER);
|
||||
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_TIMER_REG(frequency):
|
||||
return "KVM_REG_RISCV_TIMER_REG(frequency)";
|
||||
@@ -273,8 +295,7 @@ static const char *timer_id_to_str(const char *prefix, __u64 id)
|
||||
return "KVM_REG_RISCV_TIMER_REG(state)";
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown timer reg id: 0x%llx", prefix, id);
|
||||
return NULL;
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *fp_f_id_to_str(const char *prefix, __u64 id)
|
||||
@@ -282,6 +303,8 @@ static const char *fp_f_id_to_str(const char *prefix, __u64 id)
|
||||
/* reg_off is the offset into struct __riscv_f_ext_state */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_FP_F);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_FP_F);
|
||||
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_FP_F_REG(f[0]) ...
|
||||
KVM_REG_RISCV_FP_F_REG(f[31]):
|
||||
@@ -290,8 +313,7 @@ static const char *fp_f_id_to_str(const char *prefix, __u64 id)
|
||||
return "KVM_REG_RISCV_FP_F_REG(fcsr)";
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown fp_f reg id: 0x%llx", prefix, id);
|
||||
return NULL;
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *fp_d_id_to_str(const char *prefix, __u64 id)
|
||||
@@ -299,6 +321,8 @@ static const char *fp_d_id_to_str(const char *prefix, __u64 id)
|
||||
/* reg_off is the offset into struct __riscv_d_ext_state */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_FP_D);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_FP_D);
|
||||
|
||||
switch (reg_off) {
|
||||
case KVM_REG_RISCV_FP_D_REG(f[0]) ...
|
||||
KVM_REG_RISCV_FP_D_REG(f[31]):
|
||||
@@ -307,96 +331,93 @@ static const char *fp_d_id_to_str(const char *prefix, __u64 id)
|
||||
return "KVM_REG_RISCV_FP_D_REG(fcsr)";
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown fp_d reg id: 0x%llx", prefix, id);
|
||||
return NULL;
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
static const char *isa_ext_id_to_str(__u64 id)
|
||||
#define KVM_ISA_EXT_ARR(ext) \
|
||||
[KVM_RISCV_ISA_EXT_##ext] = "KVM_RISCV_ISA_EXT_" #ext
|
||||
|
||||
static const char *isa_ext_id_to_str(const char *prefix, __u64 id)
|
||||
{
|
||||
/* reg_off is the offset into unsigned long kvm_isa_ext_arr[] */
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_ISA_EXT);
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_ISA_EXT);
|
||||
|
||||
static const char * const kvm_isa_ext_reg_name[] = {
|
||||
"KVM_RISCV_ISA_EXT_A",
|
||||
"KVM_RISCV_ISA_EXT_C",
|
||||
"KVM_RISCV_ISA_EXT_D",
|
||||
"KVM_RISCV_ISA_EXT_F",
|
||||
"KVM_RISCV_ISA_EXT_H",
|
||||
"KVM_RISCV_ISA_EXT_I",
|
||||
"KVM_RISCV_ISA_EXT_M",
|
||||
"KVM_RISCV_ISA_EXT_SVPBMT",
|
||||
"KVM_RISCV_ISA_EXT_SSTC",
|
||||
"KVM_RISCV_ISA_EXT_SVINVAL",
|
||||
"KVM_RISCV_ISA_EXT_ZIHINTPAUSE",
|
||||
"KVM_RISCV_ISA_EXT_ZICBOM",
|
||||
"KVM_RISCV_ISA_EXT_ZICBOZ",
|
||||
"KVM_RISCV_ISA_EXT_ZBB",
|
||||
"KVM_RISCV_ISA_EXT_SSAIA",
|
||||
"KVM_RISCV_ISA_EXT_V",
|
||||
"KVM_RISCV_ISA_EXT_SVNAPOT",
|
||||
"KVM_RISCV_ISA_EXT_ZBA",
|
||||
"KVM_RISCV_ISA_EXT_ZBS",
|
||||
"KVM_RISCV_ISA_EXT_ZICNTR",
|
||||
"KVM_RISCV_ISA_EXT_ZICSR",
|
||||
"KVM_RISCV_ISA_EXT_ZIFENCEI",
|
||||
"KVM_RISCV_ISA_EXT_ZIHPM",
|
||||
KVM_ISA_EXT_ARR(A),
|
||||
KVM_ISA_EXT_ARR(C),
|
||||
KVM_ISA_EXT_ARR(D),
|
||||
KVM_ISA_EXT_ARR(F),
|
||||
KVM_ISA_EXT_ARR(H),
|
||||
KVM_ISA_EXT_ARR(I),
|
||||
KVM_ISA_EXT_ARR(M),
|
||||
KVM_ISA_EXT_ARR(V),
|
||||
KVM_ISA_EXT_ARR(SMSTATEEN),
|
||||
KVM_ISA_EXT_ARR(SSAIA),
|
||||
KVM_ISA_EXT_ARR(SSTC),
|
||||
KVM_ISA_EXT_ARR(SVINVAL),
|
||||
KVM_ISA_EXT_ARR(SVNAPOT),
|
||||
KVM_ISA_EXT_ARR(SVPBMT),
|
||||
KVM_ISA_EXT_ARR(ZBA),
|
||||
KVM_ISA_EXT_ARR(ZBB),
|
||||
KVM_ISA_EXT_ARR(ZBS),
|
||||
KVM_ISA_EXT_ARR(ZICBOM),
|
||||
KVM_ISA_EXT_ARR(ZICBOZ),
|
||||
KVM_ISA_EXT_ARR(ZICNTR),
|
||||
KVM_ISA_EXT_ARR(ZICOND),
|
||||
KVM_ISA_EXT_ARR(ZICSR),
|
||||
KVM_ISA_EXT_ARR(ZIFENCEI),
|
||||
KVM_ISA_EXT_ARR(ZIHINTPAUSE),
|
||||
KVM_ISA_EXT_ARR(ZIHPM),
|
||||
};
|
||||
|
||||
if (reg_off >= ARRAY_SIZE(kvm_isa_ext_reg_name)) {
|
||||
/*
|
||||
* isa_ext regs would grow regularly with new isa extension added, so
|
||||
* just show "reg" to indicate a new extension.
|
||||
*/
|
||||
if (reg_off >= ARRAY_SIZE(kvm_isa_ext_reg_name))
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
return kvm_isa_ext_reg_name[reg_off];
|
||||
}
|
||||
|
||||
#define KVM_SBI_EXT_ARR(ext) \
|
||||
[ext] = "KVM_REG_RISCV_SBI_SINGLE | " #ext
|
||||
|
||||
static const char *sbi_ext_single_id_to_str(__u64 reg_off)
|
||||
{
|
||||
/* reg_off is KVM_RISCV_SBI_EXT_ID */
|
||||
static const char * const kvm_sbi_ext_reg_name[] = {
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_V01",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_TIME",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_IPI",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_RFENCE",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_SRST",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_HSM",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_PMU",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_EXPERIMENTAL",
|
||||
"KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_VENDOR",
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_V01),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_TIME),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_IPI),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_RFENCE),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_SRST),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_HSM),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_PMU),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_EXPERIMENTAL),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_VENDOR),
|
||||
KVM_SBI_EXT_ARR(KVM_RISCV_SBI_EXT_DBCN),
|
||||
};
|
||||
|
||||
if (reg_off >= ARRAY_SIZE(kvm_sbi_ext_reg_name)) {
|
||||
/*
|
||||
* sbi_ext regs would grow regularly with new sbi extension added, so
|
||||
* just show "reg" to indicate a new extension.
|
||||
*/
|
||||
if (reg_off >= ARRAY_SIZE(kvm_sbi_ext_reg_name))
|
||||
return strdup_printf("KVM_REG_RISCV_SBI_SINGLE | %lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
|
||||
return kvm_sbi_ext_reg_name[reg_off];
|
||||
}
|
||||
|
||||
static const char *sbi_ext_multi_id_to_str(__u64 reg_subtype, __u64 reg_off)
|
||||
{
|
||||
if (reg_off > KVM_REG_RISCV_SBI_MULTI_REG_LAST) {
|
||||
/*
|
||||
* sbi_ext regs would grow regularly with new sbi extension added, so
|
||||
* just show "reg" to indicate a new extension.
|
||||
*/
|
||||
return strdup_printf("%lld /* UNKNOWN */", reg_off);
|
||||
}
|
||||
const char *unknown = "";
|
||||
|
||||
if (reg_off > KVM_REG_RISCV_SBI_MULTI_REG_LAST)
|
||||
unknown = " /* UNKNOWN */";
|
||||
|
||||
switch (reg_subtype) {
|
||||
case KVM_REG_RISCV_SBI_MULTI_EN:
|
||||
return strdup_printf("KVM_REG_RISCV_SBI_MULTI_EN | %lld", reg_off);
|
||||
return strdup_printf("KVM_REG_RISCV_SBI_MULTI_EN | %lld%s", reg_off, unknown);
|
||||
case KVM_REG_RISCV_SBI_MULTI_DIS:
|
||||
return strdup_printf("KVM_REG_RISCV_SBI_MULTI_DIS | %lld", reg_off);
|
||||
return strdup_printf("KVM_REG_RISCV_SBI_MULTI_DIS | %lld%s", reg_off, unknown);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return strdup_printf("%lld | %lld /* UNKNOWN */", reg_subtype, reg_off);
|
||||
}
|
||||
|
||||
static const char *sbi_ext_id_to_str(const char *prefix, __u64 id)
|
||||
@@ -404,6 +425,8 @@ static const char *sbi_ext_id_to_str(const char *prefix, __u64 id)
|
||||
__u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_SBI_EXT);
|
||||
__u64 reg_subtype = reg_off & KVM_REG_RISCV_SUBTYPE_MASK;
|
||||
|
||||
assert((id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_SBI_EXT);
|
||||
|
||||
reg_off &= ~KVM_REG_RISCV_SUBTYPE_MASK;
|
||||
|
||||
switch (reg_subtype) {
|
||||
@@ -414,8 +437,7 @@ static const char *sbi_ext_id_to_str(const char *prefix, __u64 id)
|
||||
return sbi_ext_multi_id_to_str(reg_subtype, reg_off);
|
||||
}
|
||||
|
||||
TEST_FAIL("%s: Unknown sbi ext subtype: 0x%llx", prefix, reg_subtype);
|
||||
return NULL;
|
||||
return strdup_printf("%lld | %lld /* UNKNOWN */", reg_subtype, reg_off);
|
||||
}
|
||||
|
||||
void print_reg(const char *prefix, __u64 id)
|
||||
@@ -436,14 +458,14 @@ void print_reg(const char *prefix, __u64 id)
|
||||
reg_size = "KVM_REG_SIZE_U128";
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("%s: Unexpected reg size: 0x%llx in reg id: 0x%llx",
|
||||
prefix, (id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id);
|
||||
printf("\tKVM_REG_RISCV | (%lld << KVM_REG_SIZE_SHIFT) | 0x%llx /* UNKNOWN */,",
|
||||
(id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id & REG_MASK);
|
||||
}
|
||||
|
||||
switch (id & KVM_REG_RISCV_TYPE_MASK) {
|
||||
case KVM_REG_RISCV_CONFIG:
|
||||
printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_CONFIG | %s,\n",
|
||||
reg_size, config_id_to_str(id));
|
||||
reg_size, config_id_to_str(prefix, id));
|
||||
break;
|
||||
case KVM_REG_RISCV_CORE:
|
||||
printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_CORE | %s,\n",
|
||||
@@ -467,15 +489,15 @@ void print_reg(const char *prefix, __u64 id)
|
||||
break;
|
||||
case KVM_REG_RISCV_ISA_EXT:
|
||||
printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_ISA_EXT | %s,\n",
|
||||
reg_size, isa_ext_id_to_str(id));
|
||||
reg_size, isa_ext_id_to_str(prefix, id));
|
||||
break;
|
||||
case KVM_REG_RISCV_SBI_EXT:
|
||||
printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_SBI_EXT | %s,\n",
|
||||
reg_size, sbi_ext_id_to_str(prefix, id));
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("%s: Unexpected reg type: 0x%llx in reg id: 0x%llx", prefix,
|
||||
(id & KVM_REG_RISCV_TYPE_MASK) >> KVM_REG_RISCV_TYPE_SHIFT, id);
|
||||
printf("\tKVM_REG_RISCV | %s | 0x%llx /* UNKNOWN */,",
|
||||
reg_size, id & REG_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +554,7 @@ static __u64 base_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sip),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(satp),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(scounteren),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(senvcfg),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(frequency),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(time),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(compare),
|
||||
@@ -545,6 +568,7 @@ static __u64 base_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_PMU,
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_EXPERIMENTAL,
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_VENDOR,
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_DBCN,
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_MULTI_EN | 0,
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_MULTI_DIS | 0,
|
||||
};
|
||||
@@ -603,6 +627,10 @@ static __u64 zicntr_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICNTR,
|
||||
};
|
||||
|
||||
static __u64 zicond_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICOND,
|
||||
};
|
||||
|
||||
static __u64 zicsr_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICSR,
|
||||
};
|
||||
@@ -626,6 +654,11 @@ static __u64 aia_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA,
|
||||
};
|
||||
|
||||
static __u64 smstateen_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_SMSTATEEN | KVM_REG_RISCV_CSR_SMSTATEEN_REG(sstateen0),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SMSTATEEN,
|
||||
};
|
||||
|
||||
static __u64 fp_f_regs[] = {
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[0]),
|
||||
KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[1]),
|
||||
@@ -725,6 +758,8 @@ static __u64 fp_d_regs[] = {
|
||||
{"zbs", .feature = KVM_RISCV_ISA_EXT_ZBS, .regs = zbs_regs, .regs_n = ARRAY_SIZE(zbs_regs),}
|
||||
#define ZICNTR_REGS_SUBLIST \
|
||||
{"zicntr", .feature = KVM_RISCV_ISA_EXT_ZICNTR, .regs = zicntr_regs, .regs_n = ARRAY_SIZE(zicntr_regs),}
|
||||
#define ZICOND_REGS_SUBLIST \
|
||||
{"zicond", .feature = KVM_RISCV_ISA_EXT_ZICOND, .regs = zicond_regs, .regs_n = ARRAY_SIZE(zicond_regs),}
|
||||
#define ZICSR_REGS_SUBLIST \
|
||||
{"zicsr", .feature = KVM_RISCV_ISA_EXT_ZICSR, .regs = zicsr_regs, .regs_n = ARRAY_SIZE(zicsr_regs),}
|
||||
#define ZIFENCEI_REGS_SUBLIST \
|
||||
@@ -733,6 +768,8 @@ static __u64 fp_d_regs[] = {
|
||||
{"zihpm", .feature = KVM_RISCV_ISA_EXT_ZIHPM, .regs = zihpm_regs, .regs_n = ARRAY_SIZE(zihpm_regs),}
|
||||
#define AIA_REGS_SUBLIST \
|
||||
{"aia", .feature = KVM_RISCV_ISA_EXT_SSAIA, .regs = aia_regs, .regs_n = ARRAY_SIZE(aia_regs),}
|
||||
#define SMSTATEEN_REGS_SUBLIST \
|
||||
{"smstateen", .feature = KVM_RISCV_ISA_EXT_SMSTATEEN, .regs = smstateen_regs, .regs_n = ARRAY_SIZE(smstateen_regs),}
|
||||
#define FP_F_REGS_SUBLIST \
|
||||
{"fp_f", .feature = KVM_RISCV_ISA_EXT_F, .regs = fp_f_regs, \
|
||||
.regs_n = ARRAY_SIZE(fp_f_regs),}
|
||||
@@ -828,6 +865,14 @@ static struct vcpu_reg_list zicntr_config = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list zicond_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
ZICOND_REGS_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list zicsr_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
@@ -860,6 +905,14 @@ static struct vcpu_reg_list aia_config = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list smstateen_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
SMSTATEEN_REGS_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list fp_f_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
@@ -888,10 +941,12 @@ struct vcpu_reg_list *vcpu_configs[] = {
|
||||
&zbb_config,
|
||||
&zbs_config,
|
||||
&zicntr_config,
|
||||
&zicond_config,
|
||||
&zicsr_config,
|
||||
&zifencei_config,
|
||||
&zihpm_config,
|
||||
&aia_config,
|
||||
&smstateen_config,
|
||||
&fp_f_config,
|
||||
&fp_d_config,
|
||||
};
|
||||
|
||||
47
tools/testing/selftests/kvm/x86_64/hwcr_msr_test.c
Normal file
47
tools/testing/selftests/kvm/x86_64/hwcr_msr_test.c
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for program_invocation_short_name */
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "vmx.h"
|
||||
|
||||
void test_hwcr_bit(struct kvm_vcpu *vcpu, unsigned int bit)
|
||||
{
|
||||
const uint64_t ignored = BIT_ULL(3) | BIT_ULL(6) | BIT_ULL(8);
|
||||
const uint64_t valid = BIT_ULL(18) | BIT_ULL(24);
|
||||
const uint64_t legal = ignored | valid;
|
||||
uint64_t val = BIT_ULL(bit);
|
||||
uint64_t actual;
|
||||
int r;
|
||||
|
||||
r = _vcpu_set_msr(vcpu, MSR_K7_HWCR, val);
|
||||
TEST_ASSERT(val & ~legal ? !r : r == 1,
|
||||
"Expected KVM_SET_MSRS(MSR_K7_HWCR) = 0x%lx to %s",
|
||||
val, val & ~legal ? "fail" : "succeed");
|
||||
|
||||
actual = vcpu_get_msr(vcpu, MSR_K7_HWCR);
|
||||
TEST_ASSERT(actual == (val & valid),
|
||||
"Bit %u: unexpected HWCR 0x%lx; expected 0x%lx",
|
||||
bit, actual, (val & valid));
|
||||
|
||||
vcpu_set_msr(vcpu, MSR_K7_HWCR, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_vcpu *vcpu;
|
||||
unsigned int bit;
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, NULL);
|
||||
|
||||
for (bit = 0; bit < BITS_PER_LONG; bit++)
|
||||
test_hwcr_bit(vcpu, bit);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
Reference in New Issue
Block a user