mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
KVM/arm64 fixes for 7.0, take #1
- Make sure we don't leak any S1POE state from guest to guest when the feature is supported on the HW, but not enabled on the host - Propagate the ID registers from the host into non-protected VMs managed by pKVM, ensuring that the guest sees the intended feature set - Drop double kern_hyp_va() from unpin_host_sve_state(), which could bite us if we were to change kern_hyp_va() to not being idempotent - Don't leak stage-2 mappings in protected mode - Correctly align the faulting address when dealing with single page stage-2 mappings for PAGE_SIZE > 4kB - Fix detection of virtualisation-capable GICv5 IRS, due to the maintainer being obviously fat fingered... - Remove duplication of code retrieving the ASID for the purpose of S1 PT handling - Fix slightly abusive const-ification in vgic_set_kvm_info() -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAmmfIDAACgkQI9DQutE9 ekNw0w//aFrTa4yoUns0Jd1HeCMnKIP3Iljz1FR0g58KAgIX5tTvgysThhNN7yjW k7Xp03oD7K4UtxqJVQnB4nPTX0PYFFOxfcsHNN1+cxhepo6n9t/61KMa6IM/EE2X lH5Cypz4pxHEynMXEDugnGJQaMKIe5eU9UHs5rTqqUeMGupqUOk2mjBOk77CWfio oIvOIne4h7SD9tzYcgZADZ5YAtc1QcRVZkLTAtfsd2xRzNlpBDNAqVX8PJPzEW35 cffrEvT0NGBu+0MLBzc3bZ3cViGv5/8DHqiTcQhvK14dHMUIxl1GxUrkXVHb8Ca0 hoBEV7dJHS0avPQoKXb+DjB+HkxHda4g8EJ7RxWJisofS1AztWWDZOsZnfgTaaLB Ia0WekWra0yuEiYlU5kE/HPRlXAKUAa3mhih3uu5Uq1c5xuhCzHY7pyB9uNCdYfz KiSsTFG0i+T0ddT8E92LOFTt65G2wZyd498yVoVJ7MDOUSb+eESvgu5BPH4hiJLM txnDDvEvcHn5zlnayUItxk4SSHLZHFEP/tjfWVTrEpFW3s9O86aQlSqCsxOQReYZ ZVSa4Qr/aef8KzS4IMxFcSIMGkUoNjFIe0+SsVj/5vLoX6eK0ppE0GoQhmREU2cc hcicwGH5VYdJXhxJUvxCqtrmX+F14JlENmdXBZsE4N0b41pi7G8= =Ppfu -----END PGP SIGNATURE----- Merge tag 'kvmarm-fixes-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD KVM/arm64 fixes for 7.0, take #1 - Make sure we don't leak any S1POE state from guest to guest when the feature is supported on the HW, but not enabled on the host - Propagate the ID registers from the host into non-protected VMs managed by pKVM, ensuring that the guest sees the intended feature set - Drop double kern_hyp_va() from unpin_host_sve_state(), which could bite us if we were to change kern_hyp_va() to not being idempotent - Don't leak stage-2 mappings in protected mode - Correctly align the faulting address when dealing with single page stage-2 mappings for PAGE_SIZE > 4kB - Fix detection of virtualisation-capable GICv5 IRS, due to the maintainer being obviously fat fingered... - Remove duplication of code retrieving the ASID for the purpose of S1 PT handling - Fix slightly abusive const-ification in vgic_set_kvm_info()
This commit is contained in:
commit
55365ab85a
8 changed files with 80 additions and 69 deletions
|
|
@ -1616,7 +1616,8 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val);
|
|||
(kvm_has_feat((k), ID_AA64MMFR3_EL1, S1PIE, IMP))
|
||||
|
||||
#define kvm_has_s1poe(k) \
|
||||
(kvm_has_feat((k), ID_AA64MMFR3_EL1, S1POE, IMP))
|
||||
(system_supports_poe() && \
|
||||
kvm_has_feat((k), ID_AA64MMFR3_EL1, S1POE, IMP))
|
||||
|
||||
#define kvm_has_ras(k) \
|
||||
(kvm_has_feat((k), ID_AA64PFR0_EL1, RAS, IMP))
|
||||
|
|
|
|||
|
|
@ -397,6 +397,8 @@ int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu);
|
|||
int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu);
|
||||
void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val);
|
||||
|
||||
u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime);
|
||||
|
||||
#define vncr_fixmap(c) \
|
||||
({ \
|
||||
u32 __c = (c); \
|
||||
|
|
|
|||
|
|
@ -540,31 +540,8 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
|||
wr->pa |= va & GENMASK_ULL(va_bottom - 1, 0);
|
||||
|
||||
wr->nG = (wi->regime != TR_EL2) && (desc & PTE_NG);
|
||||
if (wr->nG) {
|
||||
u64 asid_ttbr, tcr;
|
||||
|
||||
switch (wi->regime) {
|
||||
case TR_EL10:
|
||||
tcr = vcpu_read_sys_reg(vcpu, TCR_EL1);
|
||||
asid_ttbr = ((tcr & TCR_A1) ?
|
||||
vcpu_read_sys_reg(vcpu, TTBR1_EL1) :
|
||||
vcpu_read_sys_reg(vcpu, TTBR0_EL1));
|
||||
break;
|
||||
case TR_EL20:
|
||||
tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
|
||||
asid_ttbr = ((tcr & TCR_A1) ?
|
||||
vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
|
||||
vcpu_read_sys_reg(vcpu, TTBR0_EL2));
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
wr->asid = FIELD_GET(TTBR_ASID_MASK, asid_ttbr);
|
||||
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
|
||||
!(tcr & TCR_ASID16))
|
||||
wr->asid &= GENMASK(7, 0);
|
||||
}
|
||||
if (wr->nG)
|
||||
wr->asid = get_asid_by_regime(vcpu, wi->regime);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -342,6 +342,7 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
|
|||
/* No restrictions for non-protected VMs. */
|
||||
if (!kvm_vm_is_protected(kvm)) {
|
||||
hyp_vm->kvm.arch.flags = host_arch_flags;
|
||||
hyp_vm->kvm.arch.flags &= ~BIT_ULL(KVM_ARCH_FLAG_ID_REGS_INITIALIZED);
|
||||
|
||||
bitmap_copy(kvm->arch.vcpu_features,
|
||||
host_kvm->arch.vcpu_features,
|
||||
|
|
@ -391,7 +392,7 @@ static void unpin_host_sve_state(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
if (!vcpu_has_feature(&hyp_vcpu->vcpu, KVM_ARM_VCPU_SVE))
|
||||
return;
|
||||
|
||||
sve_state = kern_hyp_va(hyp_vcpu->vcpu.arch.sve_state);
|
||||
sve_state = hyp_vcpu->vcpu.arch.sve_state;
|
||||
hyp_unpin_shared_mem(sve_state,
|
||||
sve_state + vcpu_sve_state_size(&hyp_vcpu->vcpu));
|
||||
}
|
||||
|
|
@ -471,6 +472,35 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int vm_copy_id_regs(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
|
||||
const struct kvm *host_kvm = hyp_vm->host_kvm;
|
||||
struct kvm *kvm = &hyp_vm->kvm;
|
||||
|
||||
if (!test_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &host_kvm->arch.flags))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_and_set_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags))
|
||||
return 0;
|
||||
|
||||
memcpy(kvm->arch.id_regs, host_kvm->arch.id_regs, sizeof(kvm->arch.id_regs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pkvm_vcpu_init_sysregs(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
kvm_init_pvm_id_regs(&hyp_vcpu->vcpu);
|
||||
else
|
||||
ret = vm_copy_id_regs(hyp_vcpu);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
|
||||
struct pkvm_hyp_vm *hyp_vm,
|
||||
struct kvm_vcpu *host_vcpu)
|
||||
|
|
@ -490,8 +520,9 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
|
|||
hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
|
||||
hyp_vcpu->vcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
|
||||
|
||||
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
kvm_init_pvm_id_regs(&hyp_vcpu->vcpu);
|
||||
ret = pkvm_vcpu_init_sysregs(hyp_vcpu);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
ret = pkvm_vcpu_init_traps(hyp_vcpu);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -1754,14 +1754,12 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
}
|
||||
|
||||
/*
|
||||
* Both the canonical IPA and fault IPA must be hugepage-aligned to
|
||||
* ensure we find the right PFN and lay down the mapping in the right
|
||||
* place.
|
||||
* Both the canonical IPA and fault IPA must be aligned to the
|
||||
* mapping size to ensure we find the right PFN and lay down the
|
||||
* mapping in the right place.
|
||||
*/
|
||||
if (vma_pagesize == PMD_SIZE || vma_pagesize == PUD_SIZE) {
|
||||
fault_ipa &= ~(vma_pagesize - 1);
|
||||
ipa &= ~(vma_pagesize - 1);
|
||||
}
|
||||
fault_ipa = ALIGN_DOWN(fault_ipa, vma_pagesize);
|
||||
ipa = ALIGN_DOWN(ipa, vma_pagesize);
|
||||
|
||||
gfn = ipa >> PAGE_SHIFT;
|
||||
mte_allowed = kvm_vma_mte_allowed(vma);
|
||||
|
|
|
|||
|
|
@ -854,6 +854,33 @@ int kvm_inject_s2_fault(struct kvm_vcpu *vcpu, u64 esr_el2)
|
|||
return kvm_inject_nested_sync(vcpu, esr_el2);
|
||||
}
|
||||
|
||||
u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime)
|
||||
{
|
||||
enum vcpu_sysreg ttbr_elx;
|
||||
u64 tcr;
|
||||
u16 asid;
|
||||
|
||||
switch (regime) {
|
||||
case TR_EL10:
|
||||
tcr = vcpu_read_sys_reg(vcpu, TCR_EL1);
|
||||
ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL1 : TTBR0_EL1;
|
||||
break;
|
||||
case TR_EL20:
|
||||
tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
|
||||
ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL2 : TTBR0_EL2;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
asid = FIELD_GET(TTBRx_EL1_ASID, vcpu_read_sys_reg(vcpu, ttbr_elx));
|
||||
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
|
||||
!(tcr & TCR_ASID16))
|
||||
asid &= GENMASK(7, 0);
|
||||
|
||||
return asid;
|
||||
}
|
||||
|
||||
static void invalidate_vncr(struct vncr_tlb *vt)
|
||||
{
|
||||
vt->valid = false;
|
||||
|
|
@ -1154,9 +1181,6 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!kvm->arch.nested_mmus_size)
|
||||
return;
|
||||
|
||||
for (i = 0; i < kvm->arch.nested_mmus_size; i++) {
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i];
|
||||
|
||||
|
|
@ -1336,20 +1360,8 @@ static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu)
|
|||
if (read_vncr_el2(vcpu) != vt->gva)
|
||||
return false;
|
||||
|
||||
if (vt->wr.nG) {
|
||||
u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
|
||||
u64 ttbr = ((tcr & TCR_A1) ?
|
||||
vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
|
||||
vcpu_read_sys_reg(vcpu, TTBR0_EL2));
|
||||
u16 asid;
|
||||
|
||||
asid = FIELD_GET(TTBR_ASID_MASK, ttbr);
|
||||
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
|
||||
!(tcr & TCR_ASID16))
|
||||
asid &= GENMASK(7, 0);
|
||||
|
||||
return asid == vt->wr.asid;
|
||||
}
|
||||
if (vt->wr.nG)
|
||||
return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1452,21 +1464,8 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
|
|||
if (read_vncr_el2(vcpu) != vt->gva)
|
||||
return;
|
||||
|
||||
if (vt->wr.nG) {
|
||||
u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
|
||||
u64 ttbr = ((tcr & TCR_A1) ?
|
||||
vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
|
||||
vcpu_read_sys_reg(vcpu, TTBR0_EL2));
|
||||
u16 asid;
|
||||
|
||||
asid = FIELD_GET(TTBR_ASID_MASK, ttbr);
|
||||
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
|
||||
!(tcr & TCR_ASID16))
|
||||
asid &= GENMASK(7, 0);
|
||||
|
||||
if (asid != vt->wr.asid)
|
||||
return;
|
||||
}
|
||||
if (vt->wr.nG && get_asid_by_regime(vcpu, TR_EL20) != vt->wr.asid)
|
||||
return;
|
||||
|
||||
vt->cpu = smp_processor_id();
|
||||
|
||||
|
|
|
|||
|
|
@ -1816,6 +1816,9 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
|
|||
ID_AA64MMFR3_EL1_SCTLRX |
|
||||
ID_AA64MMFR3_EL1_S1POE |
|
||||
ID_AA64MMFR3_EL1_S1PIE;
|
||||
|
||||
if (!system_supports_poe())
|
||||
val &= ~ID_AA64MMFR3_EL1_S1POE;
|
||||
break;
|
||||
case SYS_ID_MMFR4_EL1:
|
||||
val &= ~ID_MMFR4_EL1_CCIDX;
|
||||
|
|
|
|||
|
|
@ -699,7 +699,7 @@ static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data)
|
|||
*/
|
||||
if (list_empty(&irs_nodes)) {
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR0);
|
||||
gicv5_global_data.virt_capable = !FIELD_GET(GICV5_IRS_IDR0_VIRT, idr);
|
||||
gicv5_global_data.virt_capable = !!FIELD_GET(GICV5_IRS_IDR0_VIRT, idr);
|
||||
|
||||
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
|
||||
irs_setup_pri_bits(idr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue