[PATCH 08/15] KVM: x86/mmu: Implement PWALK_FORCE_SET_ACCESSED in page walker

Nikolas Wipper
2024-09-10T15:24:43Z

Implement PWALK_FORCE_SET_ACCESSED in the page walker. This flag forces
the page walker to set the accessed flag in all successfully visited page
table levels, regardless of the outcome of the page walk.

For example, if the page walk fails on level 2, the accessed bit will
still be set on levels 3 and up.

If the nested translations of GPAs fail, the bits will still be set.

Signed-off-by: Nikolas Wipper <nikwip@amazon.de>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/mmu/paging_tmpl.h | 17 +++++++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3acf0b069693..cd2c391d6a24 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -287,6 +287,7 @@ enum x86_intercept_stage;

#define PWALK_SET_ACCESSED BIT(0)
#define PWALK_SET_DIRTY BIT(1)
+#define PWALK_FORCE_SET_ACCESSED BIT(2)
#define PWALK_SET_ALL (PWALK_SET_ACCESSED | PWALK_SET_DIRTY)

/* apic attention bits */
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
index b6897f7fbf52..2cc40fd17f53 100644
--- a/arch/x86/kvm/mmu/paging_tmpl.h
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
@@ -319,6 +319,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
const int fetch_fault = access & PFERR_FETCH_MASK;
const int set_accessed = flags & PWALK_SET_ACCESSED;
const int set_dirty = flags & PWALK_SET_DIRTY;
+ const int force_set = flags & PWALK_FORCE_SET_ACCESSED;
u16 errcode = 0;
gpa_t real_gpa;
gfn_t gfn;
@@ -395,7 +396,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
* fields.
*/
if (unlikely(real_gpa == INVALID_GPA))
- return 0;
+ goto late_exit;

slot = kvm_vcpu_gfn_to_memslot(vcpu, gpa_to_gfn(real_gpa));
if (!kvm_is_visible_memslot(slot)) {
@@ -455,7 +456,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(gfn), access,
flags, &walker->fault);
if (real_gpa == INVALID_GPA)
- return 0;
+ goto late_exit;

walker->gfn = real_gpa >> PAGE_SHIFT;

@@ -528,6 +529,18 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->fault.async_page_fault = false;

trace_kvm_mmu_walker_error(walker->fault.error_code);
+
+late_exit:
+ if (force_set) {
+ /*
+ * Don't set the accessed bit for the page table that caused the
+ * walk to fail.
+ */
+ ++walker->level;
+ FNAME(update_accessed_dirty_bits)(vcpu, mmu, walker, addr,
+ false);
+ --walker->level;
+ }
return 0;
}