Skip to content

Commit c3a62df

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/pgtable-fixes-6.4 into kvmarm-master/fixes
* kvm-arm64/pgtable-fixes-6.4: : . : Fixes for concurrent S2 mapping race from Oliver: : : "So it appears that there is a race between two parallel stage-2 map : walkers that could lead to mapping the incorrect PA for a given IPA, as : the IPA -> PA relationship picks up an unintended offset. This series : eliminates the problem by using the current IPA of the walk as the : source-of-truth regarding where we are in a map operation." : . KVM: arm64: Constify start/end/phys fields of the pgtable walker data KVM: arm64: Infer PA offset from VA in hyp map walker KVM: arm64: Infer the PA offset from IPA in stage-2 map walker Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents 9a48c59 + 1ea2441 commit c3a62df

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ struct kvm_pgtable_visit_ctx {
209209
kvm_pte_t old;
210210
void *arg;
211211
struct kvm_pgtable_mm_ops *mm_ops;
212+
u64 start;
212213
u64 addr;
213214
u64 end;
214215
u32 level;

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@
5858
struct kvm_pgtable_walk_data {
5959
struct kvm_pgtable_walker *walker;
6060

61+
const u64 start;
6162
u64 addr;
62-
u64 end;
63+
const u64 end;
6364
};
6465

6566
static bool kvm_phys_is_valid(u64 phys)
@@ -201,6 +202,7 @@ static inline int __kvm_pgtable_visit(struct kvm_pgtable_walk_data *data,
201202
.old = READ_ONCE(*ptep),
202203
.arg = data->walker->arg,
203204
.mm_ops = mm_ops,
205+
.start = data->start,
204206
.addr = data->addr,
205207
.end = data->end,
206208
.level = level,
@@ -293,6 +295,7 @@ int kvm_pgtable_walk(struct kvm_pgtable *pgt, u64 addr, u64 size,
293295
struct kvm_pgtable_walker *walker)
294296
{
295297
struct kvm_pgtable_walk_data walk_data = {
298+
.start = ALIGN_DOWN(addr, PAGE_SIZE),
296299
.addr = ALIGN_DOWN(addr, PAGE_SIZE),
297300
.end = PAGE_ALIGN(walk_data.addr + size),
298301
.walker = walker,
@@ -349,7 +352,7 @@ int kvm_pgtable_get_leaf(struct kvm_pgtable *pgt, u64 addr,
349352
}
350353

351354
struct hyp_map_data {
352-
u64 phys;
355+
const u64 phys;
353356
kvm_pte_t attr;
354357
};
355358

@@ -407,13 +410,12 @@ enum kvm_pgtable_prot kvm_pgtable_hyp_pte_prot(kvm_pte_t pte)
407410
static bool hyp_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx,
408411
struct hyp_map_data *data)
409412
{
413+
u64 phys = data->phys + (ctx->addr - ctx->start);
410414
kvm_pte_t new;
411-
u64 granule = kvm_granule_size(ctx->level), phys = data->phys;
412415

413416
if (!kvm_block_mapping_supported(ctx, phys))
414417
return false;
415418

416-
data->phys += granule;
417419
new = kvm_init_valid_leaf_pte(phys, data->attr, ctx->level);
418420
if (ctx->old == new)
419421
return true;
@@ -576,7 +578,7 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt)
576578
}
577579

578580
struct stage2_map_data {
579-
u64 phys;
581+
const u64 phys;
580582
kvm_pte_t attr;
581583
u8 owner_id;
582584

@@ -794,20 +796,43 @@ static bool stage2_pte_executable(kvm_pte_t pte)
794796
return !(pte & KVM_PTE_LEAF_ATTR_HI_S2_XN);
795797
}
796798

799+
static u64 stage2_map_walker_phys_addr(const struct kvm_pgtable_visit_ctx *ctx,
800+
const struct stage2_map_data *data)
801+
{
802+
u64 phys = data->phys;
803+
804+
/*
805+
* Stage-2 walks to update ownership data are communicated to the map
806+
* walker using an invalid PA. Avoid offsetting an already invalid PA,
807+
* which could overflow and make the address valid again.
808+
*/
809+
if (!kvm_phys_is_valid(phys))
810+
return phys;
811+
812+
/*
813+
* Otherwise, work out the correct PA based on how far the walk has
814+
* gotten.
815+
*/
816+
return phys + (ctx->addr - ctx->start);
817+
}
818+
797819
static bool stage2_leaf_mapping_allowed(const struct kvm_pgtable_visit_ctx *ctx,
798820
struct stage2_map_data *data)
799821
{
822+
u64 phys = stage2_map_walker_phys_addr(ctx, data);
823+
800824
if (data->force_pte && (ctx->level < (KVM_PGTABLE_MAX_LEVELS - 1)))
801825
return false;
802826

803-
return kvm_block_mapping_supported(ctx, data->phys);
827+
return kvm_block_mapping_supported(ctx, phys);
804828
}
805829

806830
static int stage2_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx,
807831
struct stage2_map_data *data)
808832
{
809833
kvm_pte_t new;
810-
u64 granule = kvm_granule_size(ctx->level), phys = data->phys;
834+
u64 phys = stage2_map_walker_phys_addr(ctx, data);
835+
u64 granule = kvm_granule_size(ctx->level);
811836
struct kvm_pgtable *pgt = data->mmu->pgt;
812837
struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops;
813838

@@ -841,8 +866,6 @@ static int stage2_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx,
841866

842867
stage2_make_pte(ctx, new);
843868

844-
if (kvm_phys_is_valid(phys))
845-
data->phys += granule;
846869
return 0;
847870
}
848871

0 commit comments

Comments
 (0)