summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv/include/asm/kvm_gstage.h3
-rw-r--r--arch/riscv/kvm/gstage.c59
-rw-r--r--arch/riscv/kvm/mmu.c2
3 files changed, 63 insertions, 1 deletions
diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index f72627978017..aaf080ba1b77 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -82,6 +82,9 @@ bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,
bool kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end);
+bool kvm_riscv_gstage_wp_pt_masked(struct kvm_gstage *gstage, gfn_t base_gfn,
+ unsigned long mask);
+
void kvm_riscv_gstage_mode_detect(void);
static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels)
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 01679aef5328..e5002cb9cbef 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -485,6 +485,65 @@ bool kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
return flush;
}
+static inline void clear_huge_mask(unsigned long *mask, unsigned long page_size,
+ gfn_t base_gfn, gpa_t addr)
+{
+ unsigned long start_index = 0;
+ unsigned long end_index = BITS_PER_LONG - 1;
+ unsigned long end_gfn = base_gfn + end_index;
+ unsigned long aligned_start_gfn = addr >> PAGE_SHIFT;
+ unsigned long aligned_end_gfn = aligned_start_gfn + (page_size >> PAGE_SHIFT) - 1;
+ unsigned int nbits = 0;
+
+ if (aligned_start_gfn > base_gfn)
+ start_index = aligned_start_gfn - base_gfn;
+
+ if (aligned_end_gfn < end_gfn)
+ end_index = aligned_end_gfn - base_gfn;
+
+ nbits = end_index - start_index + 1;
+ bitmap_clear(mask, start_index, nbits);
+}
+
+bool kvm_riscv_gstage_wp_pt_masked(struct kvm_gstage *gstage, gfn_t base_gfn,
+ unsigned long mask)
+{
+ unsigned long page_size;
+ bool flush = false;
+ bool found_leaf;
+ u32 ptep_level;
+ pte_t *ptep;
+ gpa_t addr = 0;
+ int ret;
+
+ while (mask) {
+ addr = (base_gfn + __ffs(mask)) << PAGE_SHIFT;
+
+ found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level);
+ ret = gstage_level_to_page_size(gstage, ptep_level, &page_size);
+ if (ret)
+ break;
+
+ if (found_leaf) {
+ if (ptep_level) {
+ addr = ALIGN_DOWN(addr, page_size);
+ clear_huge_mask(&mask, page_size, base_gfn, addr);
+ }
+
+ flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep,
+ ptep_level, GSTAGE_OP_WP);
+
+ if (ptep_level)
+ continue;
+ }
+
+ /* clear the first set bit*/
+ mask &= mask - 1;
+ }
+
+ return flush;
+}
+
void __init kvm_riscv_gstage_mode_detect(void)
{
#ifdef CONFIG_64BIT
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 496cd9ede5cd..d9040c394933 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -169,7 +169,7 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
kvm_riscv_gstage_init(&gstage, kvm);
- kvm_riscv_gstage_wp_range(&gstage, start, end);
+ kvm_riscv_gstage_wp_pt_masked(&gstage, base_gfn, mask);
if (kvm_dirty_log_manual_protect_and_init_set(kvm)) {
if (READ_ONCE(eager_page_split))