1 // SPDX-License-Identifier: GPL-2.0-only
3 * Debug helper used to dump the stage-2 pagetables of the system and their
4 * associated permissions.
6 * Copyright (C) Google, 2024
7 * Author: Sebastian Ene <sebastianene@google.com>
9 #include <linux/debugfs.h>
10 #include <linux/kvm_host.h>
11 #include <linux/seq_file.h>
13 #include <asm/kvm_mmu.h>
14 #include <asm/kvm_pgtable.h>
15 #include <asm/ptdump.h>
18 #define KVM_PGTABLE_MAX_LEVELS (KVM_PGTABLE_LAST_LEVEL + 1)
20 struct kvm_ptdump_guest_state
{
22 struct ptdump_pg_state parser_state
;
23 struct addr_marker ipa_marker
[MARKERS_LEN
];
24 struct ptdump_pg_level level
[KVM_PGTABLE_MAX_LEVELS
];
25 struct ptdump_range range
[MARKERS_LEN
];
28 static const struct ptdump_prot_bits stage2_pte_bits
[] = {
35 .mask
= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R
| PTE_VALID
,
36 .val
= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R
| PTE_VALID
,
40 .mask
= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W
| PTE_VALID
,
41 .val
= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W
| PTE_VALID
,
45 .mask
= KVM_PTE_LEAF_ATTR_HI_S2_XN
| PTE_VALID
,
50 .mask
= KVM_PTE_LEAF_ATTR_LO_S2_AF
| PTE_VALID
,
51 .val
= KVM_PTE_LEAF_ATTR_LO_S2_AF
| PTE_VALID
,
55 .mask
= PTE_TABLE_BIT
| PTE_VALID
,
62 static int kvm_ptdump_visitor(const struct kvm_pgtable_visit_ctx
*ctx
,
63 enum kvm_pgtable_walk_flags visit
)
65 struct ptdump_pg_state
*st
= ctx
->arg
;
66 struct ptdump_state
*pt_st
= &st
->ptdump
;
68 note_page(pt_st
, ctx
->addr
, ctx
->level
, ctx
->old
);
73 static int kvm_ptdump_build_levels(struct ptdump_pg_level
*level
, u32 start_lvl
)
78 if (WARN_ON_ONCE(start_lvl
>= KVM_PGTABLE_LAST_LEVEL
))
82 for (i
= 0; i
< ARRAY_SIZE(stage2_pte_bits
); i
++)
83 mask
|= stage2_pte_bits
[i
].mask
;
85 for (i
= start_lvl
; i
< KVM_PGTABLE_MAX_LEVELS
; i
++) {
86 snprintf(level
[i
].name
, sizeof(level
[i
].name
), "%u", i
);
88 level
[i
].num
= ARRAY_SIZE(stage2_pte_bits
);
89 level
[i
].bits
= stage2_pte_bits
;
96 static struct kvm_ptdump_guest_state
*kvm_ptdump_parser_create(struct kvm
*kvm
)
98 struct kvm_ptdump_guest_state
*st
;
99 struct kvm_s2_mmu
*mmu
= &kvm
->arch
.mmu
;
100 struct kvm_pgtable
*pgtable
= mmu
->pgt
;
103 st
= kzalloc(sizeof(struct kvm_ptdump_guest_state
), GFP_KERNEL_ACCOUNT
);
105 return ERR_PTR(-ENOMEM
);
107 ret
= kvm_ptdump_build_levels(&st
->level
[0], pgtable
->start_level
);
113 st
->ipa_marker
[0].name
= "Guest IPA";
114 st
->ipa_marker
[1].start_address
= BIT(pgtable
->ia_bits
);
115 st
->range
[0].end
= BIT(pgtable
->ia_bits
);
118 st
->parser_state
= (struct ptdump_pg_state
) {
119 .marker
= &st
->ipa_marker
[0],
121 .pg_level
= &st
->level
[0],
122 .ptdump
.range
= &st
->range
[0],
129 static int kvm_ptdump_guest_show(struct seq_file
*m
, void *unused
)
132 struct kvm_ptdump_guest_state
*st
= m
->private;
133 struct kvm
*kvm
= st
->kvm
;
134 struct kvm_s2_mmu
*mmu
= &kvm
->arch
.mmu
;
135 struct ptdump_pg_state
*parser_state
= &st
->parser_state
;
136 struct kvm_pgtable_walker walker
= (struct kvm_pgtable_walker
) {
137 .cb
= kvm_ptdump_visitor
,
139 .flags
= KVM_PGTABLE_WALK_LEAF
,
142 parser_state
->seq
= m
;
144 write_lock(&kvm
->mmu_lock
);
145 ret
= kvm_pgtable_walk(mmu
->pgt
, 0, BIT(mmu
->pgt
->ia_bits
), &walker
);
146 write_unlock(&kvm
->mmu_lock
);
151 static int kvm_ptdump_guest_open(struct inode
*m
, struct file
*file
)
153 struct kvm
*kvm
= m
->i_private
;
154 struct kvm_ptdump_guest_state
*st
;
157 if (!kvm_get_kvm_safe(kvm
))
160 st
= kvm_ptdump_parser_create(kvm
);
163 goto err_with_kvm_ref
;
166 ret
= single_open(file
, kvm_ptdump_guest_show
, st
);
176 static int kvm_ptdump_guest_close(struct inode
*m
, struct file
*file
)
178 struct kvm
*kvm
= m
->i_private
;
179 void *st
= ((struct seq_file
*)file
->private_data
)->private;
184 return single_release(m
, file
);
187 static const struct file_operations kvm_ptdump_guest_fops
= {
188 .open
= kvm_ptdump_guest_open
,
191 .release
= kvm_ptdump_guest_close
,
194 static int kvm_pgtable_range_show(struct seq_file
*m
, void *unused
)
196 struct kvm_pgtable
*pgtable
= m
->private;
198 seq_printf(m
, "%2u\n", pgtable
->ia_bits
);
202 static int kvm_pgtable_levels_show(struct seq_file
*m
, void *unused
)
204 struct kvm_pgtable
*pgtable
= m
->private;
206 seq_printf(m
, "%1d\n", KVM_PGTABLE_MAX_LEVELS
- pgtable
->start_level
);
210 static int kvm_pgtable_debugfs_open(struct inode
*m
, struct file
*file
,
211 int (*show
)(struct seq_file
*, void *))
213 struct kvm
*kvm
= m
->i_private
;
214 struct kvm_pgtable
*pgtable
;
217 if (!kvm_get_kvm_safe(kvm
))
220 pgtable
= kvm
->arch
.mmu
.pgt
;
222 ret
= single_open(file
, show
, pgtable
);
228 static int kvm_pgtable_range_open(struct inode
*m
, struct file
*file
)
230 return kvm_pgtable_debugfs_open(m
, file
, kvm_pgtable_range_show
);
233 static int kvm_pgtable_levels_open(struct inode
*m
, struct file
*file
)
235 return kvm_pgtable_debugfs_open(m
, file
, kvm_pgtable_levels_show
);
238 static int kvm_pgtable_debugfs_close(struct inode
*m
, struct file
*file
)
240 struct kvm
*kvm
= m
->i_private
;
243 return single_release(m
, file
);
246 static const struct file_operations kvm_pgtable_range_fops
= {
247 .open
= kvm_pgtable_range_open
,
250 .release
= kvm_pgtable_debugfs_close
,
253 static const struct file_operations kvm_pgtable_levels_fops
= {
254 .open
= kvm_pgtable_levels_open
,
257 .release
= kvm_pgtable_debugfs_close
,
260 void kvm_s2_ptdump_create_debugfs(struct kvm
*kvm
)
262 debugfs_create_file("stage2_page_tables", 0400, kvm
->debugfs_dentry
,
263 kvm
, &kvm_ptdump_guest_fops
);
264 debugfs_create_file("ipa_range", 0400, kvm
->debugfs_dentry
, kvm
,
265 &kvm_pgtable_range_fops
);
266 debugfs_create_file("stage2_levels", 0400, kvm
->debugfs_dentry
,
267 kvm
, &kvm_pgtable_levels_fops
);