mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
Merge branch 'kvm-arm64/vncr-fixes' into next
* kvm-arm64/vncr-fixes: : Fixes for handling L1 VNCR mappings, courtesy of Marc Zyngier : : - Recompute the VNCR software TLB when the MMU is toggled at stage-1 : : - Improve TLB invalidation intersection to handle TLBIs affecting the : end of the VA space : : - Fix race to invalidate the VNCR fixmap between TLBI emulation and : vcpu_put() : : - Add missing sign extension for computing TLBI ranges : : - Make VNCR invalidation participate in the MMU notifier seqcount, : preventing a concurrent VNCR TLB fill from consuming a stale : translation KVM: arm64: Correctly cap TLBI Range to the architural limit KVM: arm64: Add VNCR TLB tracking again KVM: arm64: Make VNCR invalidation participate in MMU invalidation retry KVM: arm64: Sign-extend VA for range-based TLBI invalidation KVM: arm64: Handle VNCR TLB invalidation race with vcpu_put() VNCR unmapping KVM: arm64: Correctly handle end of VA space TLBI invalidation KVM: arm64: Consider SCTLR_EL2.M when mapping the L1 VNCR page KVM: arm64: Handle negative S1 walk levels in VNCR TLB size evaluation KVM: arm64: Remove VM-wide VNCR mapping counter Signed-off-by: Oliver Upton <oupton@kernel.org>
This commit is contained in:
commit
c75d616c08
|
|
@ -414,8 +414,8 @@ struct kvm_arch {
|
|||
/* Masks for VNCR-backed and general EL2 sysregs */
|
||||
struct kvm_sysreg_masks *sysreg_masks;
|
||||
|
||||
/* Count the number of VNCR_EL2 currently mapped */
|
||||
atomic_t vncr_map_count;
|
||||
/* Count the number of VNCR_EL2 TLBs */
|
||||
atomic_t vncr_tlb_count;
|
||||
|
||||
/*
|
||||
* For an untrusted host VM, 'pkvm.handle' is used to lookup
|
||||
|
|
|
|||
|
|
@ -291,6 +291,13 @@ static inline u64 decode_range_tlbi(u64 val, u64 *range, u16 *asid)
|
|||
|
||||
base = (val & GENMASK(36, 0)) << shift;
|
||||
|
||||
/*
|
||||
* We only deal with at most 48bit VA/IPA, so 48 is where we
|
||||
* sign-extend from. Should we support FEAT_L{VP}A* at some point,
|
||||
* this will need to be revisited.
|
||||
*/
|
||||
base = (u64)sign_extend64(base, 48);
|
||||
|
||||
if (asid)
|
||||
*asid = FIELD_GET(TLBIR_ASID_MASK, val);
|
||||
|
||||
|
|
@ -298,6 +305,12 @@ static inline u64 decode_range_tlbi(u64 val, u64 *range, u16 *asid)
|
|||
num = FIELD_GET(GENMASK(43, 39), val);
|
||||
*range = __TLBI_RANGE_PAGES(num, scale) << shift;
|
||||
|
||||
/* Cap the range to the correct half of the address space */
|
||||
if (!(base & BIT(48)))
|
||||
*range = min(*range, (BIT(48) - base));
|
||||
else
|
||||
*range = min(*range, ~base + 1);
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
|
|
@ -388,6 +401,8 @@ struct s1_walk_result {
|
|||
bool failed;
|
||||
};
|
||||
|
||||
#define S1_MMU_DISABLED (-127)
|
||||
|
||||
static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
|
||||
{
|
||||
wr->fst = fst;
|
||||
|
|
@ -396,6 +411,11 @@ static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw)
|
|||
wr->failed = true;
|
||||
}
|
||||
|
||||
static inline bool s1_walk_translated(struct s1_walk_result *wr)
|
||||
{
|
||||
return wr->level != S1_MMU_DISABLED;
|
||||
}
|
||||
|
||||
int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
struct s1_walk_result *wr, u64 va);
|
||||
int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/lsui.h>
|
||||
|
||||
#define S1_MMU_DISABLED (-127)
|
||||
|
||||
static int get_ia_size(struct s1_walk_info *wi)
|
||||
{
|
||||
return 64 - wi->txsz;
|
||||
|
|
|
|||
|
|
@ -436,11 +436,15 @@ static bool kvm_hyp_handle_tlbi_el2(struct kvm_vcpu *vcpu, u64 *exit_code)
|
|||
return false;
|
||||
|
||||
/*
|
||||
* If we have to check for any VNCR mapping being invalidated,
|
||||
* go back to the slow path for further processing.
|
||||
* If we have to check for any VNCR TLB being invalidated, go back
|
||||
* to the slow path for further processing.
|
||||
*
|
||||
* The synchronisation betweem TLBI and walk is provided by the
|
||||
* speculative increment of the TLB counter on walk, and the
|
||||
* invalidation counter. Yes, this is fiddly.
|
||||
*/
|
||||
if (vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu) &&
|
||||
atomic_read(&vcpu->kvm->arch.vncr_map_count))
|
||||
atomic_read(&vcpu->kvm->arch.vncr_tlb_count))
|
||||
return false;
|
||||
|
||||
__kvm_skip_instr(vcpu);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ struct vncr_tlb {
|
|||
bool hpa_writable;
|
||||
|
||||
/* -1 when not mapped on a CPU */
|
||||
int cpu;
|
||||
atomic_t cpu;
|
||||
|
||||
/*
|
||||
* true if the TLB is valid. Can only be changed with the
|
||||
|
|
@ -48,7 +48,7 @@ void kvm_init_nested(struct kvm *kvm)
|
|||
{
|
||||
kvm->arch.nested_mmus = NULL;
|
||||
kvm->arch.nested_mmus_size = 0;
|
||||
atomic_set(&kvm->arch.vncr_map_count, 0);
|
||||
atomic_set(&kvm->arch.vncr_tlb_count, 0);
|
||||
}
|
||||
|
||||
static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
|
||||
|
|
@ -506,7 +506,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ttl_to_size(u8 ttl)
|
||||
static unsigned int __ttl_to_size(u8 ttl)
|
||||
{
|
||||
int level = ttl & 3;
|
||||
int gran = (ttl >> 2) & 3;
|
||||
|
|
@ -562,10 +562,22 @@ static unsigned int ttl_to_size(u8 ttl)
|
|||
return max_size;
|
||||
}
|
||||
|
||||
static u8 pgshift_level_to_ttl(u16 shift, u8 level)
|
||||
static unsigned int ttl_to_size(u8 ttl)
|
||||
{
|
||||
return __ttl_to_size(ttl) ?: SZ_1G;
|
||||
}
|
||||
|
||||
static u8 pgshift_level_to_ttl(u16 shift, s8 level)
|
||||
{
|
||||
u8 ttl;
|
||||
|
||||
/*
|
||||
* If we don't have a proper level, fallback to the maximum
|
||||
* size.
|
||||
*/
|
||||
if (level < 0)
|
||||
return 0;
|
||||
|
||||
switch(shift) {
|
||||
case 12:
|
||||
ttl = TLBI_TTL_TG_4K;
|
||||
|
|
@ -676,7 +688,11 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)
|
|||
ttl = get_guest_mapping_ttl(mmu, addr);
|
||||
}
|
||||
|
||||
max_size = ttl_to_size(ttl);
|
||||
/*
|
||||
* Don't use the default 1GB fallback, as we can adapt to the
|
||||
* max mapping size we allow at S2.
|
||||
*/
|
||||
max_size = __ttl_to_size(ttl);
|
||||
|
||||
if (!max_size) {
|
||||
/* Compute the maximum extent of the invalidation */
|
||||
|
|
@ -879,18 +895,41 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmapping an L1 VNCR can happen concurrently without the mmu lock being
|
||||
* effective (vcpu_put() vs TLBI handling). The atomic_xchg below ensures
|
||||
* that only one CPU sets it to -1 while getting a valid CPU number back.
|
||||
*/
|
||||
static int unmap_l1_vncr(struct vncr_tlb *vt)
|
||||
{
|
||||
int cpu = atomic_xchg_relaxed(&vt->cpu, -1);
|
||||
|
||||
if (cpu != -1)
|
||||
clear_fixmap(vncr_fixmap(cpu));
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!host_data_test_flag(L1_VNCR_MAPPED))
|
||||
return;
|
||||
|
||||
BUG_ON(vcpu->arch.vncr_tlb->cpu != smp_processor_id());
|
||||
BUG_ON(is_hyp_ctxt(vcpu));
|
||||
|
||||
clear_fixmap(vncr_fixmap(vcpu->arch.vncr_tlb->cpu));
|
||||
vcpu->arch.vncr_tlb->cpu = -1;
|
||||
/*
|
||||
* Unconditionally unmap the local VNCR if we have lost the race
|
||||
* against a concurrent TLBI. Otherwise we could end-up running
|
||||
* another vcpu with VNCR still mapped if the TLBI thread is
|
||||
* preempted between the exchange and the clear_fixmap().
|
||||
*
|
||||
* Note that we do not care about the TLBI nuking the fixmap behind
|
||||
* the back of an running vcpu. This will only generate a fault and
|
||||
* possibly a retranslation.
|
||||
*/
|
||||
if (unmap_l1_vncr(vcpu->arch.vncr_tlb) == -1)
|
||||
clear_fixmap(vncr_fixmap(smp_processor_id()));
|
||||
host_data_clear_flag(L1_VNCR_MAPPED);
|
||||
atomic_dec(&vcpu->kvm->arch.vncr_map_count);
|
||||
}
|
||||
|
||||
void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu)
|
||||
|
|
@ -978,11 +1017,26 @@ u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime)
|
|||
return asid;
|
||||
}
|
||||
|
||||
static void invalidate_vncr(struct vncr_tlb *vt)
|
||||
static void invalidate_vncr(struct kvm *kvm, struct vncr_tlb *vt)
|
||||
{
|
||||
BUG_ON(!vt->valid);
|
||||
vt->valid = false;
|
||||
if (vt->cpu != -1)
|
||||
clear_fixmap(vncr_fixmap(vt->cpu));
|
||||
unmap_l1_vncr(vt);
|
||||
atomic_dec(&kvm->arch.vncr_tlb_count);
|
||||
}
|
||||
|
||||
static bool vncr_tlb_intersects(struct vncr_tlb *vt, u64 addr,
|
||||
u64 scope_start, u64 scope_size)
|
||||
{
|
||||
u64 tlb_size, tlb_start, tlb_end, scope_end;
|
||||
|
||||
tlb_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level));
|
||||
|
||||
tlb_start = addr & ~(tlb_size - 1);
|
||||
tlb_end = tlb_start + tlb_size - 1;
|
||||
scope_end = scope_start + scope_size - 1;
|
||||
|
||||
return !(tlb_end < scope_start || tlb_start > scope_end);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1007,19 +1061,15 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
|
|||
if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY))
|
||||
return;
|
||||
|
||||
kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
|
||||
u64 ipa_start, ipa_end, ipa_size;
|
||||
|
||||
ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
|
||||
vt->wr.level));
|
||||
ipa_start = vt->wr.pa & ~(ipa_size - 1);
|
||||
ipa_end = ipa_start + ipa_size;
|
||||
|
||||
if (ipa_end <= start || ipa_start >= end)
|
||||
continue;
|
||||
|
||||
invalidate_vncr(vt);
|
||||
}
|
||||
/*
|
||||
* Note that invalidating the VNCR on the back of an MMU notifier
|
||||
* doesn't require messing with the invalidation counter for a
|
||||
* parallel walk. The notifier itself will have bumped the counter,
|
||||
* making sure we rewalk.
|
||||
*/
|
||||
kvm_for_each_vncr_tlb(i, vcpu, vt, kvm)
|
||||
if (vncr_tlb_intersects(vt, vt->wr.pa, start, end - start))
|
||||
invalidate_vncr(kvm, vt);
|
||||
}
|
||||
|
||||
struct s1e2_tlbi_scope {
|
||||
|
|
@ -1044,29 +1094,29 @@ static void invalidate_vncr_va(struct kvm *kvm,
|
|||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
|
||||
/*
|
||||
* We might be performing a parallel S1 walk, so bump up the
|
||||
* invalidation counter even in the absence of an actual VNCR TLB
|
||||
* invalidation, as this could indicate that the guest has gone
|
||||
* through a BBM sequence.
|
||||
*/
|
||||
kvm->mmu_invalidate_seq++;
|
||||
smp_wmb();
|
||||
|
||||
kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
|
||||
u64 va_start, va_end, va_size;
|
||||
|
||||
va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
|
||||
vt->wr.level));
|
||||
va_start = vt->gva & ~(va_size - 1);
|
||||
va_end = va_start + va_size;
|
||||
|
||||
switch (scope->type) {
|
||||
case TLBI_ALL:
|
||||
break;
|
||||
|
||||
case TLBI_VA:
|
||||
if (va_end <= scope->va ||
|
||||
va_start >= (scope->va + scope->size))
|
||||
if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size))
|
||||
continue;
|
||||
if (vt->wr.nG && vt->wr.asid != scope->asid)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case TLBI_VAA:
|
||||
if (va_end <= scope->va ||
|
||||
va_start >= (scope->va + scope->size))
|
||||
if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size))
|
||||
continue;
|
||||
break;
|
||||
|
||||
|
|
@ -1076,7 +1126,7 @@ static void invalidate_vncr_va(struct kvm *kvm,
|
|||
break;
|
||||
}
|
||||
|
||||
invalidate_vncr(vt);
|
||||
invalidate_vncr(kvm, vt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1126,8 +1176,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,
|
|||
case OP_TLBI_VALE1OSNXS:
|
||||
scope->type = TLBI_VA;
|
||||
scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));
|
||||
if (!scope->size)
|
||||
scope->size = SZ_1G;
|
||||
scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1);
|
||||
scope->asid = FIELD_GET(TLBIR_ASID_MASK, val);
|
||||
break;
|
||||
|
|
@ -1154,8 +1202,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val,
|
|||
case OP_TLBI_VAALE1OSNXS:
|
||||
scope->type = TLBI_VAA;
|
||||
scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val));
|
||||
if (!scope->size)
|
||||
scope->size = SZ_1G;
|
||||
scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1);
|
||||
break;
|
||||
case OP_TLBI_RVAE2:
|
||||
|
|
@ -1316,13 +1362,20 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
|||
* intersects with the TLBI request, invalidate it, and unmap the page
|
||||
* from the fixmap. Because we need to look at all the vcpu-private TLBs,
|
||||
* this requires some wide-ranging locking to ensure that nothing races
|
||||
* against it. This may require some refcounting to avoid the search when
|
||||
* no such TLB is present.
|
||||
* against it. This requires some refcounting to avoid the search when
|
||||
* no such TLB is present (see below).
|
||||
*
|
||||
* - On MMU notifiers, we must invalidate our TLB in a similar way, but
|
||||
* looking at the IPA instead. The funny part is that there may not be a
|
||||
* stage-2 mapping for this page if L1 hasn't accessed it using LD/ST
|
||||
* instructions.
|
||||
*
|
||||
* - vncr_tlb_count tracks the number of valid VNCR TLBs VM-wide. This isn't
|
||||
* the number of *mapped* L1 VNCR pages, which is likely be a subset (and
|
||||
* by definition, a TLBI handled from L1 runs with the canonical VNCR
|
||||
* page, not the L1's). The innermost trap handling code checks this to
|
||||
* find out whether to return to the guest ASAP (no L1 TLBs) or to visit
|
||||
* this part of the world for some extra invalidation work.
|
||||
*/
|
||||
|
||||
int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)
|
||||
|
|
@ -1377,7 +1430,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
|
|||
*/
|
||||
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
|
||||
this_cpu_reset_vncr_fixmap(vcpu);
|
||||
vt->valid = false;
|
||||
if (vt->valid)
|
||||
invalidate_vncr(vcpu->kvm, vt);
|
||||
|
||||
vt->wi = (struct s1_walk_info) {
|
||||
.regime = TR_EL20,
|
||||
|
|
@ -1391,15 +1445,15 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
|
|||
|
||||
va = read_vncr_el2(vcpu);
|
||||
|
||||
mmu_seq = vcpu->kvm->mmu_invalidate_seq;
|
||||
smp_rmb();
|
||||
|
||||
ret = __kvm_translate_va(vcpu, &vt->wi, &vt->wr, va);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
write_fault = kvm_is_write_fault(vcpu);
|
||||
|
||||
mmu_seq = vcpu->kvm->mmu_invalidate_seq;
|
||||
smp_rmb();
|
||||
|
||||
gfn = vt->wr.pa >> PAGE_SHIFT;
|
||||
memslot = gfn_to_memslot(vcpu->kvm, gfn);
|
||||
if (!memslot) {
|
||||
|
|
@ -1447,7 +1501,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
|
|||
vt->hpa = pfn << PAGE_SHIFT;
|
||||
vt->hpa_writable = writable;
|
||||
vt->valid = true;
|
||||
vt->cpu = -1;
|
||||
atomic_set(&vt->cpu, -1);
|
||||
|
||||
kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);
|
||||
kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable);
|
||||
|
|
@ -1502,7 +1556,20 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Speculatively increment the TLB count to make sure concurrent
|
||||
* TLBIs will take the slow path, and will interact with the retry
|
||||
* mechanism. Drop it again on error.
|
||||
*/
|
||||
atomic_inc(&vcpu->kvm->arch.vncr_tlb_count);
|
||||
smp_mb__after_atomic();
|
||||
|
||||
ret = kvm_translate_vncr(vcpu, &is_gmem);
|
||||
if (ret) {
|
||||
smp_mb__before_atomic();
|
||||
atomic_dec(&vcpu->kvm->arch.vncr_tlb_count);
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case -EAGAIN:
|
||||
/* Let's try again... */
|
||||
|
|
@ -1568,14 +1635,16 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
|
|||
if (!vt->valid)
|
||||
return;
|
||||
|
||||
/* We cache the MMU state in the TLB. Check that it matches. */
|
||||
if (!!(vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_M) != s1_walk_translated(&vt->wr))
|
||||
return;
|
||||
|
||||
if (read_vncr_el2(vcpu) != vt->gva)
|
||||
return;
|
||||
|
||||
if (vt->wr.nG && get_asid_by_regime(vcpu, TR_EL20) != vt->wr.asid)
|
||||
return;
|
||||
|
||||
vt->cpu = smp_processor_id();
|
||||
|
||||
if (vt->hpa_writable && vt->wr.pw && vt->wr.pr)
|
||||
prot = PAGE_KERNEL;
|
||||
else if (vt->wr.pr)
|
||||
|
|
@ -1590,9 +1659,9 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu)
|
|||
* FIXME: WO doesn't work at all, need POE support in the kernel.
|
||||
*/
|
||||
if (pgprot_val(prot) != pgprot_val(PAGE_NONE)) {
|
||||
__set_fixmap(vncr_fixmap(vt->cpu), vt->hpa, prot);
|
||||
atomic_set(&vt->cpu, smp_processor_id());
|
||||
__set_fixmap(vncr_fixmap(atomic_read(&vt->cpu)), vt->hpa, prot);
|
||||
host_data_set_flag(L1_VNCR_MAPPED);
|
||||
atomic_inc(&vcpu->kvm->arch.vncr_map_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4154,6 +4154,7 @@ static bool handle_ripas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||
u32 sys_encoding = sys_insn(p->Op0, p->Op1, p->CRn, p->CRm, p->Op2);
|
||||
u64 vttbr = vcpu_read_sys_reg(vcpu, VTTBR_EL2);
|
||||
u64 base, range;
|
||||
int pa_bits;
|
||||
|
||||
if (!kvm_supported_tlbi_ipas2_op(vcpu, sys_encoding))
|
||||
return undef_access(vcpu, p, r);
|
||||
|
|
@ -4165,6 +4166,16 @@ static bool handle_ripas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||
*/
|
||||
base = decode_range_tlbi(p->regval, &range, NULL);
|
||||
|
||||
/*
|
||||
* Ignore TLBIs that start out of PA_bits range, and cap the
|
||||
* invalidation to the [base:bit(PA_bits)] interval.
|
||||
*/
|
||||
pa_bits = kvm_get_pa_bits(vcpu->kvm);
|
||||
if (fls64(base) > pa_bits)
|
||||
return true;
|
||||
|
||||
range = min(range, BIT_ULL(pa_bits) - base);
|
||||
|
||||
kvm_s2_mmu_iterate_by_vmid(vcpu->kvm, get_vmid(vttbr),
|
||||
&(union tlbi_info) {
|
||||
.range = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user