mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
Merge branch 'kvm-arm64/writable-midr' into kvmarm/next
* kvm-arm64/writable-midr: : Writable implementation ID registers, courtesy of Sebastian Ott : : Introduce a new capability that allows userspace to set the : ID registers that identify a CPU implementation: MIDR_EL1, REVIDR_EL1, : and AIDR_EL1. Also plug a hole in KVM's trap configuration where : SMIDR_EL1 was readable at EL1, despite the fact that KVM does not : support SME. KVM: arm64: Fix documentation for KVM_CAP_ARM_WRITABLE_IMP_ID_REGS KVM: arm64: Copy MIDR_EL1 into hyp VM when it is writable KVM: arm64: Copy guest CTR_EL0 into hyp VM KVM: selftests: arm64: Test writes to MIDR,REVIDR,AIDR KVM: arm64: Allow userspace to change the implementation ID registers KVM: arm64: Load VPIDR_EL2 with the VM's MIDR_EL1 value KVM: arm64: Maintain per-VM copy of implementation ID regs KVM: arm64: Set HCR_EL2.TID1 unconditionally Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
commit
4f2774c57a
|
|
@ -8258,6 +8258,24 @@ KVM exits with the register state of either the L1 or L2 guest
|
|||
depending on which executed at the time of an exit. Userspace must
|
||||
take care to differentiate between these cases.
|
||||
|
||||
7.37 KVM_CAP_ARM_WRITABLE_IMP_ID_REGS
|
||||
-------------------------------------
|
||||
|
||||
:Architectures: arm64
|
||||
:Target: VM
|
||||
:Parameters: None
|
||||
:Returns: 0 on success, -EINVAL if vCPUs have been created before enabling this
|
||||
capability.
|
||||
|
||||
This capability changes the behavior of the registers that identify a PE
|
||||
implementation of the Arm architecture: MIDR_EL1, REVIDR_EL1, and AIDR_EL1.
|
||||
By default, these registers are visible to userspace but treated as invariant.
|
||||
|
||||
When this capability is enabled, KVM allows userspace to change the
|
||||
aforementioned registers before the first KVM_RUN. These registers are VM
|
||||
scoped, meaning that the same set of values are presented on all vCPUs in a
|
||||
given VM.
|
||||
|
||||
8. Other capabilities.
|
||||
======================
|
||||
|
||||
|
|
|
|||
|
|
@ -92,12 +92,12 @@
|
|||
* SWIO: Turn set/way invalidates into set/way clean+invalidate
|
||||
* PTW: Take a stage2 fault if a stage1 walk steps in device memory
|
||||
* TID3: Trap EL1 reads of group 3 ID registers
|
||||
* TID2: Trap CTR_EL0, CCSIDR2_EL1, CLIDR_EL1, and CSSELR_EL1
|
||||
* TID1: Trap REVIDR_EL1, AIDR_EL1, and SMIDR_EL1
|
||||
*/
|
||||
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
|
||||
HCR_BSU_IS | HCR_FB | HCR_TACR | \
|
||||
HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW | HCR_TLOR | \
|
||||
HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3)
|
||||
HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3 | HCR_TID1)
|
||||
#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)
|
||||
#define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC)
|
||||
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
|
||||
|
|
|
|||
|
|
@ -336,6 +336,8 @@ struct kvm_arch {
|
|||
#define KVM_ARCH_FLAG_FGU_INITIALIZED 8
|
||||
/* SVE exposed to guest */
|
||||
#define KVM_ARCH_FLAG_GUEST_HAS_SVE 9
|
||||
/* MIDR_EL1, REVIDR_EL1, and AIDR_EL1 are writable from userspace */
|
||||
#define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10
|
||||
unsigned long flags;
|
||||
|
||||
/* VM-wide vCPU feature set */
|
||||
|
|
@ -375,6 +377,9 @@ struct kvm_arch {
|
|||
#define KVM_ARM_ID_REG_NUM (IDREG_IDX(sys_reg(3, 0, 0, 7, 7)) + 1)
|
||||
u64 id_regs[KVM_ARM_ID_REG_NUM];
|
||||
|
||||
u64 midr_el1;
|
||||
u64 revidr_el1;
|
||||
u64 aidr_el1;
|
||||
u64 ctr_el0;
|
||||
|
||||
/* Masks for VNCR-backed and general EL2 sysregs */
|
||||
|
|
@ -1489,6 +1494,12 @@ static inline u64 *__vm_id_reg(struct kvm_arch *ka, u32 reg)
|
|||
return &ka->id_regs[IDREG_IDX(reg)];
|
||||
case SYS_CTR_EL0:
|
||||
return &ka->ctr_el0;
|
||||
case SYS_MIDR_EL1:
|
||||
return &ka->midr_el1;
|
||||
case SYS_REVIDR_EL1:
|
||||
return &ka->revidr_el1;
|
||||
case SYS_AIDR_EL1:
|
||||
return &ka->aidr_el1;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -125,6 +125,14 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
|
|||
}
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
break;
|
||||
case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS:
|
||||
mutex_lock(&kvm->lock);
|
||||
if (!kvm->created_vcpus) {
|
||||
r = 0;
|
||||
set_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &kvm->arch.flags);
|
||||
}
|
||||
mutex_unlock(&kvm->lock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -313,6 +321,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|||
case KVM_CAP_ARM_SYSTEM_SUSPEND:
|
||||
case KVM_CAP_IRQFD_RESAMPLE:
|
||||
case KVM_CAP_COUNTER_OFFSET:
|
||||
case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_SET_GUEST_DEBUG2:
|
||||
|
|
|
|||
|
|
@ -43,6 +43,17 @@ static inline u64 *ctxt_mdscr_el1(struct kvm_cpu_context *ctxt)
|
|||
return &ctxt_sys_reg(ctxt, MDSCR_EL1);
|
||||
}
|
||||
|
||||
static inline u64 ctxt_midr_el1(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
struct kvm *kvm = kern_hyp_va(ctxt_to_vcpu(ctxt)->kvm);
|
||||
|
||||
if (!(ctxt_is_guest(ctxt) &&
|
||||
test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &kvm->arch.flags)))
|
||||
return read_cpuid_id();
|
||||
|
||||
return kvm_read_vm_id_reg(kvm, SYS_MIDR_EL1);
|
||||
}
|
||||
|
||||
static inline void __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
*ctxt_mdscr_el1(ctxt) = read_sysreg(mdscr_el1);
|
||||
|
|
@ -168,8 +179,9 @@ static inline void __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
|
|||
}
|
||||
|
||||
static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt,
|
||||
u64 mpidr)
|
||||
u64 midr, u64 mpidr)
|
||||
{
|
||||
write_sysreg(midr, vpidr_el2);
|
||||
write_sysreg(mpidr, vmpidr_el2);
|
||||
|
||||
if (has_vhe() ||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ static void pkvm_vcpu_reset_hcr(struct kvm_vcpu *vcpu)
|
|||
vcpu->arch.hcr_el2 |= HCR_FWB;
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_EVT) &&
|
||||
!cpus_have_final_cap(ARM64_MISMATCHED_CACHE_TYPE))
|
||||
!cpus_have_final_cap(ARM64_MISMATCHED_CACHE_TYPE) &&
|
||||
kvm_read_vm_id_reg(vcpu->kvm, SYS_CTR_EL0) == read_cpuid(CTR_EL0))
|
||||
vcpu->arch.hcr_el2 |= HCR_TID4;
|
||||
else
|
||||
vcpu->arch.hcr_el2 |= HCR_TID2;
|
||||
|
|
@ -315,6 +316,9 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
|
|||
unsigned long host_arch_flags = READ_ONCE(host_kvm->arch.flags);
|
||||
DECLARE_BITMAP(allowed_features, KVM_VCPU_MAX_FEATURES);
|
||||
|
||||
/* CTR_EL0 is always under host control, even for protected VMs. */
|
||||
hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
|
||||
|
||||
if (test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &host_kvm->arch.flags))
|
||||
set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
|
||||
|
||||
|
|
@ -325,6 +329,10 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
|
|||
bitmap_copy(kvm->arch.vcpu_features,
|
||||
host_kvm->arch.vcpu_features,
|
||||
KVM_VCPU_MAX_FEATURES);
|
||||
|
||||
if (test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &host_arch_flags))
|
||||
hyp_vm->kvm.arch.midr_el1 = host_kvm->arch.midr_el1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt)
|
|||
|
||||
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_el1_state(ctxt, ctxt_sys_reg(ctxt, MPIDR_EL1));
|
||||
u64 midr = ctxt_midr_el1(ctxt);
|
||||
|
||||
__sysreg_restore_el1_state(ctxt, midr, ctxt_sys_reg(ctxt, MPIDR_EL1));
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
__sysreg_restore_user_state(ctxt);
|
||||
__sysreg_restore_el2_return_state(ctxt);
|
||||
|
|
|
|||
|
|
@ -87,11 +87,12 @@ static void __sysreg_restore_vel2_state(struct kvm_vcpu *vcpu)
|
|||
write_sysreg(__vcpu_sys_reg(vcpu, PAR_EL1), par_el1);
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, TPIDR_EL1), tpidr_el1);
|
||||
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, MPIDR_EL1), vmpidr_el2);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, MAIR_EL2), SYS_MAIR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, VBAR_EL2), SYS_VBAR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, CONTEXTIDR_EL2), SYS_CONTEXTIDR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, AMAIR_EL2), SYS_AMAIR);
|
||||
write_sysreg(ctxt_midr_el1(&vcpu->arch.ctxt), vpidr_el2);
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, MPIDR_EL1), vmpidr_el2);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, MAIR_EL2), SYS_MAIR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, VBAR_EL2), SYS_VBAR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, CONTEXTIDR_EL2), SYS_CONTEXTIDR);
|
||||
write_sysreg_el1(__vcpu_sys_reg(vcpu, AMAIR_EL2), SYS_AMAIR);
|
||||
|
||||
if (vcpu_el2_e2h_is_set(vcpu)) {
|
||||
/*
|
||||
|
|
@ -191,7 +192,7 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
u64 mpidr;
|
||||
u64 midr, mpidr;
|
||||
|
||||
host_ctxt = host_data_ptr(host_ctxt);
|
||||
__sysreg_save_user_state(host_ctxt);
|
||||
|
|
@ -220,23 +221,18 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
|
|||
__sysreg_restore_vel2_state(vcpu);
|
||||
} else {
|
||||
if (vcpu_has_nv(vcpu)) {
|
||||
/*
|
||||
* Use the guest hypervisor's VPIDR_EL2 when in a
|
||||
* nested state. The hardware value of MIDR_EL1 gets
|
||||
* restored on put.
|
||||
*/
|
||||
write_sysreg(ctxt_sys_reg(guest_ctxt, VPIDR_EL2), vpidr_el2);
|
||||
|
||||
/*
|
||||
* As we're restoring a nested guest, set the value
|
||||
* provided by the guest hypervisor.
|
||||
*/
|
||||
midr = ctxt_sys_reg(guest_ctxt, VPIDR_EL2);
|
||||
mpidr = ctxt_sys_reg(guest_ctxt, VMPIDR_EL2);
|
||||
} else {
|
||||
midr = ctxt_midr_el1(guest_ctxt);
|
||||
mpidr = ctxt_sys_reg(guest_ctxt, MPIDR_EL1);
|
||||
}
|
||||
|
||||
__sysreg_restore_el1_state(guest_ctxt, mpidr);
|
||||
__sysreg_restore_el1_state(guest_ctxt, midr, mpidr);
|
||||
}
|
||||
|
||||
vcpu_set_flag(vcpu, SYSREGS_ON_CPU);
|
||||
|
|
@ -271,9 +267,5 @@ void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu)
|
|||
/* Restore host user state */
|
||||
__sysreg_restore_user_state(host_ctxt);
|
||||
|
||||
/* If leaving a nesting guest, restore MIDR_EL1 default view */
|
||||
if (vcpu_has_nv(vcpu))
|
||||
write_sysreg(read_cpuid_id(), vpidr_el2);
|
||||
|
||||
vcpu_clear_flag(vcpu, SYSREGS_ON_CPU);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1674,15 +1674,24 @@ static bool is_feature_id_reg(u32 encoding)
|
|||
* Return true if the register's (Op0, Op1, CRn, CRm, Op2) is
|
||||
* (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8, which is the range of ID
|
||||
* registers KVM maintains on a per-VM basis.
|
||||
*
|
||||
* Additionally, the implementation ID registers and CTR_EL0 are handled as
|
||||
* per-VM registers.
|
||||
*/
|
||||
static inline bool is_vm_ftr_id_reg(u32 id)
|
||||
{
|
||||
if (id == SYS_CTR_EL0)
|
||||
switch (id) {
|
||||
case SYS_CTR_EL0:
|
||||
case SYS_MIDR_EL1:
|
||||
case SYS_REVIDR_EL1:
|
||||
case SYS_AIDR_EL1:
|
||||
return true;
|
||||
default:
|
||||
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) < 8);
|
||||
|
||||
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) < 8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_vcpu_ftr_id_reg(u32 id)
|
||||
|
|
@ -2570,6 +2579,120 @@ static bool access_mdcr(struct kvm_vcpu *vcpu,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* For historical (ahem ABI) reasons, KVM treated MIDR_EL1, REVIDR_EL1, and
|
||||
* AIDR_EL1 as "invariant" registers, meaning userspace cannot change them.
|
||||
* The values made visible to userspace were the register values of the boot
|
||||
* CPU.
|
||||
*
|
||||
* At the same time, reads from these registers at EL1 previously were not
|
||||
* trapped, allowing the guest to read the actual hardware value. On big-little
|
||||
* machines, this means the VM can see different values depending on where a
|
||||
* given vCPU got scheduled.
|
||||
*
|
||||
* These registers are now trapped as collateral damage from SME, and what
|
||||
* follows attempts to give a user / guest view consistent with the existing
|
||||
* ABI.
|
||||
*/
|
||||
static bool access_imp_id_reg(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
if (p->is_write)
|
||||
return write_to_read_only(vcpu, p, r);
|
||||
|
||||
/*
|
||||
* Return the VM-scoped implementation ID register values if userspace
|
||||
* has made them writable.
|
||||
*/
|
||||
if (test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &vcpu->kvm->arch.flags))
|
||||
return access_id_reg(vcpu, p, r);
|
||||
|
||||
/*
|
||||
* Otherwise, fall back to the old behavior of returning the value of
|
||||
* the current CPU.
|
||||
*/
|
||||
switch (reg_to_encoding(r)) {
|
||||
case SYS_REVIDR_EL1:
|
||||
p->regval = read_sysreg(revidr_el1);
|
||||
break;
|
||||
case SYS_AIDR_EL1:
|
||||
p->regval = read_sysreg(aidr_el1);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u64 __ro_after_init boot_cpu_midr_val;
|
||||
static u64 __ro_after_init boot_cpu_revidr_val;
|
||||
static u64 __ro_after_init boot_cpu_aidr_val;
|
||||
|
||||
static void init_imp_id_regs(void)
|
||||
{
|
||||
boot_cpu_midr_val = read_sysreg(midr_el1);
|
||||
boot_cpu_revidr_val = read_sysreg(revidr_el1);
|
||||
boot_cpu_aidr_val = read_sysreg(aidr_el1);
|
||||
}
|
||||
|
||||
static u64 reset_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||
{
|
||||
switch (reg_to_encoding(r)) {
|
||||
case SYS_MIDR_EL1:
|
||||
return boot_cpu_midr_val;
|
||||
case SYS_REVIDR_EL1:
|
||||
return boot_cpu_revidr_val;
|
||||
case SYS_AIDR_EL1:
|
||||
return boot_cpu_aidr_val;
|
||||
default:
|
||||
KVM_BUG_ON(1, vcpu->kvm);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 val)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
u64 expected;
|
||||
|
||||
guard(mutex)(&kvm->arch.config_lock);
|
||||
|
||||
expected = read_id_reg(vcpu, r);
|
||||
if (expected == val)
|
||||
return 0;
|
||||
|
||||
if (!test_bit(KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS, &kvm->arch.flags))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Once the VM has started the ID registers are immutable. Reject the
|
||||
* write if userspace tries to change it.
|
||||
*/
|
||||
if (kvm_vm_has_ran_once(kvm))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* Any value is allowed for the implementation ID registers so long as
|
||||
* it is within the writable mask.
|
||||
*/
|
||||
if ((val & r->val) != val)
|
||||
return -EINVAL;
|
||||
|
||||
kvm_set_vm_id_reg(kvm, reg_to_encoding(r), val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IMPLEMENTATION_ID(reg, mask) { \
|
||||
SYS_DESC(SYS_##reg), \
|
||||
.access = access_imp_id_reg, \
|
||||
.get_user = get_id_reg, \
|
||||
.set_user = set_imp_id_reg, \
|
||||
.reset = reset_imp_id_reg, \
|
||||
.val = mask, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Architected system registers.
|
||||
|
|
@ -2619,7 +2742,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
|
||||
{ SYS_DESC(SYS_DBGVCR32_EL2), undef_access, reset_val, DBGVCR32_EL2, 0 },
|
||||
|
||||
IMPLEMENTATION_ID(MIDR_EL1, GENMASK_ULL(31, 0)),
|
||||
{ SYS_DESC(SYS_MPIDR_EL1), NULL, reset_mpidr, MPIDR_EL1 },
|
||||
IMPLEMENTATION_ID(REVIDR_EL1, GENMASK_ULL(63, 0)),
|
||||
|
||||
/*
|
||||
* ID regs: all ID_SANITISED() entries here must have corresponding
|
||||
|
|
@ -2890,6 +3015,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
.set_user = set_clidr, .val = ~CLIDR_EL1_RES0 },
|
||||
{ SYS_DESC(SYS_CCSIDR2_EL1), undef_access },
|
||||
{ SYS_DESC(SYS_SMIDR_EL1), undef_access },
|
||||
IMPLEMENTATION_ID(AIDR_EL1, GENMASK_ULL(63, 0)),
|
||||
{ SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 },
|
||||
ID_FILTERED(CTR_EL0, ctr_el0,
|
||||
CTR_EL0_DIC_MASK |
|
||||
|
|
@ -4381,9 +4507,13 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu)
|
|||
* Certain AArch32 ID registers are handled by rerouting to the AArch64
|
||||
* system register table. Registers in the ID range where CRm=0 are
|
||||
* excluded from this scheme as they do not trivially map into AArch64
|
||||
* system register encodings.
|
||||
* system register encodings, except for AIDR/REVIDR.
|
||||
*/
|
||||
if (params.Op1 == 0 && params.CRn == 0 && params.CRm)
|
||||
if (params.Op1 == 0 && params.CRn == 0 &&
|
||||
(params.CRm || params.Op2 == 6 /* REVIDR */))
|
||||
return kvm_emulate_cp15_id_reg(vcpu, ¶ms);
|
||||
if (params.Op1 == 1 && params.CRn == 0 &&
|
||||
params.CRm == 0 && params.Op2 == 7 /* AIDR */)
|
||||
return kvm_emulate_cp15_id_reg(vcpu, ¶ms);
|
||||
|
||||
return kvm_handle_cp_32(vcpu, ¶ms, cp15_regs, ARRAY_SIZE(cp15_regs));
|
||||
|
|
@ -4687,65 +4817,6 @@ id_to_sys_reg_desc(struct kvm_vcpu *vcpu, u64 id,
|
|||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are the invariant sys_reg registers: we let the guest see the
|
||||
* host versions of these, so they're part of the guest state.
|
||||
*
|
||||
* A future CPU may provide a mechanism to present different values to
|
||||
* the guest, or a future kvm may trap them.
|
||||
*/
|
||||
|
||||
#define FUNCTION_INVARIANT(reg) \
|
||||
static u64 reset_##reg(struct kvm_vcpu *v, \
|
||||
const struct sys_reg_desc *r) \
|
||||
{ \
|
||||
((struct sys_reg_desc *)r)->val = read_sysreg(reg); \
|
||||
return ((struct sys_reg_desc *)r)->val; \
|
||||
}
|
||||
|
||||
FUNCTION_INVARIANT(midr_el1)
|
||||
FUNCTION_INVARIANT(revidr_el1)
|
||||
FUNCTION_INVARIANT(aidr_el1)
|
||||
|
||||
/* ->val is filled in by kvm_sys_reg_table_init() */
|
||||
static struct sys_reg_desc invariant_sys_regs[] __ro_after_init = {
|
||||
{ SYS_DESC(SYS_MIDR_EL1), NULL, reset_midr_el1 },
|
||||
{ SYS_DESC(SYS_REVIDR_EL1), NULL, reset_revidr_el1 },
|
||||
{ SYS_DESC(SYS_AIDR_EL1), NULL, reset_aidr_el1 },
|
||||
};
|
||||
|
||||
static int get_invariant_sys_reg(u64 id, u64 __user *uaddr)
|
||||
{
|
||||
const struct sys_reg_desc *r;
|
||||
|
||||
r = get_reg_by_id(id, invariant_sys_regs,
|
||||
ARRAY_SIZE(invariant_sys_regs));
|
||||
if (!r)
|
||||
return -ENOENT;
|
||||
|
||||
return put_user(r->val, uaddr);
|
||||
}
|
||||
|
||||
static int set_invariant_sys_reg(u64 id, u64 __user *uaddr)
|
||||
{
|
||||
const struct sys_reg_desc *r;
|
||||
u64 val;
|
||||
|
||||
r = get_reg_by_id(id, invariant_sys_regs,
|
||||
ARRAY_SIZE(invariant_sys_regs));
|
||||
if (!r)
|
||||
return -ENOENT;
|
||||
|
||||
if (get_user(val, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
/* This is what we mean by invariant: you can't change it. */
|
||||
if (r->val != val)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int demux_c15_get(struct kvm_vcpu *vcpu, u64 id, void __user *uaddr)
|
||||
{
|
||||
u32 val;
|
||||
|
|
@ -4827,15 +4898,10 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
|
|||
int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
void __user *uaddr = (void __user *)(unsigned long)reg->addr;
|
||||
int err;
|
||||
|
||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
||||
return demux_c15_get(vcpu, reg->id, uaddr);
|
||||
|
||||
err = get_invariant_sys_reg(reg->id, uaddr);
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
|
||||
return kvm_sys_reg_get_user(vcpu, reg,
|
||||
sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
}
|
||||
|
|
@ -4871,15 +4937,10 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
|
|||
int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
void __user *uaddr = (void __user *)(unsigned long)reg->addr;
|
||||
int err;
|
||||
|
||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
||||
return demux_c15_set(vcpu, reg->id, uaddr);
|
||||
|
||||
err = set_invariant_sys_reg(reg->id, uaddr);
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
|
||||
return kvm_sys_reg_set_user(vcpu, reg,
|
||||
sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
}
|
||||
|
|
@ -4968,23 +5029,14 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
|
|||
|
||||
unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return ARRAY_SIZE(invariant_sys_regs)
|
||||
+ num_demux_regs()
|
||||
return num_demux_regs()
|
||||
+ walk_sys_regs(vcpu, (u64 __user *)NULL);
|
||||
}
|
||||
|
||||
int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* Then give them all the invariant registers' indices. */
|
||||
for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++) {
|
||||
if (put_user(sys_reg_to_index(&invariant_sys_regs[i]), uindices))
|
||||
return -EFAULT;
|
||||
uindices++;
|
||||
}
|
||||
|
||||
err = walk_sys_regs(vcpu, uindices);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -5210,15 +5262,12 @@ int __init kvm_sys_reg_table_init(void)
|
|||
valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
|
||||
valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
|
||||
valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
|
||||
valid &= check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs), false);
|
||||
valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false);
|
||||
|
||||
if (!valid)
|
||||
return -EINVAL;
|
||||
|
||||
/* We abuse the reset function to overwrite the table itself. */
|
||||
for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
|
||||
invariant_sys_regs[i].reset(NULL, &invariant_sys_regs[i]);
|
||||
init_imp_id_regs();
|
||||
|
||||
ret = populate_nv_trap_config();
|
||||
|
||||
|
|
|
|||
|
|
@ -929,6 +929,7 @@ struct kvm_enable_cap {
|
|||
#define KVM_CAP_PRE_FAULT_MEMORY 236
|
||||
#define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237
|
||||
#define KVM_CAP_X86_GUEST_MODE 238
|
||||
#define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239
|
||||
|
||||
struct kvm_irq_routing_irqchip {
|
||||
__u32 irqchip;
|
||||
|
|
|
|||
|
|
@ -233,6 +233,9 @@ static void guest_code(void)
|
|||
GUEST_REG_SYNC(SYS_ID_AA64MMFR2_EL1);
|
||||
GUEST_REG_SYNC(SYS_ID_AA64ZFR0_EL1);
|
||||
GUEST_REG_SYNC(SYS_CTR_EL0);
|
||||
GUEST_REG_SYNC(SYS_MIDR_EL1);
|
||||
GUEST_REG_SYNC(SYS_REVIDR_EL1);
|
||||
GUEST_REG_SYNC(SYS_AIDR_EL1);
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
|
@ -612,18 +615,31 @@ static void test_ctr(struct kvm_vcpu *vcpu)
|
|||
test_reg_vals[encoding_to_range_idx(SYS_CTR_EL0)] = ctr;
|
||||
}
|
||||
|
||||
static void test_vcpu_ftr_id_regs(struct kvm_vcpu *vcpu)
|
||||
static void test_id_reg(struct kvm_vcpu *vcpu, u32 id)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(id));
|
||||
val++;
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(id), val);
|
||||
test_reg_vals[encoding_to_range_idx(id)] = val;
|
||||
}
|
||||
|
||||
static void test_vcpu_ftr_id_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
test_clidr(vcpu);
|
||||
test_ctr(vcpu);
|
||||
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1));
|
||||
val++;
|
||||
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), val);
|
||||
test_id_reg(vcpu, SYS_MPIDR_EL1);
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
test_id_reg(vcpu, SYS_MIDR_EL1);
|
||||
test_id_reg(vcpu, SYS_REVIDR_EL1);
|
||||
test_id_reg(vcpu, SYS_AIDR_EL1);
|
||||
|
||||
test_reg_vals[encoding_to_range_idx(SYS_MPIDR_EL1)] = val;
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
|
|
@ -650,6 +666,9 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
|
|||
test_assert_id_reg_unchanged(vcpu, SYS_MPIDR_EL1);
|
||||
test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1);
|
||||
test_assert_id_reg_unchanged(vcpu, SYS_CTR_EL0);
|
||||
test_assert_id_reg_unchanged(vcpu, SYS_MIDR_EL1);
|
||||
test_assert_id_reg_unchanged(vcpu, SYS_REVIDR_EL1);
|
||||
test_assert_id_reg_unchanged(vcpu, SYS_AIDR_EL1);
|
||||
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
|
@ -663,8 +682,11 @@ int main(void)
|
|||
int test_cnt;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_WRITABLE_IMP_ID_REGS));
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
vm = vm_create(1);
|
||||
vm_enable_cap(vm, KVM_CAP_ARM_WRITABLE_IMP_ID_REGS, 0);
|
||||
vcpu = vm_vcpu_add(vm, 0, guest_code);
|
||||
|
||||
/* Check for AARCH64 only system */
|
||||
val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1));
|
||||
|
|
@ -678,13 +700,14 @@ int main(void)
|
|||
ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
|
||||
ARRAY_SIZE(ftr_id_aa64pfr1_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) + 2 +
|
||||
ARRAY_SIZE(ftr_id_aa64zfr0_el1) - ARRAY_SIZE(test_regs) + 3 +
|
||||
MPAM_IDREG_TEST;
|
||||
|
||||
ksft_set_plan(test_cnt);
|
||||
|
||||
test_vm_ftr_id_regs(vcpu, aarch64_only);
|
||||
test_vcpu_ftr_id_regs(vcpu);
|
||||
test_vcpu_non_ftr_id_regs(vcpu);
|
||||
test_user_set_mpam_reg(vcpu);
|
||||
|
||||
test_guest_reg_read(vcpu);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user