mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-17 05:45:20 +00:00
KVM/arm64 updates for 6.7
- 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 -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSNXHjWXuzMZutrKNKivnWIJHzdFgUCZUFJRgAKCRCivnWIJHzd FtgYAP9cMsc1Mhlw3jNQnTc6q0cbTulD/SoEDPUat1dXMqjs+gEAnskwQTrTX834 fgGQeCAyp7Gmar+KeP64H0xm8kPSpAw= =R4M7 -----END PGP SIGNATURE----- Merge tag 'kvmarm-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD KVM/arm64 updates for 6.7 - 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
This commit is contained in:
commit
45b890f768
@ -3422,6 +3422,8 @@ return indicates the attribute is implemented. It does not necessarily
|
|||||||
indicate that the attribute can be read or written in the device's
|
indicate that the attribute can be read or written in the device's
|
||||||
current state. "addr" is ignored.
|
current state. "addr" is ignored.
|
||||||
|
|
||||||
|
.. _KVM_ARM_VCPU_INIT:
|
||||||
|
|
||||||
4.82 KVM_ARM_VCPU_INIT
|
4.82 KVM_ARM_VCPU_INIT
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -6140,6 +6142,56 @@ writes to the CNTVCT_EL0 and CNTPCT_EL0 registers using the SET_ONE_REG
|
|||||||
interface. No error will be returned, but the resulting offset will not be
|
interface. No error will be returned, but the resulting offset will not be
|
||||||
applied.
|
applied.
|
||||||
|
|
||||||
|
.. _KVM_ARM_GET_REG_WRITABLE_MASKS:
|
||||||
|
|
||||||
|
4.139 KVM_ARM_GET_REG_WRITABLE_MASKS
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
:Capability: KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES
|
||||||
|
:Architectures: arm64
|
||||||
|
:Type: vm ioctl
|
||||||
|
:Parameters: struct reg_mask_range (in/out)
|
||||||
|
:Returns: 0 on success, < 0 on error
|
||||||
|
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE 0
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
|
||||||
|
|
||||||
|
struct reg_mask_range {
|
||||||
|
__u64 addr; /* Pointer to mask array */
|
||||||
|
__u32 range; /* Requested range */
|
||||||
|
__u32 reserved[13];
|
||||||
|
};
|
||||||
|
|
||||||
|
This ioctl copies the writable masks for a selected range of registers to
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
The ``addr`` field is a pointer to the destination array where KVM copies
|
||||||
|
the writable masks.
|
||||||
|
|
||||||
|
The ``range`` field indicates the requested range of registers.
|
||||||
|
``KVM_CHECK_EXTENSION`` for the ``KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES``
|
||||||
|
capability returns the supported ranges, expressed as a set of flags. Each
|
||||||
|
flag's bit index represents a possible value for the ``range`` field.
|
||||||
|
All other values are reserved for future use and KVM may return an error.
|
||||||
|
|
||||||
|
The ``reserved[13]`` array is reserved for future use and should be 0, or
|
||||||
|
KVM may return an error.
|
||||||
|
|
||||||
|
KVM_ARM_FEATURE_ID_RANGE (0)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The Feature ID range is defined as the AArch64 System register space with
|
||||||
|
op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7}, op2=={0-7}.
|
||||||
|
|
||||||
|
The mask returned array pointed to by ``addr`` is indexed by the macro
|
||||||
|
``ARM64_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2)``, allowing userspace
|
||||||
|
to know what fields can be changed for the system register described by
|
||||||
|
``op0, op1, crn, crm, op2``. KVM rejects ID register values that describe a
|
||||||
|
superset of the features supported by the system.
|
||||||
|
|
||||||
5. The kvm_run structure
|
5. The kvm_run structure
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ ARM
|
|||||||
hypercalls
|
hypercalls
|
||||||
pvtime
|
pvtime
|
||||||
ptp_kvm
|
ptp_kvm
|
||||||
|
vcpu-features
|
||||||
|
48
Documentation/virt/kvm/arm/vcpu-features.rst
Normal file
48
Documentation/virt/kvm/arm/vcpu-features.rst
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
===============================
|
||||||
|
vCPU feature selection on arm64
|
||||||
|
===============================
|
||||||
|
|
||||||
|
KVM/arm64 provides two mechanisms that allow userspace to configure
|
||||||
|
the CPU features presented to the guest.
|
||||||
|
|
||||||
|
KVM_ARM_VCPU_INIT
|
||||||
|
=================
|
||||||
|
|
||||||
|
The ``KVM_ARM_VCPU_INIT`` ioctl accepts a bitmap of feature flags
|
||||||
|
(``struct kvm_vcpu_init::features``). Features enabled by this interface are
|
||||||
|
*opt-in* and may change/extend UAPI. See :ref:`KVM_ARM_VCPU_INIT` for complete
|
||||||
|
documentation of the features controlled by the ioctl.
|
||||||
|
|
||||||
|
Otherwise, all CPU features supported by KVM are described by the architected
|
||||||
|
ID registers.
|
||||||
|
|
||||||
|
The ID Registers
|
||||||
|
================
|
||||||
|
|
||||||
|
The Arm architecture specifies a range of *ID Registers* that describe the set
|
||||||
|
of architectural features supported by the CPU implementation. KVM initializes
|
||||||
|
the guest's ID registers to the maximum set of CPU features supported by the
|
||||||
|
system. The ID register values may be VM-scoped in KVM, meaning that the
|
||||||
|
values could be shared for all vCPUs in a VM.
|
||||||
|
|
||||||
|
KVM allows userspace to *opt-out* of certain CPU features described by the ID
|
||||||
|
registers by writing values to them via the ``KVM_SET_ONE_REG`` ioctl. The ID
|
||||||
|
registers are mutable until the VM has started, i.e. userspace has called
|
||||||
|
``KVM_RUN`` on at least one vCPU in the VM. Userspace can discover what fields
|
||||||
|
are mutable in the ID registers using the ``KVM_ARM_GET_REG_WRITABLE_MASKS``.
|
||||||
|
See the :ref:`ioctl documentation <KVM_ARM_GET_REG_WRITABLE_MASKS>` for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
Userspace is allowed to *limit* or *mask* CPU features according to the rules
|
||||||
|
outlined by the architecture in DDI0487J.a D19.1.3 'Principles of the ID
|
||||||
|
scheme for fields in ID register'. KVM does not allow ID register values that
|
||||||
|
exceed the capabilities of the system.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
It is **strongly recommended** that userspace modify the ID register values
|
||||||
|
before accessing the rest of the vCPU's CPU register state. KVM may use the
|
||||||
|
ID register values to control feature emulation. Interleaving ID register
|
||||||
|
modification with other system register accesses may lead to unpredictable
|
||||||
|
behavior.
|
@ -59,6 +59,13 @@ Groups:
|
|||||||
It is invalid to mix calls with KVM_VGIC_V3_ADDR_TYPE_REDIST and
|
It is invalid to mix calls with KVM_VGIC_V3_ADDR_TYPE_REDIST and
|
||||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION attributes.
|
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION attributes.
|
||||||
|
|
||||||
|
Note that to obtain reproducible results (the same VCPU being associated
|
||||||
|
with the same redistributor across a save/restore operation), VCPU creation
|
||||||
|
order, redistributor region creation order as well as the respective
|
||||||
|
interleaves of VCPU and region creation MUST be preserved. Any change in
|
||||||
|
either ordering may result in a different vcpu_id/redistributor association,
|
||||||
|
resulting in a VM that will fail to run at restore time.
|
||||||
|
|
||||||
Errors:
|
Errors:
|
||||||
|
|
||||||
======= =============================================================
|
======= =============================================================
|
||||||
|
@ -102,7 +102,9 @@
|
|||||||
#define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC)
|
#define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC)
|
||||||
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
|
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
|
||||||
|
|
||||||
#define HCRX_GUEST_FLAGS (HCRX_EL2_SMPME | HCRX_EL2_TCR2En)
|
#define HCRX_GUEST_FLAGS \
|
||||||
|
(HCRX_EL2_SMPME | HCRX_EL2_TCR2En | \
|
||||||
|
(cpus_have_final_cap(ARM64_HAS_MOPS) ? (HCRX_EL2_MSCEn | HCRX_EL2_MCE2) : 0))
|
||||||
#define HCRX_HOST_FLAGS (HCRX_EL2_MSCEn | HCRX_EL2_TCR2En)
|
#define HCRX_HOST_FLAGS (HCRX_EL2_MSCEn | HCRX_EL2_TCR2En)
|
||||||
|
|
||||||
/* TCR_EL2 Registers bits */
|
/* TCR_EL2 Registers bits */
|
||||||
|
@ -54,6 +54,11 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu);
|
|||||||
int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
|
int kvm_inject_nested_sync(struct kvm_vcpu *vcpu, u64 esr_el2);
|
||||||
int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
|
int kvm_inject_nested_irq(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
static inline bool vcpu_has_feature(const struct kvm_vcpu *vcpu, int feature)
|
||||||
|
{
|
||||||
|
return test_bit(feature, vcpu->kvm->arch.vcpu_features);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
|
#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
|
||||||
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
@ -62,7 +67,7 @@ static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
|||||||
#else
|
#else
|
||||||
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features);
|
return vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -465,7 +470,7 @@ static inline bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
static inline unsigned long kvm_vcpu_get_mpidr_aff(struct kvm_vcpu *vcpu)
|
static inline unsigned long kvm_vcpu_get_mpidr_aff(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return vcpu_read_sys_reg(vcpu, MPIDR_EL1) & MPIDR_HWID_BITMASK;
|
return __vcpu_sys_reg(vcpu, MPIDR_EL1) & MPIDR_HWID_BITMASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
|
static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
|
||||||
@ -565,12 +570,6 @@ static __always_inline void kvm_incr_pc(struct kvm_vcpu *vcpu)
|
|||||||
vcpu_set_flag((v), e); \
|
vcpu_set_flag((v), e); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
static inline bool vcpu_has_feature(struct kvm_vcpu *vcpu, int feature)
|
|
||||||
{
|
|
||||||
return test_bit(feature, vcpu->arch.features);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __always_inline void kvm_write_cptr_el2(u64 val)
|
static __always_inline void kvm_write_cptr_el2(u64 val)
|
||||||
{
|
{
|
||||||
if (has_vhe() || has_hvhe())
|
if (has_vhe() || has_hvhe())
|
||||||
|
@ -78,7 +78,7 @@ extern unsigned int __ro_after_init kvm_sve_max_vl;
|
|||||||
int __init kvm_arm_init_sve(void);
|
int __init kvm_arm_init_sve(void);
|
||||||
|
|
||||||
u32 __attribute_const__ kvm_target_cpu(void);
|
u32 __attribute_const__ kvm_target_cpu(void);
|
||||||
int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
|
void kvm_reset_vcpu(struct kvm_vcpu *vcpu);
|
||||||
void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu);
|
void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
struct kvm_hyp_memcache {
|
struct kvm_hyp_memcache {
|
||||||
@ -158,6 +158,16 @@ struct kvm_s2_mmu {
|
|||||||
phys_addr_t pgd_phys;
|
phys_addr_t pgd_phys;
|
||||||
struct kvm_pgtable *pgt;
|
struct kvm_pgtable *pgt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VTCR value used on the host. For a non-NV guest (or a NV
|
||||||
|
* guest that runs in a context where its own S2 doesn't
|
||||||
|
* apply), its T0SZ value reflects that of the IPA size.
|
||||||
|
*
|
||||||
|
* For a shadow S2 MMU, T0SZ reflects the PARange exposed to
|
||||||
|
* the guest.
|
||||||
|
*/
|
||||||
|
u64 vtcr;
|
||||||
|
|
||||||
/* The last vcpu id that ran on each physical CPU */
|
/* The last vcpu id that ran on each physical CPU */
|
||||||
int __percpu *last_vcpu_ran;
|
int __percpu *last_vcpu_ran;
|
||||||
|
|
||||||
@ -202,12 +212,34 @@ struct kvm_protected_vm {
|
|||||||
struct kvm_hyp_memcache teardown_mc;
|
struct kvm_hyp_memcache teardown_mc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct kvm_mpidr_data {
|
||||||
|
u64 mpidr_mask;
|
||||||
|
DECLARE_FLEX_ARRAY(u16, cmpidr_to_idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u16 kvm_mpidr_index(struct kvm_mpidr_data *data, u64 mpidr)
|
||||||
|
{
|
||||||
|
unsigned long mask = data->mpidr_mask;
|
||||||
|
u64 aff = mpidr & MPIDR_HWID_BITMASK;
|
||||||
|
int nbits, bit, bit_idx = 0;
|
||||||
|
u16 index = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this looks like RISC-V's BEXT or x86's PEXT
|
||||||
|
* instructions, it isn't by accident.
|
||||||
|
*/
|
||||||
|
nbits = fls(mask);
|
||||||
|
for_each_set_bit(bit, &mask, nbits) {
|
||||||
|
index |= (aff & BIT(bit)) >> (bit - bit_idx);
|
||||||
|
bit_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
struct kvm_arch {
|
struct kvm_arch {
|
||||||
struct kvm_s2_mmu mmu;
|
struct kvm_s2_mmu mmu;
|
||||||
|
|
||||||
/* VTCR_EL2 value for this VM */
|
|
||||||
u64 vtcr;
|
|
||||||
|
|
||||||
/* Interrupt controller */
|
/* Interrupt controller */
|
||||||
struct vgic_dist vgic;
|
struct vgic_dist vgic;
|
||||||
|
|
||||||
@ -239,15 +271,16 @@ struct kvm_arch {
|
|||||||
#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET 5
|
#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET 5
|
||||||
/* Timer PPIs made immutable */
|
/* Timer PPIs made immutable */
|
||||||
#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE 6
|
#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE 6
|
||||||
/* SMCCC filter initialized for the VM */
|
|
||||||
#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED 7
|
|
||||||
/* Initial ID reg values loaded */
|
/* Initial ID reg values loaded */
|
||||||
#define KVM_ARCH_FLAG_ID_REGS_INITIALIZED 8
|
#define KVM_ARCH_FLAG_ID_REGS_INITIALIZED 7
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/* VM-wide vCPU feature set */
|
/* VM-wide vCPU feature set */
|
||||||
DECLARE_BITMAP(vcpu_features, KVM_VCPU_MAX_FEATURES);
|
DECLARE_BITMAP(vcpu_features, KVM_VCPU_MAX_FEATURES);
|
||||||
|
|
||||||
|
/* MPIDR to vcpu index mapping, optional */
|
||||||
|
struct kvm_mpidr_data *mpidr_data;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VM-wide PMU filter, implemented as a bitmap and big enough for
|
* VM-wide PMU filter, implemented as a bitmap and big enough for
|
||||||
* up to 2^10 events (ARMv8.0) or 2^16 events (ARMv8.1+).
|
* up to 2^10 events (ARMv8.0) or 2^16 events (ARMv8.1+).
|
||||||
@ -257,6 +290,9 @@ struct kvm_arch {
|
|||||||
|
|
||||||
cpumask_var_t supported_cpus;
|
cpumask_var_t supported_cpus;
|
||||||
|
|
||||||
|
/* PMCR_EL0.N value for the guest */
|
||||||
|
u8 pmcr_n;
|
||||||
|
|
||||||
/* Hypercall features firmware registers' descriptor */
|
/* Hypercall features firmware registers' descriptor */
|
||||||
struct kvm_smccc_features smccc_feat;
|
struct kvm_smccc_features smccc_feat;
|
||||||
struct maple_tree smccc_filter;
|
struct maple_tree smccc_filter;
|
||||||
@ -574,9 +610,6 @@ struct kvm_vcpu_arch {
|
|||||||
/* Cache some mmu pages needed inside spinlock regions */
|
/* Cache some mmu pages needed inside spinlock regions */
|
||||||
struct kvm_mmu_memory_cache mmu_page_cache;
|
struct kvm_mmu_memory_cache mmu_page_cache;
|
||||||
|
|
||||||
/* feature flags */
|
|
||||||
DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
|
|
||||||
|
|
||||||
/* Virtual SError ESR to restore when HCR_EL2.VSE is set */
|
/* Virtual SError ESR to restore when HCR_EL2.VSE is set */
|
||||||
u64 vsesr_el2;
|
u64 vsesr_el2;
|
||||||
|
|
||||||
@ -1025,7 +1058,7 @@ int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
|
|||||||
extern unsigned int __ro_after_init kvm_arm_vmid_bits;
|
extern unsigned int __ro_after_init kvm_arm_vmid_bits;
|
||||||
int __init kvm_arm_vmid_alloc_init(void);
|
int __init kvm_arm_vmid_alloc_init(void);
|
||||||
void __init kvm_arm_vmid_alloc_free(void);
|
void __init kvm_arm_vmid_alloc_free(void);
|
||||||
void kvm_arm_vmid_update(struct kvm_vmid *kvm_vmid);
|
bool kvm_arm_vmid_update(struct kvm_vmid *kvm_vmid);
|
||||||
void kvm_arm_vmid_clear_active(void);
|
void kvm_arm_vmid_clear_active(void);
|
||||||
|
|
||||||
static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
|
static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
|
||||||
@ -1078,6 +1111,8 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
|
|||||||
struct kvm_arm_copy_mte_tags *copy_tags);
|
struct kvm_arm_copy_mte_tags *copy_tags);
|
||||||
int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
|
int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
|
||||||
struct kvm_arm_counter_offset *offset);
|
struct kvm_arm_counter_offset *offset);
|
||||||
|
int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm,
|
||||||
|
struct reg_mask_range *range);
|
||||||
|
|
||||||
/* Guest/host FPSIMD coordination helpers */
|
/* Guest/host FPSIMD coordination helpers */
|
||||||
int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
|
int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
|
||||||
@ -1109,8 +1144,8 @@ static inline bool kvm_set_pmuserenr(u64 val)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu);
|
void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu);
|
||||||
void kvm_vcpu_put_sysregs_vhe(struct kvm_vcpu *vcpu);
|
void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
int __init kvm_set_ipa_limit(void);
|
int __init kvm_set_ipa_limit(void);
|
||||||
|
|
||||||
|
@ -93,6 +93,8 @@ void __timer_disable_traps(struct kvm_vcpu *vcpu);
|
|||||||
void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt);
|
void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt);
|
||||||
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt);
|
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt);
|
||||||
#else
|
#else
|
||||||
|
void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu);
|
||||||
|
void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu);
|
||||||
void sysreg_save_host_state_vhe(struct kvm_cpu_context *ctxt);
|
void sysreg_save_host_state_vhe(struct kvm_cpu_context *ctxt);
|
||||||
void sysreg_restore_host_state_vhe(struct kvm_cpu_context *ctxt);
|
void sysreg_restore_host_state_vhe(struct kvm_cpu_context *ctxt);
|
||||||
void sysreg_save_guest_state_vhe(struct kvm_cpu_context *ctxt);
|
void sysreg_save_guest_state_vhe(struct kvm_cpu_context *ctxt);
|
||||||
@ -111,11 +113,6 @@ void __fpsimd_save_state(struct user_fpsimd_state *fp_regs);
|
|||||||
void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
|
void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
|
||||||
void __sve_restore_state(void *sve_pffr, u32 *fpsr);
|
void __sve_restore_state(void *sve_pffr, u32 *fpsr);
|
||||||
|
|
||||||
#ifndef __KVM_NVHE_HYPERVISOR__
|
|
||||||
void activate_traps_vhe_load(struct kvm_vcpu *vcpu);
|
|
||||||
void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u64 __guest_enter(struct kvm_vcpu *vcpu);
|
u64 __guest_enter(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt, u32 func_id);
|
bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt, u32 func_id);
|
||||||
|
@ -150,9 +150,9 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v)
|
|||||||
*/
|
*/
|
||||||
#define KVM_PHYS_SHIFT (40)
|
#define KVM_PHYS_SHIFT (40)
|
||||||
|
|
||||||
#define kvm_phys_shift(kvm) VTCR_EL2_IPA(kvm->arch.vtcr)
|
#define kvm_phys_shift(mmu) VTCR_EL2_IPA((mmu)->vtcr)
|
||||||
#define kvm_phys_size(kvm) (_AC(1, ULL) << kvm_phys_shift(kvm))
|
#define kvm_phys_size(mmu) (_AC(1, ULL) << kvm_phys_shift(mmu))
|
||||||
#define kvm_phys_mask(kvm) (kvm_phys_size(kvm) - _AC(1, ULL))
|
#define kvm_phys_mask(mmu) (kvm_phys_size(mmu) - _AC(1, ULL))
|
||||||
|
|
||||||
#include <asm/kvm_pgtable.h>
|
#include <asm/kvm_pgtable.h>
|
||||||
#include <asm/stage2_pgtable.h>
|
#include <asm/stage2_pgtable.h>
|
||||||
@ -224,16 +224,41 @@ static inline void __clean_dcache_guest_page(void *va, size_t size)
|
|||||||
kvm_flush_dcache_to_poc(va, size);
|
kvm_flush_dcache_to_poc(va, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t __invalidate_icache_max_range(void)
|
||||||
|
{
|
||||||
|
u8 iminline;
|
||||||
|
u64 ctr;
|
||||||
|
|
||||||
|
asm volatile(ALTERNATIVE_CB("movz %0, #0\n"
|
||||||
|
"movk %0, #0, lsl #16\n"
|
||||||
|
"movk %0, #0, lsl #32\n"
|
||||||
|
"movk %0, #0, lsl #48\n",
|
||||||
|
ARM64_ALWAYS_SYSTEM,
|
||||||
|
kvm_compute_final_ctr_el0)
|
||||||
|
: "=r" (ctr));
|
||||||
|
|
||||||
|
iminline = SYS_FIELD_GET(CTR_EL0, IminLine, ctr) + 2;
|
||||||
|
return MAX_DVM_OPS << iminline;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __invalidate_icache_guest_page(void *va, size_t size)
|
static inline void __invalidate_icache_guest_page(void *va, size_t size)
|
||||||
{
|
{
|
||||||
if (icache_is_aliasing()) {
|
/*
|
||||||
/* any kind of VIPT cache */
|
* VPIPT I-cache maintenance must be done from EL2. See comment in the
|
||||||
|
* nVHE flavor of __kvm_tlb_flush_vmid_ipa().
|
||||||
|
*/
|
||||||
|
if (icache_is_vpipt() && read_sysreg(CurrentEL) != CurrentEL_EL2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Blow the whole I-cache if it is aliasing (i.e. VIPT) or the
|
||||||
|
* invalidation range exceeds our arbitrary limit on invadations by
|
||||||
|
* cache line.
|
||||||
|
*/
|
||||||
|
if (icache_is_aliasing() || size > __invalidate_icache_max_range())
|
||||||
icache_inval_all_pou();
|
icache_inval_all_pou();
|
||||||
} else if (read_sysreg(CurrentEL) != CurrentEL_EL1 ||
|
else
|
||||||
!icache_is_vpipt()) {
|
|
||||||
/* PIPT or VPIPT at EL2 (see comment in __kvm_tlb_flush_vmid_ipa) */
|
|
||||||
icache_inval_pou((unsigned long)va, (unsigned long)va + size);
|
icache_inval_pou((unsigned long)va, (unsigned long)va + size);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_set_way_flush(struct kvm_vcpu *vcpu);
|
void kvm_set_way_flush(struct kvm_vcpu *vcpu);
|
||||||
@ -299,7 +324,7 @@ static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu)
|
|||||||
static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu,
|
static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu,
|
||||||
struct kvm_arch *arch)
|
struct kvm_arch *arch)
|
||||||
{
|
{
|
||||||
write_sysreg(arch->vtcr, vtcr_el2);
|
write_sysreg(mmu->vtcr, vtcr_el2);
|
||||||
write_sysreg(kvm_get_vttbr(mmu), vttbr_el2);
|
write_sysreg(kvm_get_vttbr(mmu), vttbr_el2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
#ifndef __ARM64_KVM_NESTED_H
|
#ifndef __ARM64_KVM_NESTED_H
|
||||||
#define __ARM64_KVM_NESTED_H
|
#define __ARM64_KVM_NESTED_H
|
||||||
|
|
||||||
|
#include <asm/kvm_emulate.h>
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
|
|
||||||
static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu)
|
static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return (!__is_defined(__KVM_NVHE_HYPERVISOR__) &&
|
return (!__is_defined(__KVM_NVHE_HYPERVISOR__) &&
|
||||||
cpus_have_final_cap(ARM64_HAS_NESTED_VIRT) &&
|
cpus_have_final_cap(ARM64_HAS_NESTED_VIRT) &&
|
||||||
test_bit(KVM_ARM_VCPU_HAS_EL2, vcpu->arch.features));
|
vcpu_has_feature(vcpu, KVM_ARM_VCPU_HAS_EL2));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool __check_nv_sr_forward(struct kvm_vcpu *vcpu);
|
extern bool __check_nv_sr_forward(struct kvm_vcpu *vcpu);
|
||||||
|
@ -21,13 +21,13 @@
|
|||||||
* (IPA_SHIFT - 4).
|
* (IPA_SHIFT - 4).
|
||||||
*/
|
*/
|
||||||
#define stage2_pgtable_levels(ipa) ARM64_HW_PGTABLE_LEVELS((ipa) - 4)
|
#define stage2_pgtable_levels(ipa) ARM64_HW_PGTABLE_LEVELS((ipa) - 4)
|
||||||
#define kvm_stage2_levels(kvm) VTCR_EL2_LVLS(kvm->arch.vtcr)
|
#define kvm_stage2_levels(mmu) VTCR_EL2_LVLS((mmu)->vtcr)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* kvm_mmmu_cache_min_pages() is the number of pages required to install
|
* kvm_mmmu_cache_min_pages() is the number of pages required to install
|
||||||
* a stage-2 translation. We pre-allocate the entry level page table at
|
* a stage-2 translation. We pre-allocate the entry level page table at
|
||||||
* the VM creation.
|
* the VM creation.
|
||||||
*/
|
*/
|
||||||
#define kvm_mmu_cache_min_pages(kvm) (kvm_stage2_levels(kvm) - 1)
|
#define kvm_mmu_cache_min_pages(mmu) (kvm_stage2_levels(mmu) - 1)
|
||||||
|
|
||||||
#endif /* __ARM64_S2_PGTABLE_H_ */
|
#endif /* __ARM64_S2_PGTABLE_H_ */
|
||||||
|
@ -270,6 +270,8 @@
|
|||||||
/* ETM */
|
/* ETM */
|
||||||
#define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4)
|
#define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4)
|
||||||
|
|
||||||
|
#define SYS_BRBCR_EL2 sys_reg(2, 4, 9, 0, 0)
|
||||||
|
|
||||||
#define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0)
|
#define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0)
|
||||||
#define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5)
|
#define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5)
|
||||||
#define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6)
|
#define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6)
|
||||||
@ -484,6 +486,7 @@
|
|||||||
|
|
||||||
#define SYS_SCTLR_EL2 sys_reg(3, 4, 1, 0, 0)
|
#define SYS_SCTLR_EL2 sys_reg(3, 4, 1, 0, 0)
|
||||||
#define SYS_ACTLR_EL2 sys_reg(3, 4, 1, 0, 1)
|
#define SYS_ACTLR_EL2 sys_reg(3, 4, 1, 0, 1)
|
||||||
|
#define SYS_SCTLR2_EL2 sys_reg(3, 4, 1, 0, 3)
|
||||||
#define SYS_HCR_EL2 sys_reg(3, 4, 1, 1, 0)
|
#define SYS_HCR_EL2 sys_reg(3, 4, 1, 1, 0)
|
||||||
#define SYS_MDCR_EL2 sys_reg(3, 4, 1, 1, 1)
|
#define SYS_MDCR_EL2 sys_reg(3, 4, 1, 1, 1)
|
||||||
#define SYS_CPTR_EL2 sys_reg(3, 4, 1, 1, 2)
|
#define SYS_CPTR_EL2 sys_reg(3, 4, 1, 1, 2)
|
||||||
@ -497,10 +500,15 @@
|
|||||||
#define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2)
|
#define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2)
|
||||||
|
|
||||||
#define SYS_TRFCR_EL2 sys_reg(3, 4, 1, 2, 1)
|
#define SYS_TRFCR_EL2 sys_reg(3, 4, 1, 2, 1)
|
||||||
|
#define SYS_VNCR_EL2 sys_reg(3, 4, 2, 2, 0)
|
||||||
#define SYS_HAFGRTR_EL2 sys_reg(3, 4, 3, 1, 6)
|
#define SYS_HAFGRTR_EL2 sys_reg(3, 4, 3, 1, 6)
|
||||||
#define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0)
|
#define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0)
|
||||||
#define SYS_ELR_EL2 sys_reg(3, 4, 4, 0, 1)
|
#define SYS_ELR_EL2 sys_reg(3, 4, 4, 0, 1)
|
||||||
#define SYS_SP_EL1 sys_reg(3, 4, 4, 1, 0)
|
#define SYS_SP_EL1 sys_reg(3, 4, 4, 1, 0)
|
||||||
|
#define SYS_SPSR_irq sys_reg(3, 4, 4, 3, 0)
|
||||||
|
#define SYS_SPSR_abt sys_reg(3, 4, 4, 3, 1)
|
||||||
|
#define SYS_SPSR_und sys_reg(3, 4, 4, 3, 2)
|
||||||
|
#define SYS_SPSR_fiq sys_reg(3, 4, 4, 3, 3)
|
||||||
#define SYS_IFSR32_EL2 sys_reg(3, 4, 5, 0, 1)
|
#define SYS_IFSR32_EL2 sys_reg(3, 4, 5, 0, 1)
|
||||||
#define SYS_AFSR0_EL2 sys_reg(3, 4, 5, 1, 0)
|
#define SYS_AFSR0_EL2 sys_reg(3, 4, 5, 1, 0)
|
||||||
#define SYS_AFSR1_EL2 sys_reg(3, 4, 5, 1, 1)
|
#define SYS_AFSR1_EL2 sys_reg(3, 4, 5, 1, 1)
|
||||||
@ -514,6 +522,18 @@
|
|||||||
|
|
||||||
#define SYS_MAIR_EL2 sys_reg(3, 4, 10, 2, 0)
|
#define SYS_MAIR_EL2 sys_reg(3, 4, 10, 2, 0)
|
||||||
#define SYS_AMAIR_EL2 sys_reg(3, 4, 10, 3, 0)
|
#define SYS_AMAIR_EL2 sys_reg(3, 4, 10, 3, 0)
|
||||||
|
#define SYS_MPAMHCR_EL2 sys_reg(3, 4, 10, 4, 0)
|
||||||
|
#define SYS_MPAMVPMV_EL2 sys_reg(3, 4, 10, 4, 1)
|
||||||
|
#define SYS_MPAM2_EL2 sys_reg(3, 4, 10, 5, 0)
|
||||||
|
#define __SYS__MPAMVPMx_EL2(x) sys_reg(3, 4, 10, 6, x)
|
||||||
|
#define SYS_MPAMVPM0_EL2 __SYS__MPAMVPMx_EL2(0)
|
||||||
|
#define SYS_MPAMVPM1_EL2 __SYS__MPAMVPMx_EL2(1)
|
||||||
|
#define SYS_MPAMVPM2_EL2 __SYS__MPAMVPMx_EL2(2)
|
||||||
|
#define SYS_MPAMVPM3_EL2 __SYS__MPAMVPMx_EL2(3)
|
||||||
|
#define SYS_MPAMVPM4_EL2 __SYS__MPAMVPMx_EL2(4)
|
||||||
|
#define SYS_MPAMVPM5_EL2 __SYS__MPAMVPMx_EL2(5)
|
||||||
|
#define SYS_MPAMVPM6_EL2 __SYS__MPAMVPMx_EL2(6)
|
||||||
|
#define SYS_MPAMVPM7_EL2 __SYS__MPAMVPMx_EL2(7)
|
||||||
|
|
||||||
#define SYS_VBAR_EL2 sys_reg(3, 4, 12, 0, 0)
|
#define SYS_VBAR_EL2 sys_reg(3, 4, 12, 0, 0)
|
||||||
#define SYS_RVBAR_EL2 sys_reg(3, 4, 12, 0, 1)
|
#define SYS_RVBAR_EL2 sys_reg(3, 4, 12, 0, 1)
|
||||||
@ -562,24 +582,49 @@
|
|||||||
|
|
||||||
#define SYS_CONTEXTIDR_EL2 sys_reg(3, 4, 13, 0, 1)
|
#define SYS_CONTEXTIDR_EL2 sys_reg(3, 4, 13, 0, 1)
|
||||||
#define SYS_TPIDR_EL2 sys_reg(3, 4, 13, 0, 2)
|
#define SYS_TPIDR_EL2 sys_reg(3, 4, 13, 0, 2)
|
||||||
|
#define SYS_SCXTNUM_EL2 sys_reg(3, 4, 13, 0, 7)
|
||||||
|
|
||||||
|
#define __AMEV_op2(m) (m & 0x7)
|
||||||
|
#define __AMEV_CRm(n, m) (n | ((m & 0x8) >> 3))
|
||||||
|
#define __SYS__AMEVCNTVOFF0n_EL2(m) sys_reg(3, 4, 13, __AMEV_CRm(0x8, m), __AMEV_op2(m))
|
||||||
|
#define SYS_AMEVCNTVOFF0n_EL2(m) __SYS__AMEVCNTVOFF0n_EL2(m)
|
||||||
|
#define __SYS__AMEVCNTVOFF1n_EL2(m) sys_reg(3, 4, 13, __AMEV_CRm(0xA, m), __AMEV_op2(m))
|
||||||
|
#define SYS_AMEVCNTVOFF1n_EL2(m) __SYS__AMEVCNTVOFF1n_EL2(m)
|
||||||
|
|
||||||
#define SYS_CNTVOFF_EL2 sys_reg(3, 4, 14, 0, 3)
|
#define SYS_CNTVOFF_EL2 sys_reg(3, 4, 14, 0, 3)
|
||||||
#define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0)
|
#define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0)
|
||||||
|
#define SYS_CNTHP_TVAL_EL2 sys_reg(3, 4, 14, 2, 0)
|
||||||
|
#define SYS_CNTHP_CTL_EL2 sys_reg(3, 4, 14, 2, 1)
|
||||||
|
#define SYS_CNTHP_CVAL_EL2 sys_reg(3, 4, 14, 2, 2)
|
||||||
|
#define SYS_CNTHV_TVAL_EL2 sys_reg(3, 4, 14, 3, 0)
|
||||||
|
#define SYS_CNTHV_CTL_EL2 sys_reg(3, 4, 14, 3, 1)
|
||||||
|
#define SYS_CNTHV_CVAL_EL2 sys_reg(3, 4, 14, 3, 2)
|
||||||
|
|
||||||
/* VHE encodings for architectural EL0/1 system registers */
|
/* VHE encodings for architectural EL0/1 system registers */
|
||||||
|
#define SYS_BRBCR_EL12 sys_reg(2, 5, 9, 0, 0)
|
||||||
#define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
|
#define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
|
||||||
|
#define SYS_CPACR_EL12 sys_reg(3, 5, 1, 0, 2)
|
||||||
|
#define SYS_SCTLR2_EL12 sys_reg(3, 5, 1, 0, 3)
|
||||||
|
#define SYS_ZCR_EL12 sys_reg(3, 5, 1, 2, 0)
|
||||||
|
#define SYS_TRFCR_EL12 sys_reg(3, 5, 1, 2, 1)
|
||||||
|
#define SYS_SMCR_EL12 sys_reg(3, 5, 1, 2, 6)
|
||||||
#define SYS_TTBR0_EL12 sys_reg(3, 5, 2, 0, 0)
|
#define SYS_TTBR0_EL12 sys_reg(3, 5, 2, 0, 0)
|
||||||
#define SYS_TTBR1_EL12 sys_reg(3, 5, 2, 0, 1)
|
#define SYS_TTBR1_EL12 sys_reg(3, 5, 2, 0, 1)
|
||||||
#define SYS_TCR_EL12 sys_reg(3, 5, 2, 0, 2)
|
#define SYS_TCR_EL12 sys_reg(3, 5, 2, 0, 2)
|
||||||
|
#define SYS_TCR2_EL12 sys_reg(3, 5, 2, 0, 3)
|
||||||
#define SYS_SPSR_EL12 sys_reg(3, 5, 4, 0, 0)
|
#define SYS_SPSR_EL12 sys_reg(3, 5, 4, 0, 0)
|
||||||
#define SYS_ELR_EL12 sys_reg(3, 5, 4, 0, 1)
|
#define SYS_ELR_EL12 sys_reg(3, 5, 4, 0, 1)
|
||||||
#define SYS_AFSR0_EL12 sys_reg(3, 5, 5, 1, 0)
|
#define SYS_AFSR0_EL12 sys_reg(3, 5, 5, 1, 0)
|
||||||
#define SYS_AFSR1_EL12 sys_reg(3, 5, 5, 1, 1)
|
#define SYS_AFSR1_EL12 sys_reg(3, 5, 5, 1, 1)
|
||||||
#define SYS_ESR_EL12 sys_reg(3, 5, 5, 2, 0)
|
#define SYS_ESR_EL12 sys_reg(3, 5, 5, 2, 0)
|
||||||
#define SYS_TFSR_EL12 sys_reg(3, 5, 5, 6, 0)
|
#define SYS_TFSR_EL12 sys_reg(3, 5, 5, 6, 0)
|
||||||
|
#define SYS_FAR_EL12 sys_reg(3, 5, 6, 0, 0)
|
||||||
|
#define SYS_PMSCR_EL12 sys_reg(3, 5, 9, 9, 0)
|
||||||
#define SYS_MAIR_EL12 sys_reg(3, 5, 10, 2, 0)
|
#define SYS_MAIR_EL12 sys_reg(3, 5, 10, 2, 0)
|
||||||
#define SYS_AMAIR_EL12 sys_reg(3, 5, 10, 3, 0)
|
#define SYS_AMAIR_EL12 sys_reg(3, 5, 10, 3, 0)
|
||||||
#define SYS_VBAR_EL12 sys_reg(3, 5, 12, 0, 0)
|
#define SYS_VBAR_EL12 sys_reg(3, 5, 12, 0, 0)
|
||||||
|
#define SYS_CONTEXTIDR_EL12 sys_reg(3, 5, 13, 0, 1)
|
||||||
|
#define SYS_SCXTNUM_EL12 sys_reg(3, 5, 13, 0, 7)
|
||||||
#define SYS_CNTKCTL_EL12 sys_reg(3, 5, 14, 1, 0)
|
#define SYS_CNTKCTL_EL12 sys_reg(3, 5, 14, 1, 0)
|
||||||
#define SYS_CNTP_TVAL_EL02 sys_reg(3, 5, 14, 2, 0)
|
#define SYS_CNTP_TVAL_EL02 sys_reg(3, 5, 14, 2, 0)
|
||||||
#define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1)
|
#define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1)
|
||||||
|
@ -333,7 +333,7 @@ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
|
|||||||
* This is meant to avoid soft lock-ups on large TLB flushing ranges and not
|
* This is meant to avoid soft lock-ups on large TLB flushing ranges and not
|
||||||
* necessarily a performance improvement.
|
* necessarily a performance improvement.
|
||||||
*/
|
*/
|
||||||
#define MAX_TLBI_OPS PTRS_PER_PTE
|
#define MAX_DVM_OPS PTRS_PER_PTE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* __flush_tlb_range_op - Perform TLBI operation upon a range
|
* __flush_tlb_range_op - Perform TLBI operation upon a range
|
||||||
@ -413,12 +413,12 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* When not uses TLB range ops, we can handle up to
|
* When not uses TLB range ops, we can handle up to
|
||||||
* (MAX_TLBI_OPS - 1) pages;
|
* (MAX_DVM_OPS - 1) pages;
|
||||||
* When uses TLB range ops, we can handle up to
|
* When uses TLB range ops, we can handle up to
|
||||||
* (MAX_TLBI_RANGE_PAGES - 1) pages.
|
* (MAX_TLBI_RANGE_PAGES - 1) pages.
|
||||||
*/
|
*/
|
||||||
if ((!system_supports_tlb_range() &&
|
if ((!system_supports_tlb_range() &&
|
||||||
(end - start) >= (MAX_TLBI_OPS * stride)) ||
|
(end - start) >= (MAX_DVM_OPS * stride)) ||
|
||||||
pages >= MAX_TLBI_RANGE_PAGES) {
|
pages >= MAX_TLBI_RANGE_PAGES) {
|
||||||
flush_tlb_mm(vma->vm_mm);
|
flush_tlb_mm(vma->vm_mm);
|
||||||
return;
|
return;
|
||||||
@ -451,7 +451,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
|
|||||||
{
|
{
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
|
|
||||||
if ((end - start) > (MAX_TLBI_OPS * PAGE_SIZE)) {
|
if ((end - start) > (MAX_DVM_OPS * PAGE_SIZE)) {
|
||||||
flush_tlb_all();
|
flush_tlb_all();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,9 @@
|
|||||||
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <asm/esr.h>
|
#include <asm/esr.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
|
|
||||||
struct pt_regs;
|
|
||||||
|
|
||||||
#ifdef CONFIG_ARMV8_DEPRECATED
|
#ifdef CONFIG_ARMV8_DEPRECATED
|
||||||
bool try_emulate_armv8_deprecated(struct pt_regs *regs, u32 insn);
|
bool try_emulate_armv8_deprecated(struct pt_regs *regs, u32 insn);
|
||||||
#else
|
#else
|
||||||
@ -101,4 +100,55 @@ static inline unsigned long arm64_ras_serror_get_severity(unsigned long esr)
|
|||||||
|
|
||||||
bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned long esr);
|
bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned long esr);
|
||||||
void __noreturn arm64_serror_panic(struct pt_regs *regs, unsigned long esr);
|
void __noreturn arm64_serror_panic(struct pt_regs *regs, unsigned long esr);
|
||||||
|
|
||||||
|
static inline void arm64_mops_reset_regs(struct user_pt_regs *regs, unsigned long esr)
|
||||||
|
{
|
||||||
|
bool wrong_option = esr & ESR_ELx_MOPS_ISS_WRONG_OPTION;
|
||||||
|
bool option_a = esr & ESR_ELx_MOPS_ISS_OPTION_A;
|
||||||
|
int dstreg = ESR_ELx_MOPS_ISS_DESTREG(esr);
|
||||||
|
int srcreg = ESR_ELx_MOPS_ISS_SRCREG(esr);
|
||||||
|
int sizereg = ESR_ELx_MOPS_ISS_SIZEREG(esr);
|
||||||
|
unsigned long dst, src, size;
|
||||||
|
|
||||||
|
dst = regs->regs[dstreg];
|
||||||
|
src = regs->regs[srcreg];
|
||||||
|
size = regs->regs[sizereg];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put the registers back in the original format suitable for a
|
||||||
|
* prologue instruction, using the generic return routine from the
|
||||||
|
* Arm ARM (DDI 0487I.a) rules CNTMJ and MWFQH.
|
||||||
|
*/
|
||||||
|
if (esr & ESR_ELx_MOPS_ISS_MEM_INST) {
|
||||||
|
/* SET* instruction */
|
||||||
|
if (option_a ^ wrong_option) {
|
||||||
|
/* Format is from Option A; forward set */
|
||||||
|
regs->regs[dstreg] = dst + size;
|
||||||
|
regs->regs[sizereg] = -size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* CPY* instruction */
|
||||||
|
if (!(option_a ^ wrong_option)) {
|
||||||
|
/* Format is from Option B */
|
||||||
|
if (regs->pstate & PSR_N_BIT) {
|
||||||
|
/* Backward copy */
|
||||||
|
regs->regs[dstreg] = dst - size;
|
||||||
|
regs->regs[srcreg] = src - size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Format is from Option A */
|
||||||
|
if (size & BIT(63)) {
|
||||||
|
/* Forward copy */
|
||||||
|
regs->regs[dstreg] = dst + size;
|
||||||
|
regs->regs[srcreg] = src + size;
|
||||||
|
regs->regs[sizereg] = -size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esr & ESR_ELx_MOPS_ISS_FROM_EPILOGUE)
|
||||||
|
regs->pc -= 8;
|
||||||
|
else
|
||||||
|
regs->pc -= 4;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -505,6 +505,38 @@ struct kvm_smccc_filter {
|
|||||||
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
|
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
|
||||||
#define KVM_HYPERCALL_EXIT_16BIT (1U << 1)
|
#define KVM_HYPERCALL_EXIT_16BIT (1U << 1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get feature ID registers userspace writable mask.
|
||||||
|
*
|
||||||
|
* From DDI0487J.a, D19.2.66 ("ID_AA64MMFR2_EL1, AArch64 Memory Model
|
||||||
|
* Feature Register 2"):
|
||||||
|
*
|
||||||
|
* "The Feature ID space is defined as the System register space in
|
||||||
|
* AArch64 with op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7},
|
||||||
|
* op2=={0-7}."
|
||||||
|
*
|
||||||
|
* This covers all currently known R/O registers that indicate
|
||||||
|
* anything useful feature wise, including the ID registers.
|
||||||
|
*
|
||||||
|
* If we ever need to introduce a new range, it will be described as
|
||||||
|
* such in the range field.
|
||||||
|
*/
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2) \
|
||||||
|
({ \
|
||||||
|
__u64 __op1 = (op1) & 3; \
|
||||||
|
__op1 -= (__op1 == 3); \
|
||||||
|
(__op1 << 6 | ((crm) & 7) << 3 | (op2)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE 0
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
|
||||||
|
|
||||||
|
struct reg_mask_range {
|
||||||
|
__u64 addr; /* Pointer to mask array */
|
||||||
|
__u32 range; /* Requested range */
|
||||||
|
__u32 reserved[13];
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __ARM_KVM_H__ */
|
#endif /* __ARM_KVM_H__ */
|
||||||
|
@ -516,53 +516,7 @@ void do_el1_fpac(struct pt_regs *regs, unsigned long esr)
|
|||||||
|
|
||||||
void do_el0_mops(struct pt_regs *regs, unsigned long esr)
|
void do_el0_mops(struct pt_regs *regs, unsigned long esr)
|
||||||
{
|
{
|
||||||
bool wrong_option = esr & ESR_ELx_MOPS_ISS_WRONG_OPTION;
|
arm64_mops_reset_regs(®s->user_regs, esr);
|
||||||
bool option_a = esr & ESR_ELx_MOPS_ISS_OPTION_A;
|
|
||||||
int dstreg = ESR_ELx_MOPS_ISS_DESTREG(esr);
|
|
||||||
int srcreg = ESR_ELx_MOPS_ISS_SRCREG(esr);
|
|
||||||
int sizereg = ESR_ELx_MOPS_ISS_SIZEREG(esr);
|
|
||||||
unsigned long dst, src, size;
|
|
||||||
|
|
||||||
dst = pt_regs_read_reg(regs, dstreg);
|
|
||||||
src = pt_regs_read_reg(regs, srcreg);
|
|
||||||
size = pt_regs_read_reg(regs, sizereg);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Put the registers back in the original format suitable for a
|
|
||||||
* prologue instruction, using the generic return routine from the
|
|
||||||
* Arm ARM (DDI 0487I.a) rules CNTMJ and MWFQH.
|
|
||||||
*/
|
|
||||||
if (esr & ESR_ELx_MOPS_ISS_MEM_INST) {
|
|
||||||
/* SET* instruction */
|
|
||||||
if (option_a ^ wrong_option) {
|
|
||||||
/* Format is from Option A; forward set */
|
|
||||||
pt_regs_write_reg(regs, dstreg, dst + size);
|
|
||||||
pt_regs_write_reg(regs, sizereg, -size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* CPY* instruction */
|
|
||||||
if (!(option_a ^ wrong_option)) {
|
|
||||||
/* Format is from Option B */
|
|
||||||
if (regs->pstate & PSR_N_BIT) {
|
|
||||||
/* Backward copy */
|
|
||||||
pt_regs_write_reg(regs, dstreg, dst - size);
|
|
||||||
pt_regs_write_reg(regs, srcreg, src - size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Format is from Option A */
|
|
||||||
if (size & BIT(63)) {
|
|
||||||
/* Forward copy */
|
|
||||||
pt_regs_write_reg(regs, dstreg, dst + size);
|
|
||||||
pt_regs_write_reg(regs, srcreg, src + size);
|
|
||||||
pt_regs_write_reg(regs, sizereg, -size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esr & ESR_ELx_MOPS_ISS_FROM_EPILOGUE)
|
|
||||||
regs->pc -= 8;
|
|
||||||
else
|
|
||||||
regs->pc -= 4;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If single stepping then finish the step before executing the
|
* If single stepping then finish the step before executing the
|
||||||
|
@ -453,7 +453,7 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
|
|||||||
timer_ctx->irq.level);
|
timer_ctx->irq.level);
|
||||||
|
|
||||||
if (!userspace_irqchip(vcpu->kvm)) {
|
if (!userspace_irqchip(vcpu->kvm)) {
|
||||||
ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
|
ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu,
|
||||||
timer_irq(timer_ctx),
|
timer_irq(timer_ctx),
|
||||||
timer_ctx->irq.level,
|
timer_ctx->irq.level,
|
||||||
timer_ctx);
|
timer_ctx);
|
||||||
@ -936,7 +936,7 @@ void kvm_timer_sync_user(struct kvm_vcpu *vcpu)
|
|||||||
unmask_vtimer_irq_user(vcpu);
|
unmask_vtimer_irq_user(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
|
void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
|
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
|
||||||
struct timer_map map;
|
struct timer_map map;
|
||||||
@ -980,8 +980,6 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
|
|||||||
soft_timer_cancel(&map.emul_vtimer->hrtimer);
|
soft_timer_cancel(&map.emul_vtimer->hrtimer);
|
||||||
if (map.emul_ptimer)
|
if (map.emul_ptimer)
|
||||||
soft_timer_cancel(&map.emul_ptimer->hrtimer);
|
soft_timer_cancel(&map.emul_ptimer->hrtimer);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)
|
static void timer_context_init(struct kvm_vcpu *vcpu, int timerid)
|
||||||
|
@ -205,6 +205,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
|||||||
if (is_protected_kvm_enabled())
|
if (is_protected_kvm_enabled())
|
||||||
pkvm_destroy_hyp_vm(kvm);
|
pkvm_destroy_hyp_vm(kvm);
|
||||||
|
|
||||||
|
kfree(kvm->arch.mpidr_data);
|
||||||
kvm_destroy_vcpus(kvm);
|
kvm_destroy_vcpus(kvm);
|
||||||
|
|
||||||
kvm_unshare_hyp(kvm, kvm + 1);
|
kvm_unshare_hyp(kvm, kvm + 1);
|
||||||
@ -317,6 +318,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|||||||
case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
|
case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
|
||||||
r = kvm_supported_block_sizes();
|
r = kvm_supported_block_sizes();
|
||||||
break;
|
break;
|
||||||
|
case KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES:
|
||||||
|
r = BIT(0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r = 0;
|
r = 0;
|
||||||
}
|
}
|
||||||
@ -367,7 +371,6 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
/* Force users to call KVM_ARM_VCPU_INIT */
|
/* Force users to call KVM_ARM_VCPU_INIT */
|
||||||
vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
|
vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
|
||||||
bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES);
|
|
||||||
|
|
||||||
vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
|
vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
|
||||||
|
|
||||||
@ -438,9 +441,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||||||
* We might get preempted before the vCPU actually runs, but
|
* We might get preempted before the vCPU actually runs, but
|
||||||
* over-invalidation doesn't affect correctness.
|
* over-invalidation doesn't affect correctness.
|
||||||
*/
|
*/
|
||||||
if (*last_ran != vcpu->vcpu_id) {
|
if (*last_ran != vcpu->vcpu_idx) {
|
||||||
kvm_call_hyp(__kvm_flush_cpu_context, mmu);
|
kvm_call_hyp(__kvm_flush_cpu_context, mmu);
|
||||||
*last_ran = vcpu->vcpu_id;
|
*last_ran = vcpu->vcpu_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
vcpu->cpu = cpu;
|
vcpu->cpu = cpu;
|
||||||
@ -448,7 +451,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||||||
kvm_vgic_load(vcpu);
|
kvm_vgic_load(vcpu);
|
||||||
kvm_timer_vcpu_load(vcpu);
|
kvm_timer_vcpu_load(vcpu);
|
||||||
if (has_vhe())
|
if (has_vhe())
|
||||||
kvm_vcpu_load_sysregs_vhe(vcpu);
|
kvm_vcpu_load_vhe(vcpu);
|
||||||
kvm_arch_vcpu_load_fp(vcpu);
|
kvm_arch_vcpu_load_fp(vcpu);
|
||||||
kvm_vcpu_pmu_restore_guest(vcpu);
|
kvm_vcpu_pmu_restore_guest(vcpu);
|
||||||
if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
|
if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
|
||||||
@ -472,7 +475,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
|||||||
kvm_arch_vcpu_put_debug_state_flags(vcpu);
|
kvm_arch_vcpu_put_debug_state_flags(vcpu);
|
||||||
kvm_arch_vcpu_put_fp(vcpu);
|
kvm_arch_vcpu_put_fp(vcpu);
|
||||||
if (has_vhe())
|
if (has_vhe())
|
||||||
kvm_vcpu_put_sysregs_vhe(vcpu);
|
kvm_vcpu_put_vhe(vcpu);
|
||||||
kvm_timer_vcpu_put(vcpu);
|
kvm_timer_vcpu_put(vcpu);
|
||||||
kvm_vgic_put(vcpu);
|
kvm_vgic_put(vcpu);
|
||||||
kvm_vcpu_pmu_restore_host(vcpu);
|
kvm_vcpu_pmu_restore_host(vcpu);
|
||||||
@ -578,6 +581,57 @@ static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu)
|
|||||||
return vcpu_get_flag(vcpu, VCPU_INITIALIZED);
|
return vcpu_get_flag(vcpu, VCPU_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_init_mpidr_data(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct kvm_mpidr_data *data = NULL;
|
||||||
|
unsigned long c, mask, nr_entries;
|
||||||
|
u64 aff_set = 0, aff_clr = ~0UL;
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
mutex_lock(&kvm->arch.config_lock);
|
||||||
|
|
||||||
|
if (kvm->arch.mpidr_data || atomic_read(&kvm->online_vcpus) == 1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||||
|
u64 aff = kvm_vcpu_get_mpidr_aff(vcpu);
|
||||||
|
aff_set |= aff;
|
||||||
|
aff_clr &= aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A significant bit can be either 0 or 1, and will only appear in
|
||||||
|
* aff_set. Use aff_clr to weed out the useless stuff.
|
||||||
|
*/
|
||||||
|
mask = aff_set ^ aff_clr;
|
||||||
|
nr_entries = BIT_ULL(hweight_long(mask));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't let userspace fool us. If we need more than a single page
|
||||||
|
* to describe the compressed MPIDR array, just fall back to the
|
||||||
|
* iterative method. Single vcpu VMs do not need this either.
|
||||||
|
*/
|
||||||
|
if (struct_size(data, cmpidr_to_idx, nr_entries) <= PAGE_SIZE)
|
||||||
|
data = kzalloc(struct_size(data, cmpidr_to_idx, nr_entries),
|
||||||
|
GFP_KERNEL_ACCOUNT);
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
data->mpidr_mask = mask;
|
||||||
|
|
||||||
|
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||||
|
u64 aff = kvm_vcpu_get_mpidr_aff(vcpu);
|
||||||
|
u16 index = kvm_mpidr_index(data, aff);
|
||||||
|
|
||||||
|
data->cmpidr_to_idx[index] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm->arch.mpidr_data = data;
|
||||||
|
out:
|
||||||
|
mutex_unlock(&kvm->arch.config_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle both the initialisation that is being done when the vcpu is
|
* Handle both the initialisation that is being done when the vcpu is
|
||||||
* run for the first time, as well as the updates that must be
|
* run for the first time, as well as the updates that must be
|
||||||
@ -601,6 +655,8 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
|
|||||||
if (likely(vcpu_has_run_once(vcpu)))
|
if (likely(vcpu_has_run_once(vcpu)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
kvm_init_mpidr_data(kvm);
|
||||||
|
|
||||||
kvm_arm_vcpu_init_debug(vcpu);
|
kvm_arm_vcpu_init_debug(vcpu);
|
||||||
|
|
||||||
if (likely(irqchip_in_kernel(kvm))) {
|
if (likely(irqchip_in_kernel(kvm))) {
|
||||||
@ -801,8 +857,7 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (kvm_check_request(KVM_REQ_RELOAD_PMU, vcpu))
|
if (kvm_check_request(KVM_REQ_RELOAD_PMU, vcpu))
|
||||||
kvm_pmu_handle_pmcr(vcpu,
|
kvm_vcpu_reload_pmu(vcpu);
|
||||||
__vcpu_sys_reg(vcpu, PMCR_EL0));
|
|
||||||
|
|
||||||
if (kvm_check_request(KVM_REQ_RESYNC_PMU_EL0, vcpu))
|
if (kvm_check_request(KVM_REQ_RESYNC_PMU_EL0, vcpu))
|
||||||
kvm_vcpu_pmu_restore_guest(vcpu);
|
kvm_vcpu_pmu_restore_guest(vcpu);
|
||||||
@ -950,7 +1005,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||||||
* making a thread's VMID inactive. So we need to call
|
* making a thread's VMID inactive. So we need to call
|
||||||
* kvm_arm_vmid_update() in non-premptible context.
|
* kvm_arm_vmid_update() in non-premptible context.
|
||||||
*/
|
*/
|
||||||
kvm_arm_vmid_update(&vcpu->arch.hw_mmu->vmid);
|
if (kvm_arm_vmid_update(&vcpu->arch.hw_mmu->vmid) &&
|
||||||
|
has_vhe())
|
||||||
|
__load_stage2(vcpu->arch.hw_mmu,
|
||||||
|
vcpu->arch.hw_mmu->arch);
|
||||||
|
|
||||||
kvm_pmu_flush_hwstate(vcpu);
|
kvm_pmu_flush_hwstate(vcpu);
|
||||||
|
|
||||||
@ -1134,27 +1192,23 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
|||||||
bool line_status)
|
bool line_status)
|
||||||
{
|
{
|
||||||
u32 irq = irq_level->irq;
|
u32 irq = irq_level->irq;
|
||||||
unsigned int irq_type, vcpu_idx, irq_num;
|
unsigned int irq_type, vcpu_id, irq_num;
|
||||||
int nrcpus = atomic_read(&kvm->online_vcpus);
|
|
||||||
struct kvm_vcpu *vcpu = NULL;
|
struct kvm_vcpu *vcpu = NULL;
|
||||||
bool level = irq_level->level;
|
bool level = irq_level->level;
|
||||||
|
|
||||||
irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
|
irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
|
||||||
vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
|
vcpu_id = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
|
||||||
vcpu_idx += ((irq >> KVM_ARM_IRQ_VCPU2_SHIFT) & KVM_ARM_IRQ_VCPU2_MASK) * (KVM_ARM_IRQ_VCPU_MASK + 1);
|
vcpu_id += ((irq >> KVM_ARM_IRQ_VCPU2_SHIFT) & KVM_ARM_IRQ_VCPU2_MASK) * (KVM_ARM_IRQ_VCPU_MASK + 1);
|
||||||
irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
|
irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
|
||||||
|
|
||||||
trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level);
|
trace_kvm_irq_line(irq_type, vcpu_id, irq_num, irq_level->level);
|
||||||
|
|
||||||
switch (irq_type) {
|
switch (irq_type) {
|
||||||
case KVM_ARM_IRQ_TYPE_CPU:
|
case KVM_ARM_IRQ_TYPE_CPU:
|
||||||
if (irqchip_in_kernel(kvm))
|
if (irqchip_in_kernel(kvm))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if (vcpu_idx >= nrcpus)
|
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
|
|
||||||
if (!vcpu)
|
if (!vcpu)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -1166,17 +1220,14 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
|||||||
if (!irqchip_in_kernel(kvm))
|
if (!irqchip_in_kernel(kvm))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if (vcpu_idx >= nrcpus)
|
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, vcpu_idx);
|
|
||||||
if (!vcpu)
|
if (!vcpu)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS)
|
if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level, NULL);
|
return kvm_vgic_inject_irq(kvm, vcpu, irq_num, level, NULL);
|
||||||
case KVM_ARM_IRQ_TYPE_SPI:
|
case KVM_ARM_IRQ_TYPE_SPI:
|
||||||
if (!irqchip_in_kernel(kvm))
|
if (!irqchip_in_kernel(kvm))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
@ -1184,12 +1235,36 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
|||||||
if (irq_num < VGIC_NR_PRIVATE_IRQS)
|
if (irq_num < VGIC_NR_PRIVATE_IRQS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return kvm_vgic_inject_irq(kvm, 0, irq_num, level, NULL);
|
return kvm_vgic_inject_irq(kvm, NULL, irq_num, level, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long system_supported_vcpu_features(void)
|
||||||
|
{
|
||||||
|
unsigned long features = KVM_VCPU_VALID_FEATURES;
|
||||||
|
|
||||||
|
if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
|
||||||
|
clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
|
||||||
|
|
||||||
|
if (!kvm_arm_support_pmu_v3())
|
||||||
|
clear_bit(KVM_ARM_VCPU_PMU_V3, &features);
|
||||||
|
|
||||||
|
if (!system_supports_sve())
|
||||||
|
clear_bit(KVM_ARM_VCPU_SVE, &features);
|
||||||
|
|
||||||
|
if (!system_has_full_ptr_auth()) {
|
||||||
|
clear_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, &features);
|
||||||
|
clear_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
|
||||||
|
clear_bit(KVM_ARM_VCPU_HAS_EL2, &features);
|
||||||
|
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
|
static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
|
||||||
const struct kvm_vcpu_init *init)
|
const struct kvm_vcpu_init *init)
|
||||||
{
|
{
|
||||||
@ -1204,12 +1279,25 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
|
|||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (features & ~system_supported_vcpu_features())
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For now make sure that both address/generic pointer authentication
|
||||||
|
* features are requested by the userspace together.
|
||||||
|
*/
|
||||||
|
if (test_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, &features) !=
|
||||||
|
test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Disallow NV+SVE for the time being */
|
||||||
|
if (test_bit(KVM_ARM_VCPU_HAS_EL2, &features) &&
|
||||||
|
test_bit(KVM_ARM_VCPU_SVE, &features))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!test_bit(KVM_ARM_VCPU_EL1_32BIT, &features))
|
if (!test_bit(KVM_ARM_VCPU_EL1_32BIT, &features))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!cpus_have_const_cap(ARM64_HAS_32BIT_EL1))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* MTE is incompatible with AArch32 */
|
/* MTE is incompatible with AArch32 */
|
||||||
if (kvm_has_mte(vcpu->kvm))
|
if (kvm_has_mte(vcpu->kvm))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1226,7 +1314,23 @@ static bool kvm_vcpu_init_changed(struct kvm_vcpu *vcpu,
|
|||||||
{
|
{
|
||||||
unsigned long features = init->features[0];
|
unsigned long features = init->features[0];
|
||||||
|
|
||||||
return !bitmap_equal(vcpu->arch.features, &features, KVM_VCPU_MAX_FEATURES);
|
return !bitmap_equal(vcpu->kvm->arch.vcpu_features, &features,
|
||||||
|
KVM_VCPU_MAX_FEATURES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm *kvm = vcpu->kvm;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the vCPU has a PMU, but no PMU is set for the guest
|
||||||
|
* yet, set the default one.
|
||||||
|
*/
|
||||||
|
if (kvm_vcpu_has_pmu(vcpu) && !kvm->arch.arm_pmu)
|
||||||
|
ret = kvm_arm_set_default_pmu(kvm);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
|
static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
|
||||||
@ -1239,21 +1343,21 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
|
|||||||
mutex_lock(&kvm->arch.config_lock);
|
mutex_lock(&kvm->arch.config_lock);
|
||||||
|
|
||||||
if (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags) &&
|
if (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags) &&
|
||||||
!bitmap_equal(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES))
|
kvm_vcpu_init_changed(vcpu, init))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
bitmap_copy(vcpu->arch.features, &features, KVM_VCPU_MAX_FEATURES);
|
|
||||||
|
|
||||||
/* Now we know what it is, we can reset it. */
|
|
||||||
ret = kvm_reset_vcpu(vcpu);
|
|
||||||
if (ret) {
|
|
||||||
bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES);
|
bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES);
|
||||||
|
|
||||||
|
ret = kvm_setup_vcpu(vcpu);
|
||||||
|
if (ret)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
/* Now we know what it is, we can reset it. */
|
||||||
|
kvm_reset_vcpu(vcpu);
|
||||||
|
|
||||||
set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags);
|
set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags);
|
||||||
vcpu_set_flag(vcpu, VCPU_INITIALIZED);
|
vcpu_set_flag(vcpu, VCPU_INITIALIZED);
|
||||||
|
ret = 0;
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&kvm->arch.config_lock);
|
mutex_unlock(&kvm->arch.config_lock);
|
||||||
return ret;
|
return ret;
|
||||||
@ -1278,7 +1382,8 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
|
|||||||
if (kvm_vcpu_init_changed(vcpu, init))
|
if (kvm_vcpu_init_changed(vcpu, init))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return kvm_reset_vcpu(vcpu);
|
kvm_reset_vcpu(vcpu);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
|
static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
|
||||||
@ -1629,6 +1734,13 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||||||
|
|
||||||
return kvm_vm_set_attr(kvm, &attr);
|
return kvm_vm_set_attr(kvm, &attr);
|
||||||
}
|
}
|
||||||
|
case KVM_ARM_GET_REG_WRITABLE_MASKS: {
|
||||||
|
struct reg_mask_range range;
|
||||||
|
|
||||||
|
if (copy_from_user(&range, argp, sizeof(range)))
|
||||||
|
return -EFAULT;
|
||||||
|
return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -2341,6 +2453,18 @@ struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr)
|
|||||||
unsigned long i;
|
unsigned long i;
|
||||||
|
|
||||||
mpidr &= MPIDR_HWID_BITMASK;
|
mpidr &= MPIDR_HWID_BITMASK;
|
||||||
|
|
||||||
|
if (kvm->arch.mpidr_data) {
|
||||||
|
u16 idx = kvm_mpidr_index(kvm->arch.mpidr_data, mpidr);
|
||||||
|
|
||||||
|
vcpu = kvm_get_vcpu(kvm,
|
||||||
|
kvm->arch.mpidr_data->cmpidr_to_idx[idx]);
|
||||||
|
if (mpidr != kvm_vcpu_get_mpidr_aff(vcpu))
|
||||||
|
vcpu = NULL;
|
||||||
|
|
||||||
|
return vcpu;
|
||||||
|
}
|
||||||
|
|
||||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||||
if (mpidr == kvm_vcpu_get_mpidr_aff(vcpu))
|
if (mpidr == kvm_vcpu_get_mpidr_aff(vcpu))
|
||||||
return vcpu;
|
return vcpu;
|
||||||
|
@ -648,15 +648,80 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
|
|||||||
SR_TRAP(SYS_APGAKEYLO_EL1, CGT_HCR_APK),
|
SR_TRAP(SYS_APGAKEYLO_EL1, CGT_HCR_APK),
|
||||||
SR_TRAP(SYS_APGAKEYHI_EL1, CGT_HCR_APK),
|
SR_TRAP(SYS_APGAKEYHI_EL1, CGT_HCR_APK),
|
||||||
/* All _EL2 registers */
|
/* All _EL2 registers */
|
||||||
SR_RANGE_TRAP(sys_reg(3, 4, 0, 0, 0),
|
SR_TRAP(SYS_BRBCR_EL2, CGT_HCR_NV),
|
||||||
sys_reg(3, 4, 3, 15, 7), CGT_HCR_NV),
|
SR_TRAP(SYS_VPIDR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VMPIDR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_SCTLR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_ACTLR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_SCTLR2_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_HCR_EL2,
|
||||||
|
SYS_HCRX_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_SMPRIMAP_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_SMCR_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_TTBR0_EL2,
|
||||||
|
SYS_TCR2_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VTTBR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VTCR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VNCR_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_HDFGRTR_EL2,
|
||||||
|
SYS_HAFGRTR_EL2, CGT_HCR_NV),
|
||||||
/* Skip the SP_EL1 encoding... */
|
/* Skip the SP_EL1 encoding... */
|
||||||
SR_TRAP(SYS_SPSR_EL2, CGT_HCR_NV),
|
SR_TRAP(SYS_SPSR_EL2, CGT_HCR_NV),
|
||||||
SR_TRAP(SYS_ELR_EL2, CGT_HCR_NV),
|
SR_TRAP(SYS_ELR_EL2, CGT_HCR_NV),
|
||||||
SR_RANGE_TRAP(sys_reg(3, 4, 4, 1, 1),
|
/* Skip SPSR_irq, SPSR_abt, SPSR_und, SPSR_fiq */
|
||||||
sys_reg(3, 4, 10, 15, 7), CGT_HCR_NV),
|
SR_TRAP(SYS_AFSR0_EL2, CGT_HCR_NV),
|
||||||
SR_RANGE_TRAP(sys_reg(3, 4, 12, 0, 0),
|
SR_TRAP(SYS_AFSR1_EL2, CGT_HCR_NV),
|
||||||
sys_reg(3, 4, 14, 15, 7), CGT_HCR_NV),
|
SR_TRAP(SYS_ESR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VSESR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_TFSR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_FAR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_HPFAR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_PMSCR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_MAIR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_AMAIR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_MPAMHCR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_MPAMVPMV_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_MPAM2_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_MPAMVPM0_EL2,
|
||||||
|
SYS_MPAMVPM7_EL2, CGT_HCR_NV),
|
||||||
|
/*
|
||||||
|
* Note that the spec. describes a group of MEC registers
|
||||||
|
* whose access should not trap, therefore skip the following:
|
||||||
|
* MECID_A0_EL2, MECID_A1_EL2, MECID_P0_EL2,
|
||||||
|
* MECID_P1_EL2, MECIDR_EL2, VMECID_A_EL2,
|
||||||
|
* VMECID_P_EL2.
|
||||||
|
*/
|
||||||
|
SR_RANGE_TRAP(SYS_VBAR_EL2,
|
||||||
|
SYS_RMR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_VDISR_EL2, CGT_HCR_NV),
|
||||||
|
/* ICH_AP0R<m>_EL2 */
|
||||||
|
SR_RANGE_TRAP(SYS_ICH_AP0R0_EL2,
|
||||||
|
SYS_ICH_AP0R3_EL2, CGT_HCR_NV),
|
||||||
|
/* ICH_AP1R<m>_EL2 */
|
||||||
|
SR_RANGE_TRAP(SYS_ICH_AP1R0_EL2,
|
||||||
|
SYS_ICH_AP1R3_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_ICC_SRE_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_ICH_HCR_EL2,
|
||||||
|
SYS_ICH_EISR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_ICH_ELRSR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_ICH_VMCR_EL2, CGT_HCR_NV),
|
||||||
|
/* ICH_LR<m>_EL2 */
|
||||||
|
SR_RANGE_TRAP(SYS_ICH_LR0_EL2,
|
||||||
|
SYS_ICH_LR15_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_CONTEXTIDR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_TPIDR_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_SCXTNUM_EL2, CGT_HCR_NV),
|
||||||
|
/* AMEVCNTVOFF0<n>_EL2, AMEVCNTVOFF1<n>_EL2 */
|
||||||
|
SR_RANGE_TRAP(SYS_AMEVCNTVOFF0n_EL2(0),
|
||||||
|
SYS_AMEVCNTVOFF1n_EL2(15), CGT_HCR_NV),
|
||||||
|
/* CNT*_EL2 */
|
||||||
|
SR_TRAP(SYS_CNTVOFF_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_CNTPOFF_EL2, CGT_HCR_NV),
|
||||||
|
SR_TRAP(SYS_CNTHCTL_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_CNTHP_TVAL_EL2,
|
||||||
|
SYS_CNTHP_CVAL_EL2, CGT_HCR_NV),
|
||||||
|
SR_RANGE_TRAP(SYS_CNTHV_TVAL_EL2,
|
||||||
|
SYS_CNTHV_CVAL_EL2, CGT_HCR_NV),
|
||||||
/* All _EL02, _EL12 registers */
|
/* All _EL02, _EL12 registers */
|
||||||
SR_RANGE_TRAP(sys_reg(3, 5, 0, 0, 0),
|
SR_RANGE_TRAP(sys_reg(3, 5, 0, 0, 0),
|
||||||
sys_reg(3, 5, 10, 15, 7), CGT_HCR_NV),
|
sys_reg(3, 5, 10, 15, 7), CGT_HCR_NV),
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <asm/fpsimd.h>
|
#include <asm/fpsimd.h>
|
||||||
#include <asm/debug-monitors.h>
|
#include <asm/debug-monitors.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
|
#include <asm/traps.h>
|
||||||
|
|
||||||
struct kvm_exception_table_entry {
|
struct kvm_exception_table_entry {
|
||||||
int insn, fixup;
|
int insn, fixup;
|
||||||
@ -265,6 +266,22 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu)
|
|||||||
return __get_fault_info(vcpu->arch.fault.esr_el2, &vcpu->arch.fault);
|
return __get_fault_info(vcpu->arch.fault.esr_el2, &vcpu->arch.fault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool kvm_hyp_handle_mops(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||||
|
{
|
||||||
|
*vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR);
|
||||||
|
arm64_mops_reset_regs(vcpu_gp_regs(vcpu), vcpu->arch.fault.esr_el2);
|
||||||
|
write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finish potential single step before executing the prologue
|
||||||
|
* instruction.
|
||||||
|
*/
|
||||||
|
*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
||||||
|
write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu)
|
static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2);
|
sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2);
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
|
|
||||||
#define PVM_ID_AA64ISAR2_ALLOW (\
|
#define PVM_ID_AA64ISAR2_ALLOW (\
|
||||||
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3) | \
|
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3) | \
|
||||||
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) \
|
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_APA3) | \
|
||||||
|
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_MOPS) \
|
||||||
)
|
)
|
||||||
|
|
||||||
u64 pvm_read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
|
u64 pvm_read_id_reg(const struct kvm_vcpu *vcpu, u32 id);
|
||||||
|
@ -129,8 +129,8 @@ static void prepare_host_vtcr(void)
|
|||||||
parange = kvm_get_parange(id_aa64mmfr0_el1_sys_val);
|
parange = kvm_get_parange(id_aa64mmfr0_el1_sys_val);
|
||||||
phys_shift = id_aa64mmfr0_parange_to_phys_shift(parange);
|
phys_shift = id_aa64mmfr0_parange_to_phys_shift(parange);
|
||||||
|
|
||||||
host_mmu.arch.vtcr = kvm_get_vtcr(id_aa64mmfr0_el1_sys_val,
|
host_mmu.arch.mmu.vtcr = kvm_get_vtcr(id_aa64mmfr0_el1_sys_val,
|
||||||
id_aa64mmfr1_el1_sys_val, phys_shift);
|
id_aa64mmfr1_el1_sys_val, phys_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot);
|
static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot);
|
||||||
@ -235,7 +235,7 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)
|
|||||||
unsigned long nr_pages;
|
unsigned long nr_pages;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
nr_pages = kvm_pgtable_stage2_pgd_size(vm->kvm.arch.vtcr) >> PAGE_SHIFT;
|
nr_pages = kvm_pgtable_stage2_pgd_size(mmu->vtcr) >> PAGE_SHIFT;
|
||||||
ret = hyp_pool_init(&vm->pool, hyp_virt_to_pfn(pgd), nr_pages, 0);
|
ret = hyp_pool_init(&vm->pool, hyp_virt_to_pfn(pgd), nr_pages, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -295,7 +295,7 @@ int __pkvm_prot_finalize(void)
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
params->vttbr = kvm_get_vttbr(mmu);
|
params->vttbr = kvm_get_vttbr(mmu);
|
||||||
params->vtcr = host_mmu.arch.vtcr;
|
params->vtcr = mmu->vtcr;
|
||||||
params->hcr_el2 |= HCR_VM;
|
params->hcr_el2 |= HCR_VM;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -303,7 +303,7 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm,
|
|||||||
{
|
{
|
||||||
hyp_vm->host_kvm = host_kvm;
|
hyp_vm->host_kvm = host_kvm;
|
||||||
hyp_vm->kvm.created_vcpus = nr_vcpus;
|
hyp_vm->kvm.created_vcpus = nr_vcpus;
|
||||||
hyp_vm->kvm.arch.vtcr = host_mmu.arch.vtcr;
|
hyp_vm->kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
|
static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
|
||||||
@ -483,7 +483,7 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
|
|||||||
}
|
}
|
||||||
|
|
||||||
vm_size = pkvm_get_hyp_vm_size(nr_vcpus);
|
vm_size = pkvm_get_hyp_vm_size(nr_vcpus);
|
||||||
pgd_size = kvm_pgtable_stage2_pgd_size(host_mmu.arch.vtcr);
|
pgd_size = kvm_pgtable_stage2_pgd_size(host_mmu.arch.mmu.vtcr);
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
|
||||||
|
@ -192,6 +192,7 @@ static const exit_handler_fn hyp_exit_handlers[] = {
|
|||||||
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
||||||
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
||||||
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
||||||
|
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const exit_handler_fn pvm_exit_handlers[] = {
|
static const exit_handler_fn pvm_exit_handlers[] = {
|
||||||
@ -203,6 +204,7 @@ static const exit_handler_fn pvm_exit_handlers[] = {
|
|||||||
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
||||||
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
||||||
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
||||||
|
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
||||||
|
@ -1314,7 +1314,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
|
|||||||
ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level,
|
ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level,
|
||||||
KVM_PGTABLE_WALK_HANDLE_FAULT |
|
KVM_PGTABLE_WALK_HANDLE_FAULT |
|
||||||
KVM_PGTABLE_WALK_SHARED);
|
KVM_PGTABLE_WALK_SHARED);
|
||||||
if (!ret)
|
if (!ret || ret == -EAGAIN)
|
||||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level);
|
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1511,7 +1511,7 @@ int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
|||||||
kvm_pgtable_force_pte_cb_t force_pte_cb)
|
kvm_pgtable_force_pte_cb_t force_pte_cb)
|
||||||
{
|
{
|
||||||
size_t pgd_sz;
|
size_t pgd_sz;
|
||||||
u64 vtcr = mmu->arch->vtcr;
|
u64 vtcr = mmu->vtcr;
|
||||||
u32 ia_bits = VTCR_EL2_IPA(vtcr);
|
u32 ia_bits = VTCR_EL2_IPA(vtcr);
|
||||||
u32 sl0 = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr);
|
u32 sl0 = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr);
|
||||||
u32 start_level = VTCR_EL2_TGRAN_SL0_BASE - sl0;
|
u32 start_level = VTCR_EL2_TGRAN_SL0_BASE - sl0;
|
||||||
|
@ -137,12 +137,12 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
|
|||||||
NOKPROBE_SYMBOL(__deactivate_traps);
|
NOKPROBE_SYMBOL(__deactivate_traps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable IRQs in {activate,deactivate}_traps_vhe_{load,put}() to
|
* Disable IRQs in __vcpu_{load,put}_{activate,deactivate}_traps() to
|
||||||
* prevent a race condition between context switching of PMUSERENR_EL0
|
* prevent a race condition between context switching of PMUSERENR_EL0
|
||||||
* in __{activate,deactivate}_traps_common() and IPIs that attempts to
|
* in __{activate,deactivate}_traps_common() and IPIs that attempts to
|
||||||
* update PMUSERENR_EL0. See also kvm_set_pmuserenr().
|
* update PMUSERENR_EL0. See also kvm_set_pmuserenr().
|
||||||
*/
|
*/
|
||||||
void activate_traps_vhe_load(struct kvm_vcpu *vcpu)
|
static void __vcpu_load_activate_traps(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ void activate_traps_vhe_load(struct kvm_vcpu *vcpu)
|
|||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu)
|
static void __vcpu_put_deactivate_traps(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
@ -160,6 +160,19 @@ void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu)
|
|||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
__vcpu_load_switch_sysregs(vcpu);
|
||||||
|
__vcpu_load_activate_traps(vcpu);
|
||||||
|
__load_stage2(vcpu->arch.hw_mmu, vcpu->arch.hw_mmu->arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
__vcpu_put_deactivate_traps(vcpu);
|
||||||
|
__vcpu_put_switch_sysregs(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
static const exit_handler_fn hyp_exit_handlers[] = {
|
static const exit_handler_fn hyp_exit_handlers[] = {
|
||||||
[0 ... ESR_ELx_EC_MAX] = NULL,
|
[0 ... ESR_ELx_EC_MAX] = NULL,
|
||||||
[ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32,
|
[ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32,
|
||||||
@ -170,6 +183,7 @@ static const exit_handler_fn hyp_exit_handlers[] = {
|
|||||||
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
|
||||||
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
|
||||||
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
[ESR_ELx_EC_PAC] = kvm_hyp_handle_ptrauth,
|
||||||
|
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
|
||||||
@ -214,17 +228,11 @@ static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)
|
|||||||
sysreg_save_host_state_vhe(host_ctxt);
|
sysreg_save_host_state_vhe(host_ctxt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ARM erratum 1165522 requires us to configure both stage 1 and
|
* Note that ARM erratum 1165522 requires us to configure both stage 1
|
||||||
* stage 2 translation for the guest context before we clear
|
* and stage 2 translation for the guest context before we clear
|
||||||
* HCR_EL2.TGE.
|
* HCR_EL2.TGE. The stage 1 and stage 2 guest context has already been
|
||||||
*
|
* loaded on the CPU in kvm_vcpu_load_vhe().
|
||||||
* We have already configured the guest's stage 1 translation in
|
|
||||||
* kvm_vcpu_load_sysregs_vhe above. We must now call
|
|
||||||
* __load_stage2 before __activate_traps, because
|
|
||||||
* __load_stage2 configures stage 2 translation, and
|
|
||||||
* __activate_traps clear HCR_EL2.TGE (among other things).
|
|
||||||
*/
|
*/
|
||||||
__load_stage2(vcpu->arch.hw_mmu, vcpu->arch.hw_mmu->arch);
|
|
||||||
__activate_traps(vcpu);
|
__activate_traps(vcpu);
|
||||||
|
|
||||||
__kvm_adjust_pc(vcpu);
|
__kvm_adjust_pc(vcpu);
|
||||||
|
@ -52,7 +52,7 @@ void sysreg_restore_guest_state_vhe(struct kvm_cpu_context *ctxt)
|
|||||||
NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe);
|
NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_vcpu_load_sysregs_vhe - Load guest system registers to the physical CPU
|
* __vcpu_load_switch_sysregs - Load guest system registers to the physical CPU
|
||||||
*
|
*
|
||||||
* @vcpu: The VCPU pointer
|
* @vcpu: The VCPU pointer
|
||||||
*
|
*
|
||||||
@ -62,7 +62,7 @@ NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe);
|
|||||||
* and loading system register state early avoids having to load them on
|
* and loading system register state early avoids having to load them on
|
||||||
* every entry to the VM.
|
* every entry to the VM.
|
||||||
*/
|
*/
|
||||||
void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu)
|
void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||||
struct kvm_cpu_context *host_ctxt;
|
struct kvm_cpu_context *host_ctxt;
|
||||||
@ -92,12 +92,10 @@ void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu)
|
|||||||
__sysreg_restore_el1_state(guest_ctxt);
|
__sysreg_restore_el1_state(guest_ctxt);
|
||||||
|
|
||||||
vcpu_set_flag(vcpu, SYSREGS_ON_CPU);
|
vcpu_set_flag(vcpu, SYSREGS_ON_CPU);
|
||||||
|
|
||||||
activate_traps_vhe_load(vcpu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_vcpu_put_sysregs_vhe - Restore host system registers to the physical CPU
|
* __vcpu_put_switch_syregs - Restore host system registers to the physical CPU
|
||||||
*
|
*
|
||||||
* @vcpu: The VCPU pointer
|
* @vcpu: The VCPU pointer
|
||||||
*
|
*
|
||||||
@ -107,13 +105,12 @@ void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu)
|
|||||||
* and deferring saving system register state until we're no longer running the
|
* and deferring saving system register state until we're no longer running the
|
||||||
* VCPU avoids having to save them on every exit from the VM.
|
* VCPU avoids having to save them on every exit from the VM.
|
||||||
*/
|
*/
|
||||||
void kvm_vcpu_put_sysregs_vhe(struct kvm_vcpu *vcpu)
|
void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||||
struct kvm_cpu_context *host_ctxt;
|
struct kvm_cpu_context *host_ctxt;
|
||||||
|
|
||||||
host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt;
|
host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt;
|
||||||
deactivate_traps_vhe_put(vcpu);
|
|
||||||
|
|
||||||
__sysreg_save_el1_state(guest_ctxt);
|
__sysreg_save_el1_state(guest_ctxt);
|
||||||
__sysreg_save_user_state(guest_ctxt);
|
__sysreg_save_user_state(guest_ctxt);
|
||||||
|
@ -11,18 +11,25 @@
|
|||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
|
|
||||||
struct tlb_inv_context {
|
struct tlb_inv_context {
|
||||||
unsigned long flags;
|
struct kvm_s2_mmu *mmu;
|
||||||
u64 tcr;
|
unsigned long flags;
|
||||||
u64 sctlr;
|
u64 tcr;
|
||||||
|
u64 sctlr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,
|
static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,
|
||||||
struct tlb_inv_context *cxt)
|
struct tlb_inv_context *cxt)
|
||||||
{
|
{
|
||||||
|
struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
local_irq_save(cxt->flags);
|
local_irq_save(cxt->flags);
|
||||||
|
|
||||||
|
if (vcpu && mmu != vcpu->arch.hw_mmu)
|
||||||
|
cxt->mmu = vcpu->arch.hw_mmu;
|
||||||
|
else
|
||||||
|
cxt->mmu = NULL;
|
||||||
|
|
||||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||||
/*
|
/*
|
||||||
* For CPUs that are affected by ARM errata 1165522 or 1530923,
|
* For CPUs that are affected by ARM errata 1165522 or 1530923,
|
||||||
@ -66,10 +73,13 @@ static void __tlb_switch_to_host(struct tlb_inv_context *cxt)
|
|||||||
* We're done with the TLB operation, let's restore the host's
|
* We're done with the TLB operation, let's restore the host's
|
||||||
* view of HCR_EL2.
|
* view of HCR_EL2.
|
||||||
*/
|
*/
|
||||||
write_sysreg(0, vttbr_el2);
|
|
||||||
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
||||||
isb();
|
isb();
|
||||||
|
|
||||||
|
/* ... and the stage-2 MMU context that we switched away from */
|
||||||
|
if (cxt->mmu)
|
||||||
|
__load_stage2(cxt->mmu, cxt->mmu->arch);
|
||||||
|
|
||||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||||
/* Restore the registers to what they were */
|
/* Restore the registers to what they were */
|
||||||
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
||||||
|
@ -133,12 +133,10 @@ static bool kvm_smccc_test_fw_bmap(struct kvm_vcpu *vcpu, u32 func_id)
|
|||||||
ARM_SMCCC_SMC_64, \
|
ARM_SMCCC_SMC_64, \
|
||||||
0, ARM_SMCCC_FUNC_MASK)
|
0, ARM_SMCCC_FUNC_MASK)
|
||||||
|
|
||||||
static void init_smccc_filter(struct kvm *kvm)
|
static int kvm_smccc_filter_insert_reserved(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
mt_init(&kvm->arch.smccc_filter);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prevent userspace from handling any SMCCC calls in the architecture
|
* Prevent userspace from handling any SMCCC calls in the architecture
|
||||||
* range, avoiding the risk of misrepresenting Spectre mitigation status
|
* range, avoiding the risk of misrepresenting Spectre mitigation status
|
||||||
@ -148,14 +146,25 @@ static void init_smccc_filter(struct kvm *kvm)
|
|||||||
SMC32_ARCH_RANGE_BEGIN, SMC32_ARCH_RANGE_END,
|
SMC32_ARCH_RANGE_BEGIN, SMC32_ARCH_RANGE_END,
|
||||||
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
||||||
GFP_KERNEL_ACCOUNT);
|
GFP_KERNEL_ACCOUNT);
|
||||||
WARN_ON_ONCE(r);
|
if (r)
|
||||||
|
goto out_destroy;
|
||||||
|
|
||||||
r = mtree_insert_range(&kvm->arch.smccc_filter,
|
r = mtree_insert_range(&kvm->arch.smccc_filter,
|
||||||
SMC64_ARCH_RANGE_BEGIN, SMC64_ARCH_RANGE_END,
|
SMC64_ARCH_RANGE_BEGIN, SMC64_ARCH_RANGE_END,
|
||||||
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
||||||
GFP_KERNEL_ACCOUNT);
|
GFP_KERNEL_ACCOUNT);
|
||||||
WARN_ON_ONCE(r);
|
if (r)
|
||||||
|
goto out_destroy;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
out_destroy:
|
||||||
|
mtree_destroy(&kvm->arch.smccc_filter);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool kvm_smccc_filter_configured(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return !mtree_empty(&kvm->arch.smccc_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user *uaddr)
|
static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user *uaddr)
|
||||||
@ -184,13 +193,14 @@ static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user
|
|||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!kvm_smccc_filter_configured(kvm)) {
|
||||||
|
r = kvm_smccc_filter_insert_reserved(kvm);
|
||||||
|
if (WARN_ON_ONCE(r))
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
|
r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
|
||||||
xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
|
xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
|
||||||
if (r)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
set_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags);
|
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&kvm->arch.config_lock);
|
mutex_unlock(&kvm->arch.config_lock);
|
||||||
return r;
|
return r;
|
||||||
@ -201,7 +211,7 @@ static u8 kvm_smccc_filter_get_action(struct kvm *kvm, u32 func_id)
|
|||||||
unsigned long idx = func_id;
|
unsigned long idx = func_id;
|
||||||
void *val;
|
void *val;
|
||||||
|
|
||||||
if (!test_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags))
|
if (!kvm_smccc_filter_configured(kvm))
|
||||||
return KVM_SMCCC_FILTER_HANDLE;
|
return KVM_SMCCC_FILTER_HANDLE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -387,7 +397,7 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
|
|||||||
smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
|
smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
|
||||||
smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
|
smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
|
||||||
|
|
||||||
init_smccc_filter(kvm);
|
mt_init(&kvm->arch.smccc_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arm_teardown_hypercalls(struct kvm *kvm)
|
void kvm_arm_teardown_hypercalls(struct kvm *kvm)
|
||||||
@ -554,7 +564,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||||||
{
|
{
|
||||||
bool wants_02;
|
bool wants_02;
|
||||||
|
|
||||||
wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
|
wants_02 = vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2);
|
||||||
|
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case KVM_ARM_PSCI_0_1:
|
case KVM_ARM_PSCI_0_1:
|
||||||
|
@ -135,6 +135,9 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
|||||||
* volunteered to do so, and bail out otherwise.
|
* volunteered to do so, and bail out otherwise.
|
||||||
*/
|
*/
|
||||||
if (!kvm_vcpu_dabt_isvalid(vcpu)) {
|
if (!kvm_vcpu_dabt_isvalid(vcpu)) {
|
||||||
|
trace_kvm_mmio_nisv(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
|
||||||
|
kvm_vcpu_get_hfar(vcpu), fault_ipa);
|
||||||
|
|
||||||
if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
|
if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
|
||||||
&vcpu->kvm->arch.flags)) {
|
&vcpu->kvm->arch.flags)) {
|
||||||
run->exit_reason = KVM_EXIT_ARM_NISV;
|
run->exit_reason = KVM_EXIT_ARM_NISV;
|
||||||
@ -143,7 +146,6 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_pr_unimpl("Data abort outside memslots with no valid syndrome info\n");
|
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,7 +892,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
|
|||||||
|
|
||||||
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
|
mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
|
||||||
mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
||||||
kvm->arch.vtcr = kvm_get_vtcr(mmfr0, mmfr1, phys_shift);
|
mmu->vtcr = kvm_get_vtcr(mmfr0, mmfr1, phys_shift);
|
||||||
|
|
||||||
if (mmu->pgt != NULL) {
|
if (mmu->pgt != NULL) {
|
||||||
kvm_err("kvm_arch already initialized?\n");
|
kvm_err("kvm_arch already initialized?\n");
|
||||||
@ -1067,7 +1067,8 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||||||
phys_addr_t addr;
|
phys_addr_t addr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
|
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
|
||||||
struct kvm_pgtable *pgt = kvm->arch.mmu.pgt;
|
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
|
||||||
|
struct kvm_pgtable *pgt = mmu->pgt;
|
||||||
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_DEVICE |
|
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_DEVICE |
|
||||||
KVM_PGTABLE_PROT_R |
|
KVM_PGTABLE_PROT_R |
|
||||||
(writable ? KVM_PGTABLE_PROT_W : 0);
|
(writable ? KVM_PGTABLE_PROT_W : 0);
|
||||||
@ -1080,7 +1081,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||||||
|
|
||||||
for (addr = guest_ipa; addr < guest_ipa + size; addr += PAGE_SIZE) {
|
for (addr = guest_ipa; addr < guest_ipa + size; addr += PAGE_SIZE) {
|
||||||
ret = kvm_mmu_topup_memory_cache(&cache,
|
ret = kvm_mmu_topup_memory_cache(&cache,
|
||||||
kvm_mmu_cache_min_pages(kvm));
|
kvm_mmu_cache_min_pages(mmu));
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1298,28 +1299,8 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
|||||||
if (sz < PMD_SIZE)
|
if (sz < PMD_SIZE)
|
||||||
return PAGE_SIZE;
|
return PAGE_SIZE;
|
||||||
|
|
||||||
/*
|
|
||||||
* The address we faulted on is backed by a transparent huge
|
|
||||||
* page. However, because we map the compound huge page and
|
|
||||||
* not the individual tail page, we need to transfer the
|
|
||||||
* refcount to the head page. We have to be careful that the
|
|
||||||
* THP doesn't start to split while we are adjusting the
|
|
||||||
* refcounts.
|
|
||||||
*
|
|
||||||
* We are sure this doesn't happen, because mmu_invalidate_retry
|
|
||||||
* was successful and we are holding the mmu_lock, so if this
|
|
||||||
* THP is trying to split, it will be blocked in the mmu
|
|
||||||
* notifier before touching any of the pages, specifically
|
|
||||||
* before being able to call __split_huge_page_refcount().
|
|
||||||
*
|
|
||||||
* We can therefore safely transfer the refcount from PG_tail
|
|
||||||
* to PG_head and switch the pfn from a tail page to the head
|
|
||||||
* page accordingly.
|
|
||||||
*/
|
|
||||||
*ipap &= PMD_MASK;
|
*ipap &= PMD_MASK;
|
||||||
kvm_release_pfn_clean(pfn);
|
|
||||||
pfn &= ~(PTRS_PER_PMD - 1);
|
pfn &= ~(PTRS_PER_PMD - 1);
|
||||||
get_page(pfn_to_page(pfn));
|
|
||||||
*pfnp = pfn;
|
*pfnp = pfn;
|
||||||
|
|
||||||
return PMD_SIZE;
|
return PMD_SIZE;
|
||||||
@ -1431,7 +1412,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||||||
if (fault_status != ESR_ELx_FSC_PERM ||
|
if (fault_status != ESR_ELx_FSC_PERM ||
|
||||||
(logging_active && write_fault)) {
|
(logging_active && write_fault)) {
|
||||||
ret = kvm_mmu_topup_memory_cache(memcache,
|
ret = kvm_mmu_topup_memory_cache(memcache,
|
||||||
kvm_mmu_cache_min_pages(kvm));
|
kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1747,7 +1728,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Userspace should not be able to register out-of-bounds IPAs */
|
/* Userspace should not be able to register out-of-bounds IPAs */
|
||||||
VM_BUG_ON(fault_ipa >= kvm_phys_size(vcpu->kvm));
|
VM_BUG_ON(fault_ipa >= kvm_phys_size(vcpu->arch.hw_mmu));
|
||||||
|
|
||||||
if (fault_status == ESR_ELx_FSC_ACCESS) {
|
if (fault_status == ESR_ELx_FSC_ACCESS) {
|
||||||
handle_access_fault(vcpu, fault_ipa);
|
handle_access_fault(vcpu, fault_ipa);
|
||||||
@ -2021,7 +2002,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
|||||||
* Prevent userspace from creating a memory region outside of the IPA
|
* Prevent userspace from creating a memory region outside of the IPA
|
||||||
* space addressable by the KVM guest IPA space.
|
* space addressable by the KVM guest IPA space.
|
||||||
*/
|
*/
|
||||||
if ((new->base_gfn + new->npages) > (kvm_phys_size(kvm) >> PAGE_SHIFT))
|
if ((new->base_gfn + new->npages) > (kvm_phys_size(&kvm->arch.mmu) >> PAGE_SHIFT))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
hva = new->userspace_addr;
|
hva = new->userspace_addr;
|
||||||
|
@ -123,7 +123,7 @@ static int __pkvm_create_hyp_vm(struct kvm *host_kvm)
|
|||||||
if (host_kvm->created_vcpus < 1)
|
if (host_kvm->created_vcpus < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pgd_sz = kvm_pgtable_stage2_pgd_size(host_kvm->arch.vtcr);
|
pgd_sz = kvm_pgtable_stage2_pgd_size(host_kvm->arch.mmu.vtcr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The PGD pages will be reclaimed using a hyp_memcache which implies
|
* The PGD pages will be reclaimed using a hyp_memcache which implies
|
||||||
|
@ -60,6 +60,23 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm)
|
|||||||
return __kvm_pmu_event_mask(pmuver);
|
return __kvm_pmu_event_mask(pmuver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
u64 mask = ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 |
|
||||||
|
kvm_pmu_event_mask(kvm);
|
||||||
|
u64 pfr0 = IDREG(kvm, SYS_ID_AA64PFR0_EL1);
|
||||||
|
|
||||||
|
if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL2, pfr0))
|
||||||
|
mask |= ARMV8_PMU_INCLUDE_EL2;
|
||||||
|
|
||||||
|
if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL3, pfr0))
|
||||||
|
mask |= ARMV8_PMU_EXCLUDE_NS_EL0 |
|
||||||
|
ARMV8_PMU_EXCLUDE_NS_EL1 |
|
||||||
|
ARMV8_PMU_EXCLUDE_EL3;
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_pmc_is_64bit - determine if counter is 64bit
|
* kvm_pmc_is_64bit - determine if counter is 64bit
|
||||||
* @pmc: counter context
|
* @pmc: counter context
|
||||||
@ -72,7 +89,7 @@ static bool kvm_pmc_is_64bit(struct kvm_pmc *pmc)
|
|||||||
|
|
||||||
static bool kvm_pmc_has_64bit_overflow(struct kvm_pmc *pmc)
|
static bool kvm_pmc_has_64bit_overflow(struct kvm_pmc *pmc)
|
||||||
{
|
{
|
||||||
u64 val = __vcpu_sys_reg(kvm_pmc_to_vcpu(pmc), PMCR_EL0);
|
u64 val = kvm_vcpu_read_pmcr(kvm_pmc_to_vcpu(pmc));
|
||||||
|
|
||||||
return (pmc->idx < ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LP)) ||
|
return (pmc->idx < ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LP)) ||
|
||||||
(pmc->idx == ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LC));
|
(pmc->idx == ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LC));
|
||||||
@ -250,7 +267,7 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
|
u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
u64 val = __vcpu_sys_reg(vcpu, PMCR_EL0) >> ARMV8_PMU_PMCR_N_SHIFT;
|
u64 val = kvm_vcpu_read_pmcr(vcpu) >> ARMV8_PMU_PMCR_N_SHIFT;
|
||||||
|
|
||||||
val &= ARMV8_PMU_PMCR_N_MASK;
|
val &= ARMV8_PMU_PMCR_N_MASK;
|
||||||
if (val == 0)
|
if (val == 0)
|
||||||
@ -272,7 +289,7 @@ void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
|
|||||||
if (!kvm_vcpu_has_pmu(vcpu))
|
if (!kvm_vcpu_has_pmu(vcpu))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val)
|
if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) || !val)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
|
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
|
||||||
@ -324,7 +341,7 @@ static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
|
|||||||
{
|
{
|
||||||
u64 reg = 0;
|
u64 reg = 0;
|
||||||
|
|
||||||
if ((__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) {
|
if ((kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) {
|
||||||
reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
|
reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
|
||||||
reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
|
reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
|
||||||
reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
|
reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
|
||||||
@ -348,7 +365,7 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu)
|
|||||||
pmu->irq_level = overflow;
|
pmu->irq_level = overflow;
|
||||||
|
|
||||||
if (likely(irqchip_in_kernel(vcpu->kvm))) {
|
if (likely(irqchip_in_kernel(vcpu->kvm))) {
|
||||||
int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
|
int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu,
|
||||||
pmu->irq_num, overflow, pmu);
|
pmu->irq_num, overflow, pmu);
|
||||||
WARN_ON(ret);
|
WARN_ON(ret);
|
||||||
}
|
}
|
||||||
@ -426,7 +443,7 @@ static void kvm_pmu_counter_increment(struct kvm_vcpu *vcpu,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E))
|
if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Weed out disabled counters */
|
/* Weed out disabled counters */
|
||||||
@ -569,7 +586,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
|
|||||||
static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)
|
static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)
|
||||||
{
|
{
|
||||||
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
|
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
|
||||||
return (__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) &&
|
return (kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) &&
|
||||||
(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx));
|
(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,6 +601,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
|
|||||||
struct perf_event *event;
|
struct perf_event *event;
|
||||||
struct perf_event_attr attr;
|
struct perf_event_attr attr;
|
||||||
u64 eventsel, reg, data;
|
u64 eventsel, reg, data;
|
||||||
|
bool p, u, nsk, nsu;
|
||||||
|
|
||||||
reg = counter_index_to_evtreg(pmc->idx);
|
reg = counter_index_to_evtreg(pmc->idx);
|
||||||
data = __vcpu_sys_reg(vcpu, reg);
|
data = __vcpu_sys_reg(vcpu, reg);
|
||||||
@ -610,13 +628,18 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
|
|||||||
!test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
|
!test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
p = data & ARMV8_PMU_EXCLUDE_EL1;
|
||||||
|
u = data & ARMV8_PMU_EXCLUDE_EL0;
|
||||||
|
nsk = data & ARMV8_PMU_EXCLUDE_NS_EL1;
|
||||||
|
nsu = data & ARMV8_PMU_EXCLUDE_NS_EL0;
|
||||||
|
|
||||||
memset(&attr, 0, sizeof(struct perf_event_attr));
|
memset(&attr, 0, sizeof(struct perf_event_attr));
|
||||||
attr.type = arm_pmu->pmu.type;
|
attr.type = arm_pmu->pmu.type;
|
||||||
attr.size = sizeof(attr);
|
attr.size = sizeof(attr);
|
||||||
attr.pinned = 1;
|
attr.pinned = 1;
|
||||||
attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
|
attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
|
||||||
attr.exclude_user = data & ARMV8_PMU_EXCLUDE_EL0 ? 1 : 0;
|
attr.exclude_user = (u != nsu);
|
||||||
attr.exclude_kernel = data & ARMV8_PMU_EXCLUDE_EL1 ? 1 : 0;
|
attr.exclude_kernel = (p != nsk);
|
||||||
attr.exclude_hv = 1; /* Don't count EL2 events */
|
attr.exclude_hv = 1; /* Don't count EL2 events */
|
||||||
attr.exclude_host = 1; /* Don't count host events */
|
attr.exclude_host = 1; /* Don't count host events */
|
||||||
attr.config = eventsel;
|
attr.config = eventsel;
|
||||||
@ -657,18 +680,13 @@ void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
|||||||
u64 select_idx)
|
u64 select_idx)
|
||||||
{
|
{
|
||||||
struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, select_idx);
|
struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, select_idx);
|
||||||
u64 reg, mask;
|
u64 reg;
|
||||||
|
|
||||||
if (!kvm_vcpu_has_pmu(vcpu))
|
if (!kvm_vcpu_has_pmu(vcpu))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mask = ARMV8_PMU_EVTYPE_MASK;
|
|
||||||
mask &= ~ARMV8_PMU_EVTYPE_EVENT;
|
|
||||||
mask |= kvm_pmu_event_mask(vcpu->kvm);
|
|
||||||
|
|
||||||
reg = counter_index_to_evtreg(pmc->idx);
|
reg = counter_index_to_evtreg(pmc->idx);
|
||||||
|
__vcpu_sys_reg(vcpu, reg) = data & kvm_pmu_evtyper_mask(vcpu->kvm);
|
||||||
__vcpu_sys_reg(vcpu, reg) = data & mask;
|
|
||||||
|
|
||||||
kvm_pmu_create_perf_event(pmc);
|
kvm_pmu_create_perf_event(pmc);
|
||||||
}
|
}
|
||||||
@ -717,10 +735,9 @@ static struct arm_pmu *kvm_pmu_probe_armpmu(void)
|
|||||||
* It is still necessary to get a valid cpu, though, to probe for the
|
* It is still necessary to get a valid cpu, though, to probe for the
|
||||||
* default PMU instance as userspace is not required to specify a PMU
|
* default PMU instance as userspace is not required to specify a PMU
|
||||||
* type. In order to uphold the preexisting behavior KVM selects the
|
* type. In order to uphold the preexisting behavior KVM selects the
|
||||||
* PMU instance for the core where the first call to the
|
* PMU instance for the core during vcpu init. A dependent use
|
||||||
* KVM_ARM_VCPU_PMU_V3_CTRL attribute group occurs. A dependent use case
|
* case would be a user with disdain of all things big.LITTLE that
|
||||||
* would be a user with disdain of all things big.LITTLE that affines
|
* affines the VMM to a particular cluster of cores.
|
||||||
* the VMM to a particular cluster of cores.
|
|
||||||
*
|
*
|
||||||
* In any case, userspace should just do the sane thing and use the UAPI
|
* In any case, userspace should just do the sane thing and use the UAPI
|
||||||
* to select a PMU type directly. But, be wary of the baggage being
|
* to select a PMU type directly. But, be wary of the baggage being
|
||||||
@ -786,6 +803,17 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
|
|||||||
return val & mask;
|
return val & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u64 mask = kvm_pmu_valid_counter_mask(vcpu);
|
||||||
|
|
||||||
|
kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu));
|
||||||
|
|
||||||
|
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) &= mask;
|
||||||
|
__vcpu_sys_reg(vcpu, PMINTENSET_EL1) &= mask;
|
||||||
|
__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) &= mask;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
|
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (!kvm_vcpu_has_pmu(vcpu))
|
if (!kvm_vcpu_has_pmu(vcpu))
|
||||||
@ -874,6 +902,52 @@ static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_arm_pmu_get_max_counters - Return the max number of PMU counters.
|
||||||
|
* @kvm: The kvm pointer
|
||||||
|
*/
|
||||||
|
u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct arm_pmu *arm_pmu = kvm->arch.arm_pmu;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The arm_pmu->num_events considers the cycle counter as well.
|
||||||
|
* Ignore that and return only the general-purpose counters.
|
||||||
|
*/
|
||||||
|
return arm_pmu->num_events - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
|
||||||
|
{
|
||||||
|
lockdep_assert_held(&kvm->arch.config_lock);
|
||||||
|
|
||||||
|
kvm->arch.arm_pmu = arm_pmu;
|
||||||
|
kvm->arch.pmcr_n = kvm_arm_pmu_get_max_counters(kvm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_arm_set_default_pmu - No PMU set, get the default one.
|
||||||
|
* @kvm: The kvm pointer
|
||||||
|
*
|
||||||
|
* The observant among you will notice that the supported_cpus
|
||||||
|
* mask does not get updated for the default PMU even though it
|
||||||
|
* is quite possible the selected instance supports only a
|
||||||
|
* subset of cores in the system. This is intentional, and
|
||||||
|
* upholds the preexisting behavior on heterogeneous systems
|
||||||
|
* where vCPUs can be scheduled on any core but the guest
|
||||||
|
* counters could stop working.
|
||||||
|
*/
|
||||||
|
int kvm_arm_set_default_pmu(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu();
|
||||||
|
|
||||||
|
if (!arm_pmu)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
kvm_arm_set_pmu(kvm, arm_pmu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
|
static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
|
||||||
{
|
{
|
||||||
struct kvm *kvm = vcpu->kvm;
|
struct kvm *kvm = vcpu->kvm;
|
||||||
@ -893,7 +967,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm->arch.arm_pmu = arm_pmu;
|
kvm_arm_set_pmu(kvm, arm_pmu);
|
||||||
cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
|
cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
@ -916,23 +990,6 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
|||||||
if (vcpu->arch.pmu.created)
|
if (vcpu->arch.pmu.created)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
if (!kvm->arch.arm_pmu) {
|
|
||||||
/*
|
|
||||||
* No PMU set, get the default one.
|
|
||||||
*
|
|
||||||
* The observant among you will notice that the supported_cpus
|
|
||||||
* mask does not get updated for the default PMU even though it
|
|
||||||
* is quite possible the selected instance supports only a
|
|
||||||
* subset of cores in the system. This is intentional, and
|
|
||||||
* upholds the preexisting behavior on heterogeneous systems
|
|
||||||
* where vCPUs can be scheduled on any core but the guest
|
|
||||||
* counters could stop working.
|
|
||||||
*/
|
|
||||||
kvm->arch.arm_pmu = kvm_pmu_probe_armpmu();
|
|
||||||
if (!kvm->arch.arm_pmu)
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (attr->attr) {
|
switch (attr->attr) {
|
||||||
case KVM_ARM_VCPU_PMU_V3_IRQ: {
|
case KVM_ARM_VCPU_PMU_V3_IRQ: {
|
||||||
int __user *uaddr = (int __user *)(long)attr->addr;
|
int __user *uaddr = (int __user *)(long)attr->addr;
|
||||||
@ -1072,3 +1129,15 @@ u8 kvm_arm_pmu_get_pmuver_limit(void)
|
|||||||
ID_AA64DFR0_EL1_PMUVer_V3P5);
|
ID_AA64DFR0_EL1_PMUVer_V3P5);
|
||||||
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), tmp);
|
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_vcpu_read_pmcr - Read PMCR_EL0 register for the vCPU
|
||||||
|
* @vcpu: The vcpu pointer
|
||||||
|
*/
|
||||||
|
u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u64 pmcr = __vcpu_sys_reg(vcpu, PMCR_EL0) &
|
||||||
|
~(ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
|
||||||
|
|
||||||
|
return pmcr | ((u64)vcpu->kvm->arch.pmcr_n << ARMV8_PMU_PMCR_N_SHIFT);
|
||||||
|
}
|
||||||
|
@ -73,11 +73,8 @@ int __init kvm_arm_init_sve(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu)
|
static void kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (!system_supports_sve())
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
vcpu->arch.sve_max_vl = kvm_sve_max_vl;
|
vcpu->arch.sve_max_vl = kvm_sve_max_vl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -86,8 +83,6 @@ static int kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu)
|
|||||||
* kvm_arm_vcpu_finalize(), which freezes the configuration.
|
* kvm_arm_vcpu_finalize(), which freezes the configuration.
|
||||||
*/
|
*/
|
||||||
vcpu_set_flag(vcpu, GUEST_HAS_SVE);
|
vcpu_set_flag(vcpu, GUEST_HAS_SVE);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -170,20 +165,9 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
|
|||||||
memset(vcpu->arch.sve_state, 0, vcpu_sve_state_size(vcpu));
|
memset(vcpu->arch.sve_state, 0, vcpu_sve_state_size(vcpu));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
|
static void kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* For now make sure that both address/generic pointer authentication
|
|
||||||
* features are requested by the userspace together and the system
|
|
||||||
* supports these capabilities.
|
|
||||||
*/
|
|
||||||
if (!test_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, vcpu->arch.features) ||
|
|
||||||
!test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, vcpu->arch.features) ||
|
|
||||||
!system_has_full_ptr_auth())
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
vcpu_set_flag(vcpu, GUEST_HAS_PTRAUTH);
|
vcpu_set_flag(vcpu, GUEST_HAS_PTRAUTH);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,10 +188,9 @@ static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
|
|||||||
* disable preemption around the vcpu reset as we would otherwise race with
|
* disable preemption around the vcpu reset as we would otherwise race with
|
||||||
* preempt notifiers which also call put/load.
|
* preempt notifiers which also call put/load.
|
||||||
*/
|
*/
|
||||||
int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct vcpu_reset_state reset_state;
|
struct vcpu_reset_state reset_state;
|
||||||
int ret;
|
|
||||||
bool loaded;
|
bool loaded;
|
||||||
u32 pstate;
|
u32 pstate;
|
||||||
|
|
||||||
@ -224,29 +207,16 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
|||||||
if (loaded)
|
if (loaded)
|
||||||
kvm_arch_vcpu_put(vcpu);
|
kvm_arch_vcpu_put(vcpu);
|
||||||
|
|
||||||
/* Disallow NV+SVE for the time being */
|
|
||||||
if (vcpu_has_nv(vcpu) && vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!kvm_arm_vcpu_sve_finalized(vcpu)) {
|
if (!kvm_arm_vcpu_sve_finalized(vcpu)) {
|
||||||
if (test_bit(KVM_ARM_VCPU_SVE, vcpu->arch.features)) {
|
if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_SVE))
|
||||||
ret = kvm_vcpu_enable_sve(vcpu);
|
kvm_vcpu_enable_sve(vcpu);
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
kvm_vcpu_reset_sve(vcpu);
|
kvm_vcpu_reset_sve(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, vcpu->arch.features) ||
|
if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PTRAUTH_ADDRESS) ||
|
||||||
test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, vcpu->arch.features)) {
|
vcpu_has_feature(vcpu, KVM_ARM_VCPU_PTRAUTH_GENERIC))
|
||||||
if (kvm_vcpu_enable_ptrauth(vcpu)) {
|
kvm_vcpu_enable_ptrauth(vcpu);
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vcpu_el1_is_32bit(vcpu))
|
if (vcpu_el1_is_32bit(vcpu))
|
||||||
pstate = VCPU_RESET_PSTATE_SVC;
|
pstate = VCPU_RESET_PSTATE_SVC;
|
||||||
@ -255,11 +225,6 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
|||||||
else
|
else
|
||||||
pstate = VCPU_RESET_PSTATE_EL1;
|
pstate = VCPU_RESET_PSTATE_EL1;
|
||||||
|
|
||||||
if (kvm_vcpu_has_pmu(vcpu) && !kvm_arm_support_pmu_v3()) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset core registers */
|
/* Reset core registers */
|
||||||
memset(vcpu_gp_regs(vcpu), 0, sizeof(*vcpu_gp_regs(vcpu)));
|
memset(vcpu_gp_regs(vcpu), 0, sizeof(*vcpu_gp_regs(vcpu)));
|
||||||
memset(&vcpu->arch.ctxt.fp_regs, 0, sizeof(vcpu->arch.ctxt.fp_regs));
|
memset(&vcpu->arch.ctxt.fp_regs, 0, sizeof(vcpu->arch.ctxt.fp_regs));
|
||||||
@ -294,12 +259,11 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Reset timer */
|
/* Reset timer */
|
||||||
ret = kvm_timer_vcpu_reset(vcpu);
|
kvm_timer_vcpu_reset(vcpu);
|
||||||
out:
|
|
||||||
if (loaded)
|
if (loaded)
|
||||||
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 get_kvm_ipa_limit(void)
|
u32 get_kvm_ipa_limit(void)
|
||||||
|
@ -379,7 +379,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
|
|||||||
struct sys_reg_params *p,
|
struct sys_reg_params *p,
|
||||||
const struct sys_reg_desc *r)
|
const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
u64 val = IDREG(vcpu->kvm, SYS_ID_AA64MMFR1_EL1);
|
||||||
u32 sr = reg_to_encoding(r);
|
u32 sr = reg_to_encoding(r);
|
||||||
|
|
||||||
if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
|
if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
|
||||||
@ -719,14 +719,9 @@ static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
u64 n, mask = BIT(ARMV8_PMU_CYCLE_IDX);
|
u64 mask = BIT(ARMV8_PMU_CYCLE_IDX);
|
||||||
|
u8 n = vcpu->kvm->arch.pmcr_n;
|
||||||
|
|
||||||
/* No PMU available, any PMU reg may UNDEF... */
|
|
||||||
if (!kvm_arm_support_pmu_v3())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
n = read_sysreg(pmcr_el0) >> ARMV8_PMU_PMCR_N_SHIFT;
|
|
||||||
n &= ARMV8_PMU_PMCR_N_MASK;
|
|
||||||
if (n)
|
if (n)
|
||||||
mask |= GENMASK(n - 1, 0);
|
mask |= GENMASK(n - 1, 0);
|
||||||
|
|
||||||
@ -746,8 +741,12 @@ static u64 reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|||||||
|
|
||||||
static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
|
/* This thing will UNDEF, who cares about the reset value? */
|
||||||
|
if (!kvm_vcpu_has_pmu(vcpu))
|
||||||
|
return 0;
|
||||||
|
|
||||||
reset_unknown(vcpu, r);
|
reset_unknown(vcpu, r);
|
||||||
__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_EVTYPE_MASK;
|
__vcpu_sys_reg(vcpu, r->reg) &= kvm_pmu_evtyper_mask(vcpu->kvm);
|
||||||
|
|
||||||
return __vcpu_sys_reg(vcpu, r->reg);
|
return __vcpu_sys_reg(vcpu, r->reg);
|
||||||
}
|
}
|
||||||
@ -762,17 +761,15 @@ static u64 reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|||||||
|
|
||||||
static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
u64 pmcr;
|
u64 pmcr = 0;
|
||||||
|
|
||||||
/* No PMU available, PMCR_EL0 may UNDEF... */
|
|
||||||
if (!kvm_arm_support_pmu_v3())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Only preserve PMCR_EL0.N, and reset the rest to 0 */
|
|
||||||
pmcr = read_sysreg(pmcr_el0) & (ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
|
|
||||||
if (!kvm_supports_32bit_el0())
|
if (!kvm_supports_32bit_el0())
|
||||||
pmcr |= ARMV8_PMU_PMCR_LC;
|
pmcr |= ARMV8_PMU_PMCR_LC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The value of PMCR.N field is included when the
|
||||||
|
* vCPU register is read via kvm_vcpu_read_pmcr().
|
||||||
|
*/
|
||||||
__vcpu_sys_reg(vcpu, r->reg) = pmcr;
|
__vcpu_sys_reg(vcpu, r->reg) = pmcr;
|
||||||
|
|
||||||
return __vcpu_sys_reg(vcpu, r->reg);
|
return __vcpu_sys_reg(vcpu, r->reg);
|
||||||
@ -822,7 +819,7 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||||||
* Only update writeable bits of PMCR (continuing into
|
* Only update writeable bits of PMCR (continuing into
|
||||||
* kvm_pmu_handle_pmcr() as well)
|
* kvm_pmu_handle_pmcr() as well)
|
||||||
*/
|
*/
|
||||||
val = __vcpu_sys_reg(vcpu, PMCR_EL0);
|
val = kvm_vcpu_read_pmcr(vcpu);
|
||||||
val &= ~ARMV8_PMU_PMCR_MASK;
|
val &= ~ARMV8_PMU_PMCR_MASK;
|
||||||
val |= p->regval & ARMV8_PMU_PMCR_MASK;
|
val |= p->regval & ARMV8_PMU_PMCR_MASK;
|
||||||
if (!kvm_supports_32bit_el0())
|
if (!kvm_supports_32bit_el0())
|
||||||
@ -830,7 +827,7 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||||||
kvm_pmu_handle_pmcr(vcpu, val);
|
kvm_pmu_handle_pmcr(vcpu, val);
|
||||||
} else {
|
} else {
|
||||||
/* PMCR.P & PMCR.C are RAZ */
|
/* PMCR.P & PMCR.C are RAZ */
|
||||||
val = __vcpu_sys_reg(vcpu, PMCR_EL0)
|
val = kvm_vcpu_read_pmcr(vcpu)
|
||||||
& ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C);
|
& ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C);
|
||||||
p->regval = val;
|
p->regval = val;
|
||||||
}
|
}
|
||||||
@ -879,7 +876,7 @@ static bool pmu_counter_idx_valid(struct kvm_vcpu *vcpu, u64 idx)
|
|||||||
{
|
{
|
||||||
u64 pmcr, val;
|
u64 pmcr, val;
|
||||||
|
|
||||||
pmcr = __vcpu_sys_reg(vcpu, PMCR_EL0);
|
pmcr = kvm_vcpu_read_pmcr(vcpu);
|
||||||
val = (pmcr >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK;
|
val = (pmcr >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK;
|
||||||
if (idx >= val && idx != ARMV8_PMU_CYCLE_IDX) {
|
if (idx >= val && idx != ARMV8_PMU_CYCLE_IDX) {
|
||||||
kvm_inject_undefined(vcpu);
|
kvm_inject_undefined(vcpu);
|
||||||
@ -988,12 +985,45 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||||||
kvm_pmu_set_counter_event_type(vcpu, p->regval, idx);
|
kvm_pmu_set_counter_event_type(vcpu, p->regval, idx);
|
||||||
kvm_vcpu_pmu_restore_guest(vcpu);
|
kvm_vcpu_pmu_restore_guest(vcpu);
|
||||||
} else {
|
} else {
|
||||||
p->regval = __vcpu_sys_reg(vcpu, reg) & ARMV8_PMU_EVTYPE_MASK;
|
p->regval = __vcpu_sys_reg(vcpu, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 val)
|
||||||
|
{
|
||||||
|
bool set;
|
||||||
|
|
||||||
|
val &= kvm_pmu_valid_counter_mask(vcpu);
|
||||||
|
|
||||||
|
switch (r->reg) {
|
||||||
|
case PMOVSSET_EL0:
|
||||||
|
/* CRm[1] being set indicates a SET register, and CLR otherwise */
|
||||||
|
set = r->CRm & 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Op2[0] being set indicates a SET register, and CLR otherwise */
|
||||||
|
set = r->Op2 & 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
__vcpu_sys_reg(vcpu, r->reg) |= val;
|
||||||
|
else
|
||||||
|
__vcpu_sys_reg(vcpu, r->reg) &= ~val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 *val)
|
||||||
|
{
|
||||||
|
u64 mask = kvm_pmu_valid_counter_mask(vcpu);
|
||||||
|
|
||||||
|
*val = __vcpu_sys_reg(vcpu, r->reg) & mask;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||||
const struct sys_reg_desc *r)
|
const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
@ -1103,6 +1133,51 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||||
|
u64 *val)
|
||||||
|
{
|
||||||
|
*val = kvm_vcpu_read_pmcr(vcpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||||
|
u64 val)
|
||||||
|
{
|
||||||
|
u8 new_n = (val >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK;
|
||||||
|
struct kvm *kvm = vcpu->kvm;
|
||||||
|
|
||||||
|
mutex_lock(&kvm->arch.config_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The vCPU can't have more counters than the PMU hardware
|
||||||
|
* implements. Ignore this error to maintain compatibility
|
||||||
|
* with the existing KVM behavior.
|
||||||
|
*/
|
||||||
|
if (!kvm_vm_has_ran_once(kvm) &&
|
||||||
|
new_n <= kvm_arm_pmu_get_max_counters(kvm))
|
||||||
|
kvm->arch.pmcr_n = new_n;
|
||||||
|
|
||||||
|
mutex_unlock(&kvm->arch.config_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore writes to RES0 bits, read only bits that are cleared on
|
||||||
|
* vCPU reset, and writable bits that KVM doesn't support yet.
|
||||||
|
* (i.e. only PMCR.N and bits [7:0] are mutable from userspace)
|
||||||
|
* The LP bit is RES0 when FEAT_PMUv3p5 is not supported on the vCPU.
|
||||||
|
* But, we leave the bit as it is here, as the vCPU's PMUver might
|
||||||
|
* be changed later (NOTE: the bit will be cleared on first vCPU run
|
||||||
|
* if necessary).
|
||||||
|
*/
|
||||||
|
val &= ARMV8_PMU_PMCR_MASK;
|
||||||
|
|
||||||
|
/* The LC bit is RES1 when AArch32 is not supported */
|
||||||
|
if (!kvm_supports_32bit_el0())
|
||||||
|
val |= ARMV8_PMU_PMCR_LC;
|
||||||
|
|
||||||
|
__vcpu_sys_reg(vcpu, r->reg) = val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
||||||
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
||||||
{ SYS_DESC(SYS_DBGBVRn_EL1(n)), \
|
{ SYS_DESC(SYS_DBGBVRn_EL1(n)), \
|
||||||
@ -1216,8 +1291,14 @@ static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
|
|||||||
/* Some features have different safe value type in KVM than host features */
|
/* Some features have different safe value type in KVM than host features */
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SYS_ID_AA64DFR0_EL1:
|
case SYS_ID_AA64DFR0_EL1:
|
||||||
if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
|
switch (kvm_ftr.shift) {
|
||||||
|
case ID_AA64DFR0_EL1_PMUVer_SHIFT:
|
||||||
kvm_ftr.type = FTR_LOWER_SAFE;
|
kvm_ftr.type = FTR_LOWER_SAFE;
|
||||||
|
break;
|
||||||
|
case ID_AA64DFR0_EL1_DebugVer_SHIFT:
|
||||||
|
kvm_ftr.type = FTR_LOWER_SAFE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SYS_ID_DFR0_EL1:
|
case SYS_ID_DFR0_EL1:
|
||||||
if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
|
if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
|
||||||
@ -1228,7 +1309,7 @@ static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
|
|||||||
return arm64_ftr_safe_value(&kvm_ftr, new, cur);
|
return arm64_ftr_safe_value(&kvm_ftr, new, cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* arm64_check_features() - Check if a feature register value constitutes
|
* arm64_check_features() - Check if a feature register value constitutes
|
||||||
* a subset of features indicated by the idreg's KVM sanitised limit.
|
* a subset of features indicated by the idreg's KVM sanitised limit.
|
||||||
*
|
*
|
||||||
@ -1338,7 +1419,6 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
|
|||||||
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
|
ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_GPA3));
|
||||||
if (!cpus_have_final_cap(ARM64_HAS_WFXT))
|
if (!cpus_have_final_cap(ARM64_HAS_WFXT))
|
||||||
val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
|
val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
|
||||||
val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_MOPS);
|
|
||||||
break;
|
break;
|
||||||
case SYS_ID_AA64MMFR2_EL1:
|
case SYS_ID_AA64MMFR2_EL1:
|
||||||
val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
|
val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
|
||||||
@ -1373,6 +1453,13 @@ static inline bool is_id_reg(u32 id)
|
|||||||
sys_reg_CRm(id) < 8);
|
sys_reg_CRm(id) < 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_aa32_id_reg(u32 id)
|
||||||
|
{
|
||||||
|
return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
|
||||||
|
sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
|
||||||
|
sys_reg_CRm(id) <= 3);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
|
static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
|
||||||
const struct sys_reg_desc *r)
|
const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
@ -1469,14 +1556,21 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ID_REG_LIMIT_FIELD_ENUM(val, reg, field, limit) \
|
||||||
|
({ \
|
||||||
|
u64 __f_val = FIELD_GET(reg##_##field##_MASK, val); \
|
||||||
|
(val) &= ~reg##_##field##_MASK; \
|
||||||
|
(val) |= FIELD_PREP(reg##_##field##_MASK, \
|
||||||
|
min(__f_val, (u64)reg##_##field##_##limit)); \
|
||||||
|
(val); \
|
||||||
|
})
|
||||||
|
|
||||||
static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
||||||
const struct sys_reg_desc *rd)
|
const struct sys_reg_desc *rd)
|
||||||
{
|
{
|
||||||
u64 val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
|
u64 val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
|
||||||
|
|
||||||
/* Limit debug to ARMv8.0 */
|
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8);
|
||||||
val &= ~ID_AA64DFR0_EL1_DebugVer_MASK;
|
|
||||||
val |= SYS_FIELD_PREP_ENUM(ID_AA64DFR0_EL1, DebugVer, IMP);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only initialize the PMU version if the vCPU was configured with one.
|
* Only initialize the PMU version if the vCPU was configured with one.
|
||||||
@ -1496,6 +1590,7 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
const struct sys_reg_desc *rd,
|
const struct sys_reg_desc *rd,
|
||||||
u64 val)
|
u64 val)
|
||||||
{
|
{
|
||||||
|
u8 debugver = SYS_FIELD_GET(ID_AA64DFR0_EL1, DebugVer, val);
|
||||||
u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, val);
|
u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, val);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1515,6 +1610,13 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
if (pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
|
if (pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
|
||||||
val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
|
val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ID_AA64DFR0_EL1.DebugVer is one of those awkward fields with a
|
||||||
|
* nonzero minimum safe value.
|
||||||
|
*/
|
||||||
|
if (debugver < ID_AA64DFR0_EL1_DebugVer_IMP)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
return set_id_reg(vcpu, rd, val);
|
return set_id_reg(vcpu, rd, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1528,6 +1630,8 @@ static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
if (kvm_vcpu_has_pmu(vcpu))
|
if (kvm_vcpu_has_pmu(vcpu))
|
||||||
val |= SYS_FIELD_PREP(ID_DFR0_EL1, PerfMon, perfmon);
|
val |= SYS_FIELD_PREP(ID_DFR0_EL1, PerfMon, perfmon);
|
||||||
|
|
||||||
|
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_DFR0_EL1, CopDbg, Debugv8p8);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1536,6 +1640,7 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
u64 val)
|
u64 val)
|
||||||
{
|
{
|
||||||
u8 perfmon = SYS_FIELD_GET(ID_DFR0_EL1, PerfMon, val);
|
u8 perfmon = SYS_FIELD_GET(ID_DFR0_EL1, PerfMon, val);
|
||||||
|
u8 copdbg = SYS_FIELD_GET(ID_DFR0_EL1, CopDbg, val);
|
||||||
|
|
||||||
if (perfmon == ID_DFR0_EL1_PerfMon_IMPDEF) {
|
if (perfmon == ID_DFR0_EL1_PerfMon_IMPDEF) {
|
||||||
val &= ~ID_DFR0_EL1_PerfMon_MASK;
|
val &= ~ID_DFR0_EL1_PerfMon_MASK;
|
||||||
@ -1551,6 +1656,9 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
|||||||
if (perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3)
|
if (perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copdbg < ID_DFR0_EL1_CopDbg_Armv8)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
return set_id_reg(vcpu, rd, val);
|
return set_id_reg(vcpu, rd, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1791,8 +1899,8 @@ static unsigned int el2_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
* HCR_EL2.E2H==1, and only in the sysreg table for convenience of
|
* HCR_EL2.E2H==1, and only in the sysreg table for convenience of
|
||||||
* handling traps. Given that, they are always hidden from userspace.
|
* handling traps. Given that, they are always hidden from userspace.
|
||||||
*/
|
*/
|
||||||
static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
|
static unsigned int hidden_user_visibility(const struct kvm_vcpu *vcpu,
|
||||||
const struct sys_reg_desc *rd)
|
const struct sys_reg_desc *rd)
|
||||||
{
|
{
|
||||||
return REG_HIDDEN_USER;
|
return REG_HIDDEN_USER;
|
||||||
}
|
}
|
||||||
@ -1803,7 +1911,7 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
.reset = rst, \
|
.reset = rst, \
|
||||||
.reg = name##_EL1, \
|
.reg = name##_EL1, \
|
||||||
.val = v, \
|
.val = v, \
|
||||||
.visibility = elx2_visibility, \
|
.visibility = hidden_user_visibility, \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1817,11 +1925,14 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
* from userspace.
|
* from userspace.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
#define ID_DESC(name) \
|
||||||
#define ID_SANITISED(name) { \
|
|
||||||
SYS_DESC(SYS_##name), \
|
SYS_DESC(SYS_##name), \
|
||||||
.access = access_id_reg, \
|
.access = access_id_reg, \
|
||||||
.get_user = get_id_reg, \
|
.get_user = get_id_reg \
|
||||||
|
|
||||||
|
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
||||||
|
#define ID_SANITISED(name) { \
|
||||||
|
ID_DESC(name), \
|
||||||
.set_user = set_id_reg, \
|
.set_user = set_id_reg, \
|
||||||
.visibility = id_visibility, \
|
.visibility = id_visibility, \
|
||||||
.reset = kvm_read_sanitised_id_reg, \
|
.reset = kvm_read_sanitised_id_reg, \
|
||||||
@ -1830,15 +1941,22 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
||||||
#define AA32_ID_SANITISED(name) { \
|
#define AA32_ID_SANITISED(name) { \
|
||||||
SYS_DESC(SYS_##name), \
|
ID_DESC(name), \
|
||||||
.access = access_id_reg, \
|
|
||||||
.get_user = get_id_reg, \
|
|
||||||
.set_user = set_id_reg, \
|
.set_user = set_id_reg, \
|
||||||
.visibility = aa32_id_visibility, \
|
.visibility = aa32_id_visibility, \
|
||||||
.reset = kvm_read_sanitised_id_reg, \
|
.reset = kvm_read_sanitised_id_reg, \
|
||||||
.val = 0, \
|
.val = 0, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sys_reg_desc initialiser for writable ID registers */
|
||||||
|
#define ID_WRITABLE(name, mask) { \
|
||||||
|
ID_DESC(name), \
|
||||||
|
.set_user = set_id_reg, \
|
||||||
|
.visibility = id_visibility, \
|
||||||
|
.reset = kvm_read_sanitised_id_reg, \
|
||||||
|
.val = mask, \
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sys_reg_desc initialiser for architecturally unallocated cpufeature ID
|
* sys_reg_desc initialiser for architecturally unallocated cpufeature ID
|
||||||
* register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
|
* register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
|
||||||
@ -1860,9 +1978,7 @@ static unsigned int elx2_visibility(const struct kvm_vcpu *vcpu,
|
|||||||
* RAZ for the guest.
|
* RAZ for the guest.
|
||||||
*/
|
*/
|
||||||
#define ID_HIDDEN(name) { \
|
#define ID_HIDDEN(name) { \
|
||||||
SYS_DESC(SYS_##name), \
|
ID_DESC(name), \
|
||||||
.access = access_id_reg, \
|
|
||||||
.get_user = get_id_reg, \
|
|
||||||
.set_user = set_id_reg, \
|
.set_user = set_id_reg, \
|
||||||
.visibility = raz_visibility, \
|
.visibility = raz_visibility, \
|
||||||
.reset = kvm_read_sanitised_id_reg, \
|
.reset = kvm_read_sanitised_id_reg, \
|
||||||
@ -1961,7 +2077,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
// DBGDTR[TR]X_EL0 share the same encoding
|
// DBGDTR[TR]X_EL0 share the same encoding
|
||||||
{ SYS_DESC(SYS_DBGDTRTX_EL0), trap_raz_wi },
|
{ SYS_DESC(SYS_DBGDTRTX_EL0), trap_raz_wi },
|
||||||
|
|
||||||
{ SYS_DESC(SYS_DBGVCR32_EL2), NULL, reset_val, DBGVCR32_EL2, 0 },
|
{ SYS_DESC(SYS_DBGVCR32_EL2), trap_undef, reset_val, DBGVCR32_EL2, 0 },
|
||||||
|
|
||||||
{ SYS_DESC(SYS_MPIDR_EL1), NULL, reset_mpidr, MPIDR_EL1 },
|
{ SYS_DESC(SYS_MPIDR_EL1), NULL, reset_mpidr, MPIDR_EL1 },
|
||||||
|
|
||||||
@ -1980,7 +2096,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
.set_user = set_id_dfr0_el1,
|
.set_user = set_id_dfr0_el1,
|
||||||
.visibility = aa32_id_visibility,
|
.visibility = aa32_id_visibility,
|
||||||
.reset = read_sanitised_id_dfr0_el1,
|
.reset = read_sanitised_id_dfr0_el1,
|
||||||
.val = ID_DFR0_EL1_PerfMon_MASK, },
|
.val = ID_DFR0_EL1_PerfMon_MASK |
|
||||||
|
ID_DFR0_EL1_CopDbg_MASK, },
|
||||||
ID_HIDDEN(ID_AFR0_EL1),
|
ID_HIDDEN(ID_AFR0_EL1),
|
||||||
AA32_ID_SANITISED(ID_MMFR0_EL1),
|
AA32_ID_SANITISED(ID_MMFR0_EL1),
|
||||||
AA32_ID_SANITISED(ID_MMFR1_EL1),
|
AA32_ID_SANITISED(ID_MMFR1_EL1),
|
||||||
@ -2014,11 +2131,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
.get_user = get_id_reg,
|
.get_user = get_id_reg,
|
||||||
.set_user = set_id_reg,
|
.set_user = set_id_reg,
|
||||||
.reset = read_sanitised_id_aa64pfr0_el1,
|
.reset = read_sanitised_id_aa64pfr0_el1,
|
||||||
.val = ID_AA64PFR0_EL1_CSV2_MASK | ID_AA64PFR0_EL1_CSV3_MASK, },
|
.val = ~(ID_AA64PFR0_EL1_AMU |
|
||||||
|
ID_AA64PFR0_EL1_MPAM |
|
||||||
|
ID_AA64PFR0_EL1_SVE |
|
||||||
|
ID_AA64PFR0_EL1_RAS |
|
||||||
|
ID_AA64PFR0_EL1_GIC |
|
||||||
|
ID_AA64PFR0_EL1_AdvSIMD |
|
||||||
|
ID_AA64PFR0_EL1_FP), },
|
||||||
ID_SANITISED(ID_AA64PFR1_EL1),
|
ID_SANITISED(ID_AA64PFR1_EL1),
|
||||||
ID_UNALLOCATED(4,2),
|
ID_UNALLOCATED(4,2),
|
||||||
ID_UNALLOCATED(4,3),
|
ID_UNALLOCATED(4,3),
|
||||||
ID_SANITISED(ID_AA64ZFR0_EL1),
|
ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0),
|
||||||
ID_HIDDEN(ID_AA64SMFR0_EL1),
|
ID_HIDDEN(ID_AA64SMFR0_EL1),
|
||||||
ID_UNALLOCATED(4,6),
|
ID_UNALLOCATED(4,6),
|
||||||
ID_UNALLOCATED(4,7),
|
ID_UNALLOCATED(4,7),
|
||||||
@ -2029,7 +2152,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
.get_user = get_id_reg,
|
.get_user = get_id_reg,
|
||||||
.set_user = set_id_aa64dfr0_el1,
|
.set_user = set_id_aa64dfr0_el1,
|
||||||
.reset = read_sanitised_id_aa64dfr0_el1,
|
.reset = read_sanitised_id_aa64dfr0_el1,
|
||||||
.val = ID_AA64DFR0_EL1_PMUVer_MASK, },
|
.val = ID_AA64DFR0_EL1_PMUVer_MASK |
|
||||||
|
ID_AA64DFR0_EL1_DebugVer_MASK, },
|
||||||
ID_SANITISED(ID_AA64DFR1_EL1),
|
ID_SANITISED(ID_AA64DFR1_EL1),
|
||||||
ID_UNALLOCATED(5,2),
|
ID_UNALLOCATED(5,2),
|
||||||
ID_UNALLOCATED(5,3),
|
ID_UNALLOCATED(5,3),
|
||||||
@ -2039,9 +2163,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
ID_UNALLOCATED(5,7),
|
ID_UNALLOCATED(5,7),
|
||||||
|
|
||||||
/* CRm=6 */
|
/* CRm=6 */
|
||||||
ID_SANITISED(ID_AA64ISAR0_EL1),
|
ID_WRITABLE(ID_AA64ISAR0_EL1, ~ID_AA64ISAR0_EL1_RES0),
|
||||||
ID_SANITISED(ID_AA64ISAR1_EL1),
|
ID_WRITABLE(ID_AA64ISAR1_EL1, ~(ID_AA64ISAR1_EL1_GPI |
|
||||||
ID_SANITISED(ID_AA64ISAR2_EL1),
|
ID_AA64ISAR1_EL1_GPA |
|
||||||
|
ID_AA64ISAR1_EL1_API |
|
||||||
|
ID_AA64ISAR1_EL1_APA)),
|
||||||
|
ID_WRITABLE(ID_AA64ISAR2_EL1, ~(ID_AA64ISAR2_EL1_RES0 |
|
||||||
|
ID_AA64ISAR2_EL1_APA3 |
|
||||||
|
ID_AA64ISAR2_EL1_GPA3)),
|
||||||
ID_UNALLOCATED(6,3),
|
ID_UNALLOCATED(6,3),
|
||||||
ID_UNALLOCATED(6,4),
|
ID_UNALLOCATED(6,4),
|
||||||
ID_UNALLOCATED(6,5),
|
ID_UNALLOCATED(6,5),
|
||||||
@ -2049,9 +2178,23 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
ID_UNALLOCATED(6,7),
|
ID_UNALLOCATED(6,7),
|
||||||
|
|
||||||
/* CRm=7 */
|
/* CRm=7 */
|
||||||
ID_SANITISED(ID_AA64MMFR0_EL1),
|
ID_WRITABLE(ID_AA64MMFR0_EL1, ~(ID_AA64MMFR0_EL1_RES0 |
|
||||||
ID_SANITISED(ID_AA64MMFR1_EL1),
|
ID_AA64MMFR0_EL1_TGRAN4_2 |
|
||||||
ID_SANITISED(ID_AA64MMFR2_EL1),
|
ID_AA64MMFR0_EL1_TGRAN64_2 |
|
||||||
|
ID_AA64MMFR0_EL1_TGRAN16_2)),
|
||||||
|
ID_WRITABLE(ID_AA64MMFR1_EL1, ~(ID_AA64MMFR1_EL1_RES0 |
|
||||||
|
ID_AA64MMFR1_EL1_HCX |
|
||||||
|
ID_AA64MMFR1_EL1_XNX |
|
||||||
|
ID_AA64MMFR1_EL1_TWED |
|
||||||
|
ID_AA64MMFR1_EL1_XNX |
|
||||||
|
ID_AA64MMFR1_EL1_VH |
|
||||||
|
ID_AA64MMFR1_EL1_VMIDBits)),
|
||||||
|
ID_WRITABLE(ID_AA64MMFR2_EL1, ~(ID_AA64MMFR2_EL1_RES0 |
|
||||||
|
ID_AA64MMFR2_EL1_EVT |
|
||||||
|
ID_AA64MMFR2_EL1_FWB |
|
||||||
|
ID_AA64MMFR2_EL1_IDS |
|
||||||
|
ID_AA64MMFR2_EL1_NV |
|
||||||
|
ID_AA64MMFR2_EL1_CCIDX)),
|
||||||
ID_SANITISED(ID_AA64MMFR3_EL1),
|
ID_SANITISED(ID_AA64MMFR3_EL1),
|
||||||
ID_UNALLOCATED(7,4),
|
ID_UNALLOCATED(7,4),
|
||||||
ID_UNALLOCATED(7,5),
|
ID_UNALLOCATED(7,5),
|
||||||
@ -2116,9 +2259,11 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
/* PMBIDR_EL1 is not trapped */
|
/* PMBIDR_EL1 is not trapped */
|
||||||
|
|
||||||
{ PMU_SYS_REG(PMINTENSET_EL1),
|
{ PMU_SYS_REG(PMINTENSET_EL1),
|
||||||
.access = access_pminten, .reg = PMINTENSET_EL1 },
|
.access = access_pminten, .reg = PMINTENSET_EL1,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
{ PMU_SYS_REG(PMINTENCLR_EL1),
|
{ PMU_SYS_REG(PMINTENCLR_EL1),
|
||||||
.access = access_pminten, .reg = PMINTENSET_EL1 },
|
.access = access_pminten, .reg = PMINTENSET_EL1,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
{ SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
|
{ SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
|
||||||
|
|
||||||
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
|
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
|
||||||
@ -2166,14 +2311,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
{ SYS_DESC(SYS_CTR_EL0), access_ctr },
|
{ SYS_DESC(SYS_CTR_EL0), access_ctr },
|
||||||
{ SYS_DESC(SYS_SVCR), undef_access },
|
{ SYS_DESC(SYS_SVCR), undef_access },
|
||||||
|
|
||||||
{ PMU_SYS_REG(PMCR_EL0), .access = access_pmcr,
|
{ PMU_SYS_REG(PMCR_EL0), .access = access_pmcr, .reset = reset_pmcr,
|
||||||
.reset = reset_pmcr, .reg = PMCR_EL0 },
|
.reg = PMCR_EL0, .get_user = get_pmcr, .set_user = set_pmcr },
|
||||||
{ PMU_SYS_REG(PMCNTENSET_EL0),
|
{ PMU_SYS_REG(PMCNTENSET_EL0),
|
||||||
.access = access_pmcnten, .reg = PMCNTENSET_EL0 },
|
.access = access_pmcnten, .reg = PMCNTENSET_EL0,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
{ PMU_SYS_REG(PMCNTENCLR_EL0),
|
{ PMU_SYS_REG(PMCNTENCLR_EL0),
|
||||||
.access = access_pmcnten, .reg = PMCNTENSET_EL0 },
|
.access = access_pmcnten, .reg = PMCNTENSET_EL0,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
{ PMU_SYS_REG(PMOVSCLR_EL0),
|
{ PMU_SYS_REG(PMOVSCLR_EL0),
|
||||||
.access = access_pmovs, .reg = PMOVSSET_EL0 },
|
.access = access_pmovs, .reg = PMOVSSET_EL0,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
/*
|
/*
|
||||||
* PM_SWINC_EL0 is exposed to userspace as RAZ/WI, as it was
|
* PM_SWINC_EL0 is exposed to userspace as RAZ/WI, as it was
|
||||||
* previously (and pointlessly) advertised in the past...
|
* previously (and pointlessly) advertised in the past...
|
||||||
@ -2201,7 +2349,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
{ PMU_SYS_REG(PMUSERENR_EL0), .access = access_pmuserenr,
|
{ PMU_SYS_REG(PMUSERENR_EL0), .access = access_pmuserenr,
|
||||||
.reset = reset_val, .reg = PMUSERENR_EL0, .val = 0 },
|
.reset = reset_val, .reg = PMUSERENR_EL0, .val = 0 },
|
||||||
{ PMU_SYS_REG(PMOVSSET_EL0),
|
{ PMU_SYS_REG(PMOVSSET_EL0),
|
||||||
.access = access_pmovs, .reg = PMOVSSET_EL0 },
|
.access = access_pmovs, .reg = PMOVSSET_EL0,
|
||||||
|
.get_user = get_pmreg, .set_user = set_pmreg },
|
||||||
|
|
||||||
{ SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
|
{ SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
|
||||||
{ SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
|
{ SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
|
||||||
@ -2380,18 +2529,28 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||||||
EL2_REG(VTTBR_EL2, access_rw, reset_val, 0),
|
EL2_REG(VTTBR_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(VTCR_EL2, access_rw, reset_val, 0),
|
EL2_REG(VTCR_EL2, access_rw, reset_val, 0),
|
||||||
|
|
||||||
{ SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 },
|
{ SYS_DESC(SYS_DACR32_EL2), trap_undef, reset_unknown, DACR32_EL2 },
|
||||||
EL2_REG(HDFGRTR_EL2, access_rw, reset_val, 0),
|
EL2_REG(HDFGRTR_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(HDFGWTR_EL2, access_rw, reset_val, 0),
|
EL2_REG(HDFGWTR_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(SPSR_EL2, access_rw, reset_val, 0),
|
EL2_REG(SPSR_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(ELR_EL2, access_rw, reset_val, 0),
|
EL2_REG(ELR_EL2, access_rw, reset_val, 0),
|
||||||
{ SYS_DESC(SYS_SP_EL1), access_sp_el1},
|
{ SYS_DESC(SYS_SP_EL1), access_sp_el1},
|
||||||
|
|
||||||
{ SYS_DESC(SYS_IFSR32_EL2), NULL, reset_unknown, IFSR32_EL2 },
|
/* AArch32 SPSR_* are RES0 if trapped from a NV guest */
|
||||||
|
{ SYS_DESC(SYS_SPSR_irq), .access = trap_raz_wi,
|
||||||
|
.visibility = hidden_user_visibility },
|
||||||
|
{ SYS_DESC(SYS_SPSR_abt), .access = trap_raz_wi,
|
||||||
|
.visibility = hidden_user_visibility },
|
||||||
|
{ SYS_DESC(SYS_SPSR_und), .access = trap_raz_wi,
|
||||||
|
.visibility = hidden_user_visibility },
|
||||||
|
{ SYS_DESC(SYS_SPSR_fiq), .access = trap_raz_wi,
|
||||||
|
.visibility = hidden_user_visibility },
|
||||||
|
|
||||||
|
{ SYS_DESC(SYS_IFSR32_EL2), trap_undef, reset_unknown, IFSR32_EL2 },
|
||||||
EL2_REG(AFSR0_EL2, access_rw, reset_val, 0),
|
EL2_REG(AFSR0_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(AFSR1_EL2, access_rw, reset_val, 0),
|
EL2_REG(AFSR1_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(ESR_EL2, access_rw, reset_val, 0),
|
EL2_REG(ESR_EL2, access_rw, reset_val, 0),
|
||||||
{ SYS_DESC(SYS_FPEXC32_EL2), NULL, reset_val, FPEXC32_EL2, 0x700 },
|
{ SYS_DESC(SYS_FPEXC32_EL2), trap_undef, reset_val, FPEXC32_EL2, 0x700 },
|
||||||
|
|
||||||
EL2_REG(FAR_EL2, access_rw, reset_val, 0),
|
EL2_REG(FAR_EL2, access_rw, reset_val, 0),
|
||||||
EL2_REG(HPFAR_EL2, access_rw, reset_val, 0),
|
EL2_REG(HPFAR_EL2, access_rw, reset_val, 0),
|
||||||
@ -2438,14 +2597,15 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
|
|||||||
if (p->is_write) {
|
if (p->is_write) {
|
||||||
return ignore_write(vcpu, p);
|
return ignore_write(vcpu, p);
|
||||||
} else {
|
} else {
|
||||||
u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
|
u64 dfr = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
|
||||||
u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
|
u64 pfr = IDREG(vcpu->kvm, SYS_ID_AA64PFR0_EL1);
|
||||||
u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL1_EL3_SHIFT);
|
u32 el3 = !!SYS_FIELD_GET(ID_AA64PFR0_EL1, EL3, pfr);
|
||||||
|
|
||||||
p->regval = ((((dfr >> ID_AA64DFR0_EL1_WRPs_SHIFT) & 0xf) << 28) |
|
p->regval = ((SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr) << 28) |
|
||||||
(((dfr >> ID_AA64DFR0_EL1_BRPs_SHIFT) & 0xf) << 24) |
|
(SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr) << 24) |
|
||||||
(((dfr >> ID_AA64DFR0_EL1_CTX_CMPs_SHIFT) & 0xf) << 20)
|
(SYS_FIELD_GET(ID_AA64DFR0_EL1, CTX_CMPs, dfr) << 20) |
|
||||||
| (6 << 16) | (1 << 15) | (el3 << 14) | (el3 << 12));
|
(SYS_FIELD_GET(ID_AA64DFR0_EL1, DebugVer, dfr) << 16) |
|
||||||
|
(1 << 15) | (el3 << 14) | (el3 << 12));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3572,6 +3732,65 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
|||||||
return write_demux_regids(uindices);
|
return write_demux_regids(uindices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define KVM_ARM_FEATURE_ID_RANGE_INDEX(r) \
|
||||||
|
KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(r), \
|
||||||
|
sys_reg_Op1(r), \
|
||||||
|
sys_reg_CRn(r), \
|
||||||
|
sys_reg_CRm(r), \
|
||||||
|
sys_reg_Op2(r))
|
||||||
|
|
||||||
|
static bool is_feature_id_reg(u32 encoding)
|
||||||
|
{
|
||||||
|
return (sys_reg_Op0(encoding) == 3 &&
|
||||||
|
(sys_reg_Op1(encoding) < 2 || sys_reg_Op1(encoding) == 3) &&
|
||||||
|
sys_reg_CRn(encoding) == 0 &&
|
||||||
|
sys_reg_CRm(encoding) <= 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *range)
|
||||||
|
{
|
||||||
|
const void *zero_page = page_to_virt(ZERO_PAGE(0));
|
||||||
|
u64 __user *masks = (u64 __user *)range->addr;
|
||||||
|
|
||||||
|
/* Only feature id range is supported, reserved[13] must be zero. */
|
||||||
|
if (range->range ||
|
||||||
|
memcmp(range->reserved, zero_page, sizeof(range->reserved)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Wipe the whole thing first */
|
||||||
|
if (clear_user(masks, KVM_ARM_FEATURE_ID_RANGE_SIZE * sizeof(__u64)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
|
||||||
|
const struct sys_reg_desc *reg = &sys_reg_descs[i];
|
||||||
|
u32 encoding = reg_to_encoding(reg);
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
if (!is_feature_id_reg(encoding) || !reg->set_user)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For ID registers, we return the writable mask. Other feature
|
||||||
|
* registers return a full 64bit mask. That's not necessary
|
||||||
|
* compliant with a given revision of the architecture, but the
|
||||||
|
* RES0/RES1 definitions allow us to do that.
|
||||||
|
*/
|
||||||
|
if (is_id_reg(encoding)) {
|
||||||
|
if (!reg->val ||
|
||||||
|
(is_aa32_id_reg(encoding) && !kvm_supports_32bit_el0()))
|
||||||
|
continue;
|
||||||
|
val = reg->val;
|
||||||
|
} else {
|
||||||
|
val = ~0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (put_user(val, (masks + KVM_ARM_FEATURE_ID_RANGE_INDEX(encoding))))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int __init kvm_sys_reg_table_init(void)
|
int __init kvm_sys_reg_table_init(void)
|
||||||
{
|
{
|
||||||
struct sys_reg_params params;
|
struct sys_reg_params params;
|
||||||
|
@ -136,6 +136,31 @@ TRACE_EVENT(kvm_mmio_emulate,
|
|||||||
__entry->vcpu_pc, __entry->instr, __entry->cpsr)
|
__entry->vcpu_pc, __entry->instr, __entry->cpsr)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(kvm_mmio_nisv,
|
||||||
|
TP_PROTO(unsigned long vcpu_pc, unsigned long esr,
|
||||||
|
unsigned long far, unsigned long ipa),
|
||||||
|
TP_ARGS(vcpu_pc, esr, far, ipa),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field( unsigned long, vcpu_pc )
|
||||||
|
__field( unsigned long, esr )
|
||||||
|
__field( unsigned long, far )
|
||||||
|
__field( unsigned long, ipa )
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->vcpu_pc = vcpu_pc;
|
||||||
|
__entry->esr = esr;
|
||||||
|
__entry->far = far;
|
||||||
|
__entry->ipa = ipa;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("ipa %#016lx, esr %#016lx, far %#016lx, pc %#016lx",
|
||||||
|
__entry->ipa, __entry->esr,
|
||||||
|
__entry->far, __entry->vcpu_pc)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
TRACE_EVENT(kvm_set_way_flush,
|
TRACE_EVENT(kvm_set_way_flush,
|
||||||
TP_PROTO(unsigned long vcpu_pc, bool cache),
|
TP_PROTO(unsigned long vcpu_pc, bool cache),
|
||||||
TP_ARGS(vcpu_pc, cache),
|
TP_ARGS(vcpu_pc, cache),
|
||||||
|
@ -166,7 +166,7 @@ static void print_header(struct seq_file *s, struct vgic_irq *irq,
|
|||||||
|
|
||||||
if (vcpu) {
|
if (vcpu) {
|
||||||
hdr = "VCPU";
|
hdr = "VCPU";
|
||||||
id = vcpu->vcpu_id;
|
id = vcpu->vcpu_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
seq_printf(s, "\n");
|
seq_printf(s, "\n");
|
||||||
@ -212,7 +212,7 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
|
|||||||
" %2d "
|
" %2d "
|
||||||
"\n",
|
"\n",
|
||||||
type, irq->intid,
|
type, irq->intid,
|
||||||
(irq->target_vcpu) ? irq->target_vcpu->vcpu_id : -1,
|
(irq->target_vcpu) ? irq->target_vcpu->vcpu_idx : -1,
|
||||||
pending,
|
pending,
|
||||||
irq->line_level,
|
irq->line_level,
|
||||||
irq->active,
|
irq->active,
|
||||||
@ -224,7 +224,7 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
|
|||||||
irq->mpidr,
|
irq->mpidr,
|
||||||
irq->source,
|
irq->source,
|
||||||
irq->priority,
|
irq->priority,
|
||||||
(irq->vcpu) ? irq->vcpu->vcpu_id : -1);
|
(irq->vcpu) ? irq->vcpu->vcpu_idx : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vgic_debug_show(struct seq_file *s, void *v)
|
static int vgic_debug_show(struct seq_file *s, void *v)
|
||||||
|
@ -23,7 +23,7 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
|||||||
|
|
||||||
if (!vgic_valid_spi(kvm, spi_id))
|
if (!vgic_valid_spi(kvm, spi_id))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return kvm_vgic_inject_irq(kvm, 0, spi_id, level, NULL);
|
return kvm_vgic_inject_irq(kvm, NULL, spi_id, level, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -378,6 +378,12 @@ static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct kvm_vcpu *collection_to_vcpu(struct kvm *kvm,
|
||||||
|
struct its_collection *col)
|
||||||
|
{
|
||||||
|
return kvm_get_vcpu_by_id(kvm, col->target_addr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
|
* Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
|
||||||
* is targeting) to the VGIC's view, which deals with target VCPUs.
|
* is targeting) to the VGIC's view, which deals with target VCPUs.
|
||||||
@ -391,7 +397,7 @@ static void update_affinity_ite(struct kvm *kvm, struct its_ite *ite)
|
|||||||
if (!its_is_collection_mapped(ite->collection))
|
if (!its_is_collection_mapped(ite->collection))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, ite->collection->target_addr);
|
vcpu = collection_to_vcpu(kvm, ite->collection);
|
||||||
update_affinity(ite->irq, vcpu);
|
update_affinity(ite->irq, vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +685,7 @@ int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
|
|||||||
if (!ite || !its_is_collection_mapped(ite->collection))
|
if (!ite || !its_is_collection_mapped(ite->collection))
|
||||||
return E_ITS_INT_UNMAPPED_INTERRUPT;
|
return E_ITS_INT_UNMAPPED_INTERRUPT;
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, ite->collection->target_addr);
|
vcpu = collection_to_vcpu(kvm, ite->collection);
|
||||||
if (!vcpu)
|
if (!vcpu)
|
||||||
return E_ITS_INT_UNMAPPED_INTERRUPT;
|
return E_ITS_INT_UNMAPPED_INTERRUPT;
|
||||||
|
|
||||||
@ -887,7 +893,7 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
|
|||||||
return E_ITS_MOVI_UNMAPPED_COLLECTION;
|
return E_ITS_MOVI_UNMAPPED_COLLECTION;
|
||||||
|
|
||||||
ite->collection = collection;
|
ite->collection = collection;
|
||||||
vcpu = kvm_get_vcpu(kvm, collection->target_addr);
|
vcpu = collection_to_vcpu(kvm, collection);
|
||||||
|
|
||||||
vgic_its_invalidate_cache(kvm);
|
vgic_its_invalidate_cache(kvm);
|
||||||
|
|
||||||
@ -1121,7 +1127,7 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (its_is_collection_mapped(collection))
|
if (its_is_collection_mapped(collection))
|
||||||
vcpu = kvm_get_vcpu(kvm, collection->target_addr);
|
vcpu = collection_to_vcpu(kvm, collection);
|
||||||
|
|
||||||
irq = vgic_add_lpi(kvm, lpi_nr, vcpu);
|
irq = vgic_add_lpi(kvm, lpi_nr, vcpu);
|
||||||
if (IS_ERR(irq)) {
|
if (IS_ERR(irq)) {
|
||||||
@ -1242,21 +1248,22 @@ static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
|
|||||||
u64 *its_cmd)
|
u64 *its_cmd)
|
||||||
{
|
{
|
||||||
u16 coll_id;
|
u16 coll_id;
|
||||||
u32 target_addr;
|
|
||||||
struct its_collection *collection;
|
struct its_collection *collection;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
valid = its_cmd_get_validbit(its_cmd);
|
valid = its_cmd_get_validbit(its_cmd);
|
||||||
coll_id = its_cmd_get_collection(its_cmd);
|
coll_id = its_cmd_get_collection(its_cmd);
|
||||||
target_addr = its_cmd_get_target_addr(its_cmd);
|
|
||||||
|
|
||||||
if (target_addr >= atomic_read(&kvm->online_vcpus))
|
|
||||||
return E_ITS_MAPC_PROCNUM_OOR;
|
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
vgic_its_free_collection(its, coll_id);
|
vgic_its_free_collection(its, coll_id);
|
||||||
vgic_its_invalidate_cache(kvm);
|
vgic_its_invalidate_cache(kvm);
|
||||||
} else {
|
} else {
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
vcpu = kvm_get_vcpu_by_id(kvm, its_cmd_get_target_addr(its_cmd));
|
||||||
|
if (!vcpu)
|
||||||
|
return E_ITS_MAPC_PROCNUM_OOR;
|
||||||
|
|
||||||
collection = find_collection(its, coll_id);
|
collection = find_collection(its, coll_id);
|
||||||
|
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
@ -1270,9 +1277,9 @@ static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
|
|||||||
coll_id);
|
coll_id);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
collection->target_addr = target_addr;
|
collection->target_addr = vcpu->vcpu_id;
|
||||||
} else {
|
} else {
|
||||||
collection->target_addr = target_addr;
|
collection->target_addr = vcpu->vcpu_id;
|
||||||
update_affinity_collection(kvm, its, collection);
|
update_affinity_collection(kvm, its, collection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1382,7 +1389,7 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
|
|||||||
if (!its_is_collection_mapped(collection))
|
if (!its_is_collection_mapped(collection))
|
||||||
return E_ITS_INVALL_UNMAPPED_COLLECTION;
|
return E_ITS_INVALL_UNMAPPED_COLLECTION;
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, collection->target_addr);
|
vcpu = collection_to_vcpu(kvm, collection);
|
||||||
vgic_its_invall(vcpu);
|
vgic_its_invall(vcpu);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1399,23 +1406,21 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
|
|||||||
static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
||||||
u64 *its_cmd)
|
u64 *its_cmd)
|
||||||
{
|
{
|
||||||
u32 target1_addr = its_cmd_get_target_addr(its_cmd);
|
|
||||||
u32 target2_addr = its_cmd_mask_field(its_cmd, 3, 16, 32);
|
|
||||||
struct kvm_vcpu *vcpu1, *vcpu2;
|
struct kvm_vcpu *vcpu1, *vcpu2;
|
||||||
struct vgic_irq *irq;
|
struct vgic_irq *irq;
|
||||||
u32 *intids;
|
u32 *intids;
|
||||||
int irq_count, i;
|
int irq_count, i;
|
||||||
|
|
||||||
if (target1_addr >= atomic_read(&kvm->online_vcpus) ||
|
/* We advertise GITS_TYPER.PTA==0, making the address the vcpu ID */
|
||||||
target2_addr >= atomic_read(&kvm->online_vcpus))
|
vcpu1 = kvm_get_vcpu_by_id(kvm, its_cmd_get_target_addr(its_cmd));
|
||||||
|
vcpu2 = kvm_get_vcpu_by_id(kvm, its_cmd_mask_field(its_cmd, 3, 16, 32));
|
||||||
|
|
||||||
|
if (!vcpu1 || !vcpu2)
|
||||||
return E_ITS_MOVALL_PROCNUM_OOR;
|
return E_ITS_MOVALL_PROCNUM_OOR;
|
||||||
|
|
||||||
if (target1_addr == target2_addr)
|
if (vcpu1 == vcpu2)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
vcpu1 = kvm_get_vcpu(kvm, target1_addr);
|
|
||||||
vcpu2 = kvm_get_vcpu(kvm, target2_addr);
|
|
||||||
|
|
||||||
irq_count = vgic_copy_lpi_list(kvm, vcpu1, &intids);
|
irq_count = vgic_copy_lpi_list(kvm, vcpu1, &intids);
|
||||||
if (irq_count < 0)
|
if (irq_count < 0)
|
||||||
return irq_count;
|
return irq_count;
|
||||||
@ -2258,7 +2263,7 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id,
|
|||||||
return PTR_ERR(ite);
|
return PTR_ERR(ite);
|
||||||
|
|
||||||
if (its_is_collection_mapped(collection))
|
if (its_is_collection_mapped(collection))
|
||||||
vcpu = kvm_get_vcpu(kvm, collection->target_addr);
|
vcpu = kvm_get_vcpu_by_id(kvm, collection->target_addr);
|
||||||
|
|
||||||
irq = vgic_add_lpi(kvm, lpi_id, vcpu);
|
irq = vgic_add_lpi(kvm, lpi_id, vcpu);
|
||||||
if (IS_ERR(irq)) {
|
if (IS_ERR(irq)) {
|
||||||
@ -2573,7 +2578,7 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
|
|||||||
coll_id = val & KVM_ITS_CTE_ICID_MASK;
|
coll_id = val & KVM_ITS_CTE_ICID_MASK;
|
||||||
|
|
||||||
if (target_addr != COLLECTION_NOT_MAPPED &&
|
if (target_addr != COLLECTION_NOT_MAPPED &&
|
||||||
target_addr >= atomic_read(&kvm->online_vcpus))
|
!kvm_get_vcpu_by_id(kvm, target_addr))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
collection = find_collection(its, coll_id);
|
collection = find_collection(its, coll_id);
|
||||||
|
@ -27,7 +27,8 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
|
|||||||
if (addr + size < addr)
|
if (addr + size < addr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (addr & ~kvm_phys_mask(kvm) || addr + size > kvm_phys_size(kvm))
|
if (addr & ~kvm_phys_mask(&kvm->arch.mmu) ||
|
||||||
|
(addr + size) > kvm_phys_size(&kvm->arch.mmu))
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -339,13 +340,9 @@ int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
|||||||
{
|
{
|
||||||
int cpuid;
|
int cpuid;
|
||||||
|
|
||||||
cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
|
cpuid = FIELD_GET(KVM_DEV_ARM_VGIC_CPUID_MASK, attr->attr);
|
||||||
KVM_DEV_ARM_VGIC_CPUID_SHIFT;
|
|
||||||
|
|
||||||
if (cpuid >= atomic_read(&dev->kvm->online_vcpus))
|
reg_attr->vcpu = kvm_get_vcpu_by_id(dev->kvm, cpuid);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
reg_attr->vcpu = kvm_get_vcpu(dev->kvm, cpuid);
|
|
||||||
reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
reg_attr->addr = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1013,35 +1013,6 @@ int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Compare a given affinity (level 1-3 and a level 0 mask, from the SGI
|
|
||||||
* generation register ICC_SGI1R_EL1) with a given VCPU.
|
|
||||||
* If the VCPU's MPIDR matches, return the level0 affinity, otherwise
|
|
||||||
* return -1.
|
|
||||||
*/
|
|
||||||
static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
|
|
||||||
{
|
|
||||||
unsigned long affinity;
|
|
||||||
int level0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Split the current VCPU's MPIDR into affinity level 0 and the
|
|
||||||
* rest as this is what we have to compare against.
|
|
||||||
*/
|
|
||||||
affinity = kvm_vcpu_get_mpidr_aff(vcpu);
|
|
||||||
level0 = MPIDR_AFFINITY_LEVEL(affinity, 0);
|
|
||||||
affinity &= ~MPIDR_LEVEL_MASK;
|
|
||||||
|
|
||||||
/* bail out if the upper three levels don't match */
|
|
||||||
if (sgi_aff != affinity)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Is this VCPU's bit set in the mask ? */
|
|
||||||
if (!(sgi_cpu_mask & BIT(level0)))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return level0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The ICC_SGI* registers encode the affinity differently from the MPIDR,
|
* The ICC_SGI* registers encode the affinity differently from the MPIDR,
|
||||||
@ -1052,6 +1023,38 @@ static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
|
|||||||
((((reg) & ICC_SGI1R_AFFINITY_## level ##_MASK) \
|
((((reg) & ICC_SGI1R_AFFINITY_## level ##_MASK) \
|
||||||
>> ICC_SGI1R_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level))
|
>> ICC_SGI1R_AFFINITY_## level ##_SHIFT) << MPIDR_LEVEL_SHIFT(level))
|
||||||
|
|
||||||
|
static void vgic_v3_queue_sgi(struct kvm_vcpu *vcpu, u32 sgi, bool allow_group1)
|
||||||
|
{
|
||||||
|
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, sgi);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An access targeting Group0 SGIs can only generate
|
||||||
|
* those, while an access targeting Group1 SGIs can
|
||||||
|
* generate interrupts of either group.
|
||||||
|
*/
|
||||||
|
if (!irq->group || allow_group1) {
|
||||||
|
if (!irq->hw) {
|
||||||
|
irq->pending_latch = true;
|
||||||
|
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
||||||
|
} else {
|
||||||
|
/* HW SGI? Ask the GIC to inject it */
|
||||||
|
int err;
|
||||||
|
err = irq_set_irqchip_state(irq->host_irq,
|
||||||
|
IRQCHIP_STATE_PENDING,
|
||||||
|
true);
|
||||||
|
WARN_RATELIMIT(err, "IRQ %d", irq->host_irq);
|
||||||
|
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
vgic_put_irq(vcpu->kvm, irq);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vgic_v3_dispatch_sgi - handle SGI requests from VCPUs
|
* vgic_v3_dispatch_sgi - handle SGI requests from VCPUs
|
||||||
* @vcpu: The VCPU requesting a SGI
|
* @vcpu: The VCPU requesting a SGI
|
||||||
@ -1062,83 +1065,46 @@ static int match_mpidr(u64 sgi_aff, u16 sgi_cpu_mask, struct kvm_vcpu *vcpu)
|
|||||||
* This will trap in sys_regs.c and call this function.
|
* This will trap in sys_regs.c and call this function.
|
||||||
* This ICC_SGI1R_EL1 register contains the upper three affinity levels of the
|
* This ICC_SGI1R_EL1 register contains the upper three affinity levels of the
|
||||||
* target processors as well as a bitmask of 16 Aff0 CPUs.
|
* target processors as well as a bitmask of 16 Aff0 CPUs.
|
||||||
* If the interrupt routing mode bit is not set, we iterate over all VCPUs to
|
*
|
||||||
* check for matching ones. If this bit is set, we signal all, but not the
|
* If the interrupt routing mode bit is not set, we iterate over the Aff0
|
||||||
* calling VCPU.
|
* bits and signal the VCPUs matching the provided Aff{3,2,1}.
|
||||||
|
*
|
||||||
|
* If this bit is set, we signal all, but not the calling VCPU.
|
||||||
*/
|
*/
|
||||||
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
|
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1)
|
||||||
{
|
{
|
||||||
struct kvm *kvm = vcpu->kvm;
|
struct kvm *kvm = vcpu->kvm;
|
||||||
struct kvm_vcpu *c_vcpu;
|
struct kvm_vcpu *c_vcpu;
|
||||||
u16 target_cpus;
|
unsigned long target_cpus;
|
||||||
u64 mpidr;
|
u64 mpidr;
|
||||||
int sgi;
|
u32 sgi, aff0;
|
||||||
int vcpu_id = vcpu->vcpu_id;
|
unsigned long c;
|
||||||
bool broadcast;
|
|
||||||
unsigned long c, flags;
|
|
||||||
|
|
||||||
sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT;
|
sgi = FIELD_GET(ICC_SGI1R_SGI_ID_MASK, reg);
|
||||||
broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
|
|
||||||
target_cpus = (reg & ICC_SGI1R_TARGET_LIST_MASK) >> ICC_SGI1R_TARGET_LIST_SHIFT;
|
/* Broadcast */
|
||||||
|
if (unlikely(reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT))) {
|
||||||
|
kvm_for_each_vcpu(c, c_vcpu, kvm) {
|
||||||
|
/* Don't signal the calling VCPU */
|
||||||
|
if (c_vcpu == vcpu)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We iterate over affinities to find the corresponding vcpus */
|
||||||
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
|
mpidr = SGI_AFFINITY_LEVEL(reg, 3);
|
||||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
|
mpidr |= SGI_AFFINITY_LEVEL(reg, 2);
|
||||||
mpidr |= SGI_AFFINITY_LEVEL(reg, 1);
|
mpidr |= SGI_AFFINITY_LEVEL(reg, 1);
|
||||||
|
target_cpus = FIELD_GET(ICC_SGI1R_TARGET_LIST_MASK, reg);
|
||||||
|
|
||||||
/*
|
for_each_set_bit(aff0, &target_cpus, hweight_long(ICC_SGI1R_TARGET_LIST_MASK)) {
|
||||||
* We iterate over all VCPUs to find the MPIDRs matching the request.
|
c_vcpu = kvm_mpidr_to_vcpu(kvm, mpidr | aff0);
|
||||||
* If we have handled one CPU, we clear its bit to detect early
|
if (c_vcpu)
|
||||||
* if we are already finished. This avoids iterating through all
|
vgic_v3_queue_sgi(c_vcpu, sgi, allow_group1);
|
||||||
* VCPUs when most of the times we just signal a single VCPU.
|
|
||||||
*/
|
|
||||||
kvm_for_each_vcpu(c, c_vcpu, kvm) {
|
|
||||||
struct vgic_irq *irq;
|
|
||||||
|
|
||||||
/* Exit early if we have dealt with all requested CPUs */
|
|
||||||
if (!broadcast && target_cpus == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Don't signal the calling VCPU */
|
|
||||||
if (broadcast && c == vcpu_id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!broadcast) {
|
|
||||||
int level0;
|
|
||||||
|
|
||||||
level0 = match_mpidr(mpidr, target_cpus, c_vcpu);
|
|
||||||
if (level0 == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* remove this matching VCPU from the mask */
|
|
||||||
target_cpus &= ~BIT(level0);
|
|
||||||
}
|
|
||||||
|
|
||||||
irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An access targeting Group0 SGIs can only generate
|
|
||||||
* those, while an access targeting Group1 SGIs can
|
|
||||||
* generate interrupts of either group.
|
|
||||||
*/
|
|
||||||
if (!irq->group || allow_group1) {
|
|
||||||
if (!irq->hw) {
|
|
||||||
irq->pending_latch = true;
|
|
||||||
vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
|
|
||||||
} else {
|
|
||||||
/* HW SGI? Ask the GIC to inject it */
|
|
||||||
int err;
|
|
||||||
err = irq_set_irqchip_state(irq->host_irq,
|
|
||||||
IRQCHIP_STATE_PENDING,
|
|
||||||
true);
|
|
||||||
WARN_RATELIMIT(err, "IRQ %d", irq->host_irq);
|
|
||||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
vgic_put_irq(vcpu->kvm, irq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ retry:
|
|||||||
/**
|
/**
|
||||||
* kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic
|
* kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic
|
||||||
* @kvm: The VM structure pointer
|
* @kvm: The VM structure pointer
|
||||||
* @cpuid: The CPU for PPIs
|
* @vcpu: The CPU for PPIs or NULL for global interrupts
|
||||||
* @intid: The INTID to inject a new state to.
|
* @intid: The INTID to inject a new state to.
|
||||||
* @level: Edge-triggered: true: to trigger the interrupt
|
* @level: Edge-triggered: true: to trigger the interrupt
|
||||||
* false: to ignore the call
|
* false: to ignore the call
|
||||||
@ -436,24 +436,22 @@ retry:
|
|||||||
* level-sensitive interrupts. You can think of the level parameter as 1
|
* level-sensitive interrupts. You can think of the level parameter as 1
|
||||||
* being HIGH and 0 being LOW and all devices being active-HIGH.
|
* being HIGH and 0 being LOW and all devices being active-HIGH.
|
||||||
*/
|
*/
|
||||||
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||||
bool level, void *owner)
|
unsigned int intid, bool level, void *owner)
|
||||||
{
|
{
|
||||||
struct kvm_vcpu *vcpu;
|
|
||||||
struct vgic_irq *irq;
|
struct vgic_irq *irq;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trace_vgic_update_irq_pending(cpuid, intid, level);
|
|
||||||
|
|
||||||
ret = vgic_lazy_init(kvm);
|
ret = vgic_lazy_init(kvm);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
vcpu = kvm_get_vcpu(kvm, cpuid);
|
|
||||||
if (!vcpu && intid < VGIC_NR_PRIVATE_IRQS)
|
if (!vcpu && intid < VGIC_NR_PRIVATE_IRQS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
trace_vgic_update_irq_pending(vcpu ? vcpu->vcpu_idx : 0, intid, level);
|
||||||
|
|
||||||
irq = vgic_get_irq(kvm, vcpu, intid);
|
irq = vgic_get_irq(kvm, vcpu, intid);
|
||||||
if (!irq)
|
if (!irq)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -135,10 +135,11 @@ void kvm_arm_vmid_clear_active(void)
|
|||||||
atomic64_set(this_cpu_ptr(&active_vmids), VMID_ACTIVE_INVALID);
|
atomic64_set(this_cpu_ptr(&active_vmids), VMID_ACTIVE_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arm_vmid_update(struct kvm_vmid *kvm_vmid)
|
bool kvm_arm_vmid_update(struct kvm_vmid *kvm_vmid)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u64 vmid, old_active_vmid;
|
u64 vmid, old_active_vmid;
|
||||||
|
bool updated = false;
|
||||||
|
|
||||||
vmid = atomic64_read(&kvm_vmid->id);
|
vmid = atomic64_read(&kvm_vmid->id);
|
||||||
|
|
||||||
@ -156,17 +157,21 @@ void kvm_arm_vmid_update(struct kvm_vmid *kvm_vmid)
|
|||||||
if (old_active_vmid != 0 && vmid_gen_match(vmid) &&
|
if (old_active_vmid != 0 && vmid_gen_match(vmid) &&
|
||||||
0 != atomic64_cmpxchg_relaxed(this_cpu_ptr(&active_vmids),
|
0 != atomic64_cmpxchg_relaxed(this_cpu_ptr(&active_vmids),
|
||||||
old_active_vmid, vmid))
|
old_active_vmid, vmid))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&cpu_vmid_lock, flags);
|
raw_spin_lock_irqsave(&cpu_vmid_lock, flags);
|
||||||
|
|
||||||
/* Check that our VMID belongs to the current generation. */
|
/* Check that our VMID belongs to the current generation. */
|
||||||
vmid = atomic64_read(&kvm_vmid->id);
|
vmid = atomic64_read(&kvm_vmid->id);
|
||||||
if (!vmid_gen_match(vmid))
|
if (!vmid_gen_match(vmid)) {
|
||||||
vmid = new_vmid(kvm_vmid);
|
vmid = new_vmid(kvm_vmid);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
atomic64_set(this_cpu_ptr(&active_vmids), vmid);
|
atomic64_set(this_cpu_ptr(&active_vmids), vmid);
|
||||||
raw_spin_unlock_irqrestore(&cpu_vmid_lock, flags);
|
raw_spin_unlock_irqrestore(&cpu_vmid_lock, flags);
|
||||||
|
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -96,7 +96,7 @@ struct arch_timer_cpu {
|
|||||||
|
|
||||||
int __init kvm_timer_hyp_init(bool has_gic);
|
int __init kvm_timer_hyp_init(bool has_gic);
|
||||||
int kvm_timer_enable(struct kvm_vcpu *vcpu);
|
int kvm_timer_enable(struct kvm_vcpu *vcpu);
|
||||||
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu);
|
void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu);
|
||||||
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
||||||
void kvm_timer_sync_user(struct kvm_vcpu *vcpu);
|
void kvm_timer_sync_user(struct kvm_vcpu *vcpu);
|
||||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
|
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1)
|
#define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1)
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM)
|
#if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM)
|
||||||
|
|
||||||
struct kvm_pmc {
|
struct kvm_pmc {
|
||||||
u8 idx; /* index into the pmu->pmc array */
|
u8 idx; /* index into the pmu->pmc array */
|
||||||
struct perf_event *perf_event;
|
struct perf_event *perf_event;
|
||||||
@ -63,6 +62,7 @@ void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val);
|
|||||||
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);
|
void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);
|
||||||
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
||||||
u64 select_idx);
|
u64 select_idx);
|
||||||
|
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu);
|
||||||
int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
|
int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_device_attr *attr);
|
struct kvm_device_attr *attr);
|
||||||
int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu,
|
int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu,
|
||||||
@ -77,7 +77,7 @@ void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
|
|||||||
void kvm_vcpu_pmu_resync_el0(void);
|
void kvm_vcpu_pmu_resync_el0(void);
|
||||||
|
|
||||||
#define kvm_vcpu_has_pmu(vcpu) \
|
#define kvm_vcpu_has_pmu(vcpu) \
|
||||||
(test_bit(KVM_ARM_VCPU_PMU_V3, (vcpu)->arch.features))
|
(vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Updates the vcpu's view of the pmu events for this cpu.
|
* Updates the vcpu's view of the pmu events for this cpu.
|
||||||
@ -101,7 +101,11 @@ void kvm_vcpu_pmu_resync_el0(void);
|
|||||||
})
|
})
|
||||||
|
|
||||||
u8 kvm_arm_pmu_get_pmuver_limit(void);
|
u8 kvm_arm_pmu_get_pmuver_limit(void);
|
||||||
|
u64 kvm_pmu_evtyper_mask(struct kvm *kvm);
|
||||||
|
int kvm_arm_set_default_pmu(struct kvm *kvm);
|
||||||
|
u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm);
|
||||||
|
|
||||||
|
u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu);
|
||||||
#else
|
#else
|
||||||
struct kvm_pmu {
|
struct kvm_pmu {
|
||||||
};
|
};
|
||||||
@ -168,12 +172,32 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
|
|||||||
static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}
|
||||||
|
static inline void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) {}
|
||||||
static inline u8 kvm_arm_pmu_get_pmuver_limit(void)
|
static inline u8 kvm_arm_pmu_get_pmuver_limit(void)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
static inline u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
static inline void kvm_vcpu_pmu_resync_el0(void) {}
|
static inline void kvm_vcpu_pmu_resync_el0(void) {}
|
||||||
|
|
||||||
|
static inline int kvm_arm_set_default_pmu(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,7 +26,7 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
|
|||||||
* revisions. It is thus safe to return the latest, unless
|
* revisions. It is thus safe to return the latest, unless
|
||||||
* userspace has instructed us otherwise.
|
* userspace has instructed us otherwise.
|
||||||
*/
|
*/
|
||||||
if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features)) {
|
if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2)) {
|
||||||
if (vcpu->kvm->arch.psci_version)
|
if (vcpu->kvm->arch.psci_version)
|
||||||
return vcpu->kvm->arch.psci_version;
|
return vcpu->kvm->arch.psci_version;
|
||||||
|
|
||||||
|
@ -375,8 +375,8 @@ int kvm_vgic_map_resources(struct kvm *kvm);
|
|||||||
int kvm_vgic_hyp_init(void);
|
int kvm_vgic_hyp_init(void);
|
||||||
void kvm_vgic_init_cpu_hardware(void);
|
void kvm_vgic_init_cpu_hardware(void);
|
||||||
|
|
||||||
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
|
int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||||
bool level, void *owner);
|
unsigned int intid, bool level, void *owner);
|
||||||
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,
|
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq,
|
||||||
u32 vintid, struct irq_ops *ops);
|
u32 vintid, struct irq_ops *ops);
|
||||||
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid);
|
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid);
|
||||||
|
@ -234,9 +234,12 @@
|
|||||||
/*
|
/*
|
||||||
* Event filters for PMUv3
|
* Event filters for PMUv3
|
||||||
*/
|
*/
|
||||||
#define ARMV8_PMU_EXCLUDE_EL1 (1U << 31)
|
#define ARMV8_PMU_EXCLUDE_EL1 (1U << 31)
|
||||||
#define ARMV8_PMU_EXCLUDE_EL0 (1U << 30)
|
#define ARMV8_PMU_EXCLUDE_EL0 (1U << 30)
|
||||||
#define ARMV8_PMU_INCLUDE_EL2 (1U << 27)
|
#define ARMV8_PMU_EXCLUDE_NS_EL1 (1U << 29)
|
||||||
|
#define ARMV8_PMU_EXCLUDE_NS_EL0 (1U << 28)
|
||||||
|
#define ARMV8_PMU_INCLUDE_EL2 (1U << 27)
|
||||||
|
#define ARMV8_PMU_EXCLUDE_EL3 (1U << 26)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PMUSERENR: user enable reg
|
* PMUSERENR: user enable reg
|
||||||
|
@ -1200,6 +1200,7 @@ struct kvm_ppc_resize_hpt {
|
|||||||
#define KVM_CAP_COUNTER_OFFSET 227
|
#define KVM_CAP_COUNTER_OFFSET 227
|
||||||
#define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228
|
#define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228
|
||||||
#define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229
|
#define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229
|
||||||
|
#define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
@ -1571,6 +1572,7 @@ struct kvm_s390_ucas_mapping {
|
|||||||
#define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags)
|
#define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags)
|
||||||
/* Available with KVM_CAP_COUNTER_OFFSET */
|
/* Available with KVM_CAP_COUNTER_OFFSET */
|
||||||
#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset)
|
#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset)
|
||||||
|
#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range)
|
||||||
|
|
||||||
/* ioctl for vm fd */
|
/* ioctl for vm fd */
|
||||||
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
|
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
|
||||||
|
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
|
# Create output directory if not already present
|
||||||
_dummy := $(shell [ -d '$(beauty_ioctl_outdir)' ] || mkdir -p '$(beauty_ioctl_outdir)')
|
_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)
|
$(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) > $@
|
$(Q)$(SHELL) '$(drm_ioctl_tbl)' $(drm_hdr_dir) > $@
|
||||||
|
|
||||||
@ -716,7 +725,9 @@ endif
|
|||||||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||||
build-dir = $(or $(__build-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) \
|
$(fadvise_advice_array) \
|
||||||
$(fsconfig_arrays) \
|
$(fsconfig_arrays) \
|
||||||
$(fsmount_arrays) \
|
$(fsmount_arrays) \
|
||||||
@ -1125,7 +1136,7 @@ endif # BUILD_BPF_SKEL
|
|||||||
bpf-skel-clean:
|
bpf-skel-clean:
|
||||||
$(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKELETONS)
|
$(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)
|
$(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)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||||
$(Q)$(RM) $(OUTPUT).config-detected
|
$(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_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_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||||
CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
|
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
|
$(OUTPUT)util/argv_split.o: ../lib/argv_split.c FORCE
|
||||||
$(call rule_mkdir)
|
$(call rule_mkdir)
|
||||||
|
@ -17,6 +17,15 @@ else
|
|||||||
ARCH_DIR := $(ARCH)
|
ARCH_DIR := $(ARCH)
|
||||||
endif
|
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/assert.c
|
||||||
LIBKVM += lib/elf.c
|
LIBKVM += lib/elf.c
|
||||||
LIBKVM += lib/guest_modes.c
|
LIBKVM += lib/guest_modes.c
|
||||||
@ -146,10 +155,12 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
|
|||||||
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
|
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
|
||||||
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
|
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
|
||||||
TEST_GEN_PROGS_aarch64 += aarch64/psci_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/smccc_filter
|
||||||
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
||||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
||||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
|
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 += access_tracking_perf_test
|
||||||
TEST_GEN_PROGS_aarch64 += demand_paging_test
|
TEST_GEN_PROGS_aarch64 += demand_paging_test
|
||||||
TEST_GEN_PROGS_aarch64 += dirty_log_test
|
TEST_GEN_PROGS_aarch64 += dirty_log_test
|
||||||
@ -257,13 +268,18 @@ $(TEST_GEN_OBJ): $(OUTPUT)/%.o: %.c
|
|||||||
$(SPLIT_TESTS_TARGETS): %: %.o $(SPLIT_TESTS_OBJS)
|
$(SPLIT_TESTS_TARGETS): %: %.o $(SPLIT_TESTS_OBJS)
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
|
$(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))))
|
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 $@
|
$(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 $@
|
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||||
|
|
||||||
# Compile the string overrides as freestanding to prevent the compiler from
|
# Compile the string overrides as freestanding to prevent the compiler from
|
||||||
@ -273,8 +289,10 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c
|
|||||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
|
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
|
||||||
|
|
||||||
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
|
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
|
||||||
|
$(SPLIT_TESTS_OBJS): $(GEN_HDRS)
|
||||||
$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
|
$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
|
||||||
$(TEST_GEN_PROGS_EXTENDED): $(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: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib ..
|
||||||
cscope:
|
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);
|
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);
|
||||||
|
|
||||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL0), val);
|
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val);
|
||||||
return el0 == ID_AA64PFR0_ELx_64BIT_ONLY;
|
return el0 == ID_AA64PFR0_EL1_ELx_64BIT_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
|
@ -116,12 +116,12 @@ static void reset_debug_state(void)
|
|||||||
|
|
||||||
/* Reset all bcr/bvr/wcr/wvr registers */
|
/* Reset all bcr/bvr/wcr/wvr registers */
|
||||||
dfr0 = read_sysreg(id_aa64dfr0_el1);
|
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++) {
|
for (i = 0; i <= brps; i++) {
|
||||||
write_dbgbcr(i, 0);
|
write_dbgbcr(i, 0);
|
||||||
write_dbgbvr(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++) {
|
for (i = 0; i <= wrps; i++) {
|
||||||
write_dbgwcr(i, 0);
|
write_dbgwcr(i, 0);
|
||||||
write_dbgwvr(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)
|
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)
|
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;
|
int b, w, c;
|
||||||
|
|
||||||
/* Number of breakpoints */
|
/* 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");
|
__TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required");
|
||||||
|
|
||||||
/* Number of watchpoints */
|
/* 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 */
|
/* 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__,
|
pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__,
|
||||||
brp_num, wrp_num, ctx_brp_num);
|
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 isar0 = read_sysreg(id_aa64isar0_el1);
|
||||||
uint64_t atomic;
|
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;
|
return atomic >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool guest_check_dc_zva(void)
|
static bool guest_check_dc_zva(void)
|
||||||
{
|
{
|
||||||
uint64_t dczid = read_sysreg(dczid_el0);
|
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;
|
return dzp == 0;
|
||||||
}
|
}
|
||||||
@ -135,8 +135,8 @@ static void guest_at(void)
|
|||||||
uint64_t par;
|
uint64_t par;
|
||||||
|
|
||||||
asm volatile("at s1e1r, %0" :: "r" (guest_test_memory));
|
asm volatile("at s1e1r, %0" :: "r" (guest_test_memory));
|
||||||
par = read_sysreg(par_el1);
|
|
||||||
isb();
|
isb();
|
||||||
|
par = read_sysreg(par_el1);
|
||||||
|
|
||||||
/* Bit 1 indicates whether the AT was successful */
|
/* Bit 1 indicates whether the AT was successful */
|
||||||
GUEST_ASSERT_EQ(par & 1, 0);
|
GUEST_ASSERT_EQ(par & 1, 0);
|
||||||
@ -196,7 +196,7 @@ static bool guest_set_ha(void)
|
|||||||
uint64_t hadbs, tcr;
|
uint64_t hadbs, tcr;
|
||||||
|
|
||||||
/* Skip if HA is not supported. */
|
/* 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)
|
if (hadbs == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -842,6 +842,7 @@ static void help(char *name)
|
|||||||
.name = SCAT2(ro_memslot_no_syndrome, _access), \
|
.name = SCAT2(ro_memslot_no_syndrome, _access), \
|
||||||
.data_memslot_flags = KVM_MEM_READONLY, \
|
.data_memslot_flags = KVM_MEM_READONLY, \
|
||||||
.pt_memslot_flags = KVM_MEM_READONLY, \
|
.pt_memslot_flags = KVM_MEM_READONLY, \
|
||||||
|
.guest_prepare = { _PREPARE(_access) }, \
|
||||||
.guest_test = _access, \
|
.guest_test = _access, \
|
||||||
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
|
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
|
||||||
.expected_events = { .fail_vcpu_runs = 1 }, \
|
.expected_events = { .fail_vcpu_runs = 1 }, \
|
||||||
@ -865,6 +866,7 @@ static void help(char *name)
|
|||||||
.name = SCAT2(ro_memslot_no_syn_and_dlog, _access), \
|
.name = SCAT2(ro_memslot_no_syn_and_dlog, _access), \
|
||||||
.data_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
|
.data_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
|
||||||
.pt_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 = _access, \
|
||||||
.guest_test_check = { _test_check }, \
|
.guest_test_check = { _test_check }, \
|
||||||
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
|
.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, \
|
.data_memslot_flags = KVM_MEM_READONLY, \
|
||||||
.pt_memslot_flags = KVM_MEM_READONLY, \
|
.pt_memslot_flags = KVM_MEM_READONLY, \
|
||||||
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
|
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
|
||||||
|
.guest_prepare = { _PREPARE(_access) }, \
|
||||||
.guest_test = _access, \
|
.guest_test = _access, \
|
||||||
.uffd_data_handler = _uffd_data_handler, \
|
.uffd_data_handler = _uffd_data_handler, \
|
||||||
.uffd_pt_handler = uffd_pt_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_SHIFT 26
|
||||||
#define ESR_EC_MASK (ESR_EC_NUM - 1)
|
#define ESR_EC_MASK (ESR_EC_NUM - 1)
|
||||||
|
|
||||||
|
#define ESR_EC_UNKNOWN 0x0
|
||||||
#define ESR_EC_SVC64 0x15
|
#define ESR_EC_SVC64 0x15
|
||||||
#define ESR_EC_IABT 0x21
|
#define ESR_EC_IABT 0x21
|
||||||
#define ESR_EC_DABT 0x25
|
#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, ®);
|
err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®);
|
||||||
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
|
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
|
||||||
|
|
||||||
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4), val) != 0xf;
|
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val) != 0xf;
|
||||||
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64), val) == 0;
|
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val) == 0;
|
||||||
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16), val) != 0;
|
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val) != 0;
|
||||||
|
|
||||||
close(vcpu_fd);
|
close(vcpu_fd);
|
||||||
close(vm_fd);
|
close(vm_fd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user