mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
Merge branch kvm-arm64/writable-id-regs into kvmarm/next
* kvm-arm64/writable-id-regs: : Writable ID registers, courtesy of Jing Zhang : : This series significantly expands the architectural feature set that : userspace can manipulate via the ID registers. A new ioctl is defined : that makes the mutable fields in the ID registers discoverable to : userspace. KVM: selftests: Avoid using forced target for generating arm64 headers tools headers arm64: Fix references to top srcdir in Makefile KVM: arm64: selftests: Test for setting ID register from usersapce tools headers arm64: Update sysreg.h with kernel sources KVM: selftests: Generate sysreg-defs.h and add to include path perf build: Generate arm64's sysreg-defs.h and add to include path tools: arm64: Add a Makefile for generating sysreg-defs.h KVM: arm64: Document vCPU feature selection UAPIs KVM: arm64: Allow userspace to change ID_AA64ZFR0_EL1 KVM: arm64: Allow userspace to change ID_AA64PFR0_EL1 KVM: arm64: Allow userspace to change ID_AA64MMFR{0-2}_EL1 KVM: arm64: Allow userspace to change ID_AA64ISAR{0-2}_EL1 KVM: arm64: Bump up the default KVM sanitised debug version to v8p8 KVM: arm64: Reject attempts to set invalid debug arch version KVM: arm64: Advertise selected DebugVer in DBGDIDR.Version KVM: arm64: Use guest ID register values for the sake of emulation KVM: arm64: Document KVM_ARM_GET_REG_WRITABLE_MASKS KVM: arm64: Allow userspace to get the writable masks for feature ID registers Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
commit
a87a36436c
@ -3370,6 +3370,8 @@ return indicates the attribute is implemented. It does not necessarily
|
||||
indicate that the attribute can be read or written in the device's
|
||||
current state. "addr" is ignored.
|
||||
|
||||
.. _KVM_ARM_VCPU_INIT:
|
||||
|
||||
4.82 KVM_ARM_VCPU_INIT
|
||||
----------------------
|
||||
|
||||
@ -6070,6 +6072,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
|
||||
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
|
||||
========================
|
||||
|
||||
|
@ -11,3 +11,4 @@ ARM
|
||||
hypercalls
|
||||
pvtime
|
||||
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.
|
@ -1108,6 +1108,8 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
|
||||
struct kvm_arm_copy_mte_tags *copy_tags);
|
||||
int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
|
||||
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 */
|
||||
int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
|
||||
|
@ -505,6 +505,38 @@ struct kvm_smccc_filter {
|
||||
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
|
||||
#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 /* __ARM_KVM_H__ */
|
||||
|
@ -318,6 +318,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES:
|
||||
r = kvm_supported_block_sizes();
|
||||
break;
|
||||
case KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES:
|
||||
r = BIT(0);
|
||||
break;
|
||||
default:
|
||||
r = 0;
|
||||
}
|
||||
@ -1713,6 +1716,13 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
||||
|
||||
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:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
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);
|
||||
|
||||
if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
|
||||
@ -1220,8 +1220,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 */
|
||||
switch (id) {
|
||||
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;
|
||||
break;
|
||||
case ID_AA64DFR0_EL1_DebugVer_SHIFT:
|
||||
kvm_ftr.type = FTR_LOWER_SAFE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SYS_ID_DFR0_EL1:
|
||||
if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
|
||||
@ -1377,6 +1383,13 @@ static inline bool is_id_reg(u32 id)
|
||||
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,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
@ -1473,14 +1486,21 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
|
||||
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,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
|
||||
|
||||
/* Limit debug to ARMv8.0 */
|
||||
val &= ~ID_AA64DFR0_EL1_DebugVer_MASK;
|
||||
val |= SYS_FIELD_PREP_ENUM(ID_AA64DFR0_EL1, DebugVer, IMP);
|
||||
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8);
|
||||
|
||||
/*
|
||||
* Only initialize the PMU version if the vCPU was configured with one.
|
||||
@ -1500,6 +1520,7 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
{
|
||||
u8 debugver = SYS_FIELD_GET(ID_AA64DFR0_EL1, DebugVer, val);
|
||||
u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, val);
|
||||
|
||||
/*
|
||||
@ -1519,6 +1540,13 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
if (pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1532,6 +1560,8 @@ static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
if (kvm_vcpu_has_pmu(vcpu))
|
||||
val |= SYS_FIELD_PREP(ID_DFR0_EL1, PerfMon, perfmon);
|
||||
|
||||
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_DFR0_EL1, CopDbg, Debugv8p8);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -1540,6 +1570,7 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
u64 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) {
|
||||
val &= ~ID_DFR0_EL1_PerfMon_MASK;
|
||||
@ -1555,6 +1586,9 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
|
||||
if (perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3)
|
||||
return -EINVAL;
|
||||
|
||||
if (copdbg < ID_DFR0_EL1_CopDbg_Armv8)
|
||||
return -EINVAL;
|
||||
|
||||
return set_id_reg(vcpu, rd, val);
|
||||
}
|
||||
|
||||
@ -1821,11 +1855,14 @@ static unsigned int hidden_user_visibility(const struct kvm_vcpu *vcpu,
|
||||
* from userspace.
|
||||
*/
|
||||
|
||||
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
||||
#define ID_SANITISED(name) { \
|
||||
#define ID_DESC(name) \
|
||||
SYS_DESC(SYS_##name), \
|
||||
.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, \
|
||||
.visibility = id_visibility, \
|
||||
.reset = kvm_read_sanitised_id_reg, \
|
||||
@ -1834,15 +1871,22 @@ static unsigned int hidden_user_visibility(const struct kvm_vcpu *vcpu,
|
||||
|
||||
/* sys_reg_desc initialiser for known cpufeature ID registers */
|
||||
#define AA32_ID_SANITISED(name) { \
|
||||
SYS_DESC(SYS_##name), \
|
||||
.access = access_id_reg, \
|
||||
.get_user = get_id_reg, \
|
||||
ID_DESC(name), \
|
||||
.set_user = set_id_reg, \
|
||||
.visibility = aa32_id_visibility, \
|
||||
.reset = kvm_read_sanitised_id_reg, \
|
||||
.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
|
||||
* register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
|
||||
@ -1864,9 +1908,7 @@ static unsigned int hidden_user_visibility(const struct kvm_vcpu *vcpu,
|
||||
* RAZ for the guest.
|
||||
*/
|
||||
#define ID_HIDDEN(name) { \
|
||||
SYS_DESC(SYS_##name), \
|
||||
.access = access_id_reg, \
|
||||
.get_user = get_id_reg, \
|
||||
ID_DESC(name), \
|
||||
.set_user = set_id_reg, \
|
||||
.visibility = raz_visibility, \
|
||||
.reset = kvm_read_sanitised_id_reg, \
|
||||
@ -1984,7 +2026,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
.set_user = set_id_dfr0_el1,
|
||||
.visibility = aa32_id_visibility,
|
||||
.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),
|
||||
AA32_ID_SANITISED(ID_MMFR0_EL1),
|
||||
AA32_ID_SANITISED(ID_MMFR1_EL1),
|
||||
@ -2018,11 +2061,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
.get_user = get_id_reg,
|
||||
.set_user = set_id_reg,
|
||||
.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_UNALLOCATED(4,2),
|
||||
ID_UNALLOCATED(4,3),
|
||||
ID_SANITISED(ID_AA64ZFR0_EL1),
|
||||
ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0),
|
||||
ID_HIDDEN(ID_AA64SMFR0_EL1),
|
||||
ID_UNALLOCATED(4,6),
|
||||
ID_UNALLOCATED(4,7),
|
||||
@ -2033,7 +2082,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
.get_user = get_id_reg,
|
||||
.set_user = set_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_UNALLOCATED(5,2),
|
||||
ID_UNALLOCATED(5,3),
|
||||
@ -2043,9 +2093,15 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
ID_UNALLOCATED(5,7),
|
||||
|
||||
/* CRm=6 */
|
||||
ID_SANITISED(ID_AA64ISAR0_EL1),
|
||||
ID_SANITISED(ID_AA64ISAR1_EL1),
|
||||
ID_SANITISED(ID_AA64ISAR2_EL1),
|
||||
ID_WRITABLE(ID_AA64ISAR0_EL1, ~ID_AA64ISAR0_EL1_RES0),
|
||||
ID_WRITABLE(ID_AA64ISAR1_EL1, ~(ID_AA64ISAR1_EL1_GPI |
|
||||
ID_AA64ISAR1_EL1_GPA |
|
||||
ID_AA64ISAR1_EL1_API |
|
||||
ID_AA64ISAR1_EL1_APA)),
|
||||
ID_WRITABLE(ID_AA64ISAR2_EL1, ~(ID_AA64ISAR2_EL1_RES0 |
|
||||
ID_AA64ISAR2_EL1_MOPS |
|
||||
ID_AA64ISAR2_EL1_APA3 |
|
||||
ID_AA64ISAR2_EL1_GPA3)),
|
||||
ID_UNALLOCATED(6,3),
|
||||
ID_UNALLOCATED(6,4),
|
||||
ID_UNALLOCATED(6,5),
|
||||
@ -2053,9 +2109,23 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
ID_UNALLOCATED(6,7),
|
||||
|
||||
/* CRm=7 */
|
||||
ID_SANITISED(ID_AA64MMFR0_EL1),
|
||||
ID_SANITISED(ID_AA64MMFR1_EL1),
|
||||
ID_SANITISED(ID_AA64MMFR2_EL1),
|
||||
ID_WRITABLE(ID_AA64MMFR0_EL1, ~(ID_AA64MMFR0_EL1_RES0 |
|
||||
ID_AA64MMFR0_EL1_TGRAN4_2 |
|
||||
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_UNALLOCATED(7,4),
|
||||
ID_UNALLOCATED(7,5),
|
||||
@ -2452,14 +2522,15 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
|
||||
if (p->is_write) {
|
||||
return ignore_write(vcpu, p);
|
||||
} else {
|
||||
u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
|
||||
u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
|
||||
u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL1_EL3_SHIFT);
|
||||
u64 dfr = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
|
||||
u64 pfr = IDREG(vcpu->kvm, SYS_ID_AA64PFR0_EL1);
|
||||
u32 el3 = !!SYS_FIELD_GET(ID_AA64PFR0_EL1, EL3, pfr);
|
||||
|
||||
p->regval = ((((dfr >> ID_AA64DFR0_EL1_WRPs_SHIFT) & 0xf) << 28) |
|
||||
(((dfr >> ID_AA64DFR0_EL1_BRPs_SHIFT) & 0xf) << 24) |
|
||||
(((dfr >> ID_AA64DFR0_EL1_CTX_CMPs_SHIFT) & 0xf) << 20)
|
||||
| (6 << 16) | (1 << 15) | (el3 << 14) | (el3 << 12));
|
||||
p->regval = ((SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr) << 28) |
|
||||
(SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr) << 24) |
|
||||
(SYS_FIELD_GET(ID_AA64DFR0_EL1, CTX_CMPs, dfr) << 20) |
|
||||
(SYS_FIELD_GET(ID_AA64DFR0_EL1, DebugVer, dfr) << 16) |
|
||||
(1 << 15) | (el3 << 14) | (el3 << 12));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3586,6 +3657,65 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *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)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
|
@ -1192,6 +1192,7 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_COUNTER_OFFSET 227
|
||||
#define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228
|
||||
#define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229
|
||||
#define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
@ -1562,6 +1563,7 @@ struct kvm_s390_ucas_mapping {
|
||||
#define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags)
|
||||
/* Available with KVM_CAP_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 */
|
||||
#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
|
@ -443,6 +443,15 @@ drm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/drm_ioctl.sh
|
||||
# Create output directory if not already present
|
||||
_dummy := $(shell [ -d '$(beauty_ioctl_outdir)' ] || mkdir -p '$(beauty_ioctl_outdir)')
|
||||
|
||||
arm64_gen_sysreg_dir := $(srctree)/tools/arch/arm64/tools
|
||||
|
||||
arm64-sysreg-defs: FORCE
|
||||
$(Q)$(MAKE) -C $(arm64_gen_sysreg_dir)
|
||||
|
||||
arm64-sysreg-defs-clean:
|
||||
$(call QUIET_CLEAN,arm64-sysreg-defs)
|
||||
$(Q)$(MAKE) -C $(arm64_gen_sysreg_dir) clean > /dev/null
|
||||
|
||||
$(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_tbl)
|
||||
$(Q)$(SHELL) '$(drm_ioctl_tbl)' $(drm_hdr_dir) > $@
|
||||
|
||||
@ -716,7 +725,9 @@ endif
|
||||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||
build-dir = $(or $(__build-dir),.)
|
||||
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioctl_array) \
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders \
|
||||
arm64-sysreg-defs \
|
||||
$(drm_ioctl_array) \
|
||||
$(fadvise_advice_array) \
|
||||
$(fsconfig_arrays) \
|
||||
$(fsmount_arrays) \
|
||||
@ -1125,7 +1136,7 @@ endif # BUILD_BPF_SKEL
|
||||
bpf-skel-clean:
|
||||
$(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKELETONS)
|
||||
|
||||
clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean fixdep-clean python-clean bpf-skel-clean tests-coresight-targets-clean
|
||||
clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean arm64-sysreg-defs-clean fixdep-clean python-clean bpf-skel-clean tests-coresight-targets-clean
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive $(OUTPUT)perf-iostat $(LANG_BINDINGS)
|
||||
$(Q)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT).config-detected
|
||||
|
@ -345,7 +345,7 @@ CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET
|
||||
CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
|
||||
CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/
|
||||
CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/ -I$(srctree)/tools/arch/arm64/include/generated/
|
||||
|
||||
$(OUTPUT)util/argv_split.o: ../lib/argv_split.c FORCE
|
||||
$(call rule_mkdir)
|
||||
|
@ -17,6 +17,15 @@ else
|
||||
ARCH_DIR := $(ARCH)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
arm64_tools_dir := $(top_srcdir)/tools/arch/arm64/tools/
|
||||
GEN_HDRS := $(top_srcdir)/tools/arch/arm64/include/generated/
|
||||
CFLAGS += -I$(GEN_HDRS)
|
||||
|
||||
$(GEN_HDRS): $(wildcard $(arm64_tools_dir)/*)
|
||||
$(MAKE) -C $(arm64_tools_dir)
|
||||
endif
|
||||
|
||||
LIBKVM += lib/assert.c
|
||||
LIBKVM += lib/elf.c
|
||||
LIBKVM += lib/guest_modes.c
|
||||
@ -145,6 +154,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/psci_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/set_id_regs
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/smccc_filter
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
||||
@ -256,13 +266,18 @@ $(TEST_GEN_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(SPLIT_TESTS_TARGETS): %: %.o $(SPLIT_TESTS_OBJS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
|
||||
|
||||
EXTRA_CLEAN += $(LIBKVM_OBJS) $(TEST_DEP_FILES) $(TEST_GEN_OBJ) $(SPLIT_TESTS_OBJS) cscope.*
|
||||
EXTRA_CLEAN += $(GEN_HDRS) \
|
||||
$(LIBKVM_OBJS) \
|
||||
$(SPLIT_TESTS_OBJS) \
|
||||
$(TEST_DEP_FILES) \
|
||||
$(TEST_GEN_OBJ) \
|
||||
cscope.*
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ))))
|
||||
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c $(GEN_HDRS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
|
||||
$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(GEN_HDRS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
|
||||
|
||||
# Compile the string overrides as freestanding to prevent the compiler from
|
||||
@ -272,8 +287,10 @@ $(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
|
||||
|
||||
x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
|
||||
$(SPLIT_TESTS_OBJS): $(GEN_HDRS)
|
||||
$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
|
||||
$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)
|
||||
$(TEST_GEN_OBJ): $(GEN_HDRS)
|
||||
|
||||
cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib ..
|
||||
cscope:
|
||||
|
@ -146,8 +146,8 @@ static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu)
|
||||
|
||||
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);
|
||||
|
||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL0), val);
|
||||
return el0 == ID_AA64PFR0_ELx_64BIT_ONLY;
|
||||
el0 = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0), val);
|
||||
return el0 == ID_AA64PFR0_EL1_ELx_64BIT_ONLY;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
@ -116,12 +116,12 @@ static void reset_debug_state(void)
|
||||
|
||||
/* Reset all bcr/bvr/wcr/wvr registers */
|
||||
dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), dfr0);
|
||||
brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), dfr0);
|
||||
for (i = 0; i <= brps; i++) {
|
||||
write_dbgbcr(i, 0);
|
||||
write_dbgbvr(i, 0);
|
||||
}
|
||||
wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), dfr0);
|
||||
wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), dfr0);
|
||||
for (i = 0; i <= wrps; i++) {
|
||||
write_dbgwcr(i, 0);
|
||||
write_dbgwvr(i, 0);
|
||||
@ -418,7 +418,7 @@ static void guest_code_ss(int test_cnt)
|
||||
|
||||
static int debug_version(uint64_t id_aa64dfr0)
|
||||
{
|
||||
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), id_aa64dfr0);
|
||||
return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), id_aa64dfr0);
|
||||
}
|
||||
|
||||
static void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)
|
||||
@ -539,14 +539,14 @@ void test_guest_debug_exceptions_all(uint64_t aa64dfr0)
|
||||
int b, w, c;
|
||||
|
||||
/* Number of breakpoints */
|
||||
brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), aa64dfr0) + 1;
|
||||
brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_BRPs), aa64dfr0) + 1;
|
||||
__TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required");
|
||||
|
||||
/* Number of watchpoints */
|
||||
wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), aa64dfr0) + 1;
|
||||
wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_WRPs), aa64dfr0) + 1;
|
||||
|
||||
/* Number of context aware breakpoints */
|
||||
ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_CTX_CMPS), aa64dfr0) + 1;
|
||||
ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_CTX_CMPs), aa64dfr0) + 1;
|
||||
|
||||
pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__,
|
||||
brp_num, wrp_num, ctx_brp_num);
|
||||
|
@ -96,14 +96,14 @@ static bool guest_check_lse(void)
|
||||
uint64_t isar0 = read_sysreg(id_aa64isar0_el1);
|
||||
uint64_t atomic;
|
||||
|
||||
atomic = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR0_ATOMICS), isar0);
|
||||
atomic = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR0_EL1_ATOMIC), isar0);
|
||||
return atomic >= 2;
|
||||
}
|
||||
|
||||
static bool guest_check_dc_zva(void)
|
||||
{
|
||||
uint64_t dczid = read_sysreg(dczid_el0);
|
||||
uint64_t dzp = FIELD_GET(ARM64_FEATURE_MASK(DCZID_DZP), dczid);
|
||||
uint64_t dzp = FIELD_GET(ARM64_FEATURE_MASK(DCZID_EL0_DZP), dczid);
|
||||
|
||||
return dzp == 0;
|
||||
}
|
||||
@ -196,7 +196,7 @@ static bool guest_set_ha(void)
|
||||
uint64_t hadbs, tcr;
|
||||
|
||||
/* Skip if HA is not supported. */
|
||||
hadbs = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_HADBS), mmfr1);
|
||||
hadbs = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_EL1_HAFDBS), mmfr1);
|
||||
if (hadbs == 0)
|
||||
return false;
|
||||
|
||||
|
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();
|
||||
}
|
@ -518,9 +518,9 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
|
||||
err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®);
|
||||
TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd));
|
||||
|
||||
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN4), val) != 0xf;
|
||||
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN64), val) == 0;
|
||||
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_TGRAN16), val) != 0;
|
||||
*ps4k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN4), val) != 0xf;
|
||||
*ps64k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN64), val) == 0;
|
||||
*ps16k = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_TGRAN16), val) != 0;
|
||||
|
||||
close(vcpu_fd);
|
||||
close(vm_fd);
|
||||
|
Loading…
Reference in New Issue
Block a user