~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/kvm/ptdump.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Debug helper used to dump the stage-2 pagetables of the system and their
  4  * associated permissions.
  5  *
  6  * Copyright (C) Google, 2024
  7  * Author: Sebastian Ene <sebastianene@google.com>
  8  */
  9 #include <linux/debugfs.h>
 10 #include <linux/kvm_host.h>
 11 #include <linux/seq_file.h>
 12 
 13 #include <asm/kvm_mmu.h>
 14 #include <asm/kvm_pgtable.h>
 15 #include <asm/ptdump.h>
 16 
 17 #define MARKERS_LEN             2
 18 #define KVM_PGTABLE_MAX_LEVELS  (KVM_PGTABLE_LAST_LEVEL + 1)
 19 
 20 struct kvm_ptdump_guest_state {
 21         struct kvm              *kvm;
 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];
 26 };
 27 
 28 static const struct ptdump_prot_bits stage2_pte_bits[] = {
 29         {
 30                 .mask   = PTE_VALID,
 31                 .val    = PTE_VALID,
 32                 .set    = " ",
 33                 .clear  = "F",
 34         }, {
 35                 .mask   = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | PTE_VALID,
 36                 .val    = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | PTE_VALID,
 37                 .set    = "R",
 38                 .clear  = " ",
 39         }, {
 40                 .mask   = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W | PTE_VALID,
 41                 .val    = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W | PTE_VALID,
 42                 .set    = "W",
 43                 .clear  = " ",
 44         }, {
 45                 .mask   = KVM_PTE_LEAF_ATTR_HI_S2_XN | PTE_VALID,
 46                 .val    = PTE_VALID,
 47                 .set    = " ",
 48                 .clear  = "X",
 49         }, {
 50                 .mask   = KVM_PTE_LEAF_ATTR_LO_S2_AF | PTE_VALID,
 51                 .val    = KVM_PTE_LEAF_ATTR_LO_S2_AF | PTE_VALID,
 52                 .set    = "AF",
 53                 .clear  = "  ",
 54         }, {
 55                 .mask   = PTE_TABLE_BIT | PTE_VALID,
 56                 .val    = PTE_VALID,
 57                 .set    = "BLK",
 58                 .clear  = "   ",
 59         },
 60 };
 61 
 62 static int kvm_ptdump_visitor(const struct kvm_pgtable_visit_ctx *ctx,
 63                               enum kvm_pgtable_walk_flags visit)
 64 {
 65         struct ptdump_pg_state *st = ctx->arg;
 66         struct ptdump_state *pt_st = &st->ptdump;
 67 
 68         note_page(pt_st, ctx->addr, ctx->level, ctx->old);
 69 
 70         return 0;
 71 }
 72 
 73 static int kvm_ptdump_build_levels(struct ptdump_pg_level *level, u32 start_lvl)
 74 {
 75         u32 i;
 76         u64 mask;
 77 
 78         if (WARN_ON_ONCE(start_lvl >= KVM_PGTABLE_LAST_LEVEL))
 79                 return -EINVAL;
 80 
 81         mask = 0;
 82         for (i = 0; i < ARRAY_SIZE(stage2_pte_bits); i++)
 83                 mask |= stage2_pte_bits[i].mask;
 84 
 85         for (i = start_lvl; i < KVM_PGTABLE_MAX_LEVELS; i++) {
 86                 snprintf(level[i].name, sizeof(level[i].name), "%u", i);
 87 
 88                 level[i].num    = ARRAY_SIZE(stage2_pte_bits);
 89                 level[i].bits   = stage2_pte_bits;
 90                 level[i].mask   = mask;
 91         }
 92 
 93         return 0;
 94 }
 95 
 96 static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm)
 97 {
 98         struct kvm_ptdump_guest_state *st;
 99         struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
100         struct kvm_pgtable *pgtable = mmu->pgt;
101         int ret;
102 
103         st = kzalloc(sizeof(struct kvm_ptdump_guest_state), GFP_KERNEL_ACCOUNT);
104         if (!st)
105                 return ERR_PTR(-ENOMEM);
106 
107         ret = kvm_ptdump_build_levels(&st->level[0], pgtable->start_level);
108         if (ret) {
109                 kfree(st);
110                 return ERR_PTR(ret);
111         }
112 
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);
116 
117         st->kvm                         = kvm;
118         st->parser_state = (struct ptdump_pg_state) {
119                 .marker         = &st->ipa_marker[0],
120                 .level          = -1,
121                 .pg_level       = &st->level[0],
122                 .ptdump.range   = &st->range[0],
123                 .start_address  = 0,
124         };
125 
126         return st;
127 }
128 
129 static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
130 {
131         int ret;
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,
138                 .arg    = parser_state,
139                 .flags  = KVM_PGTABLE_WALK_LEAF,
140         };
141 
142         parser_state->seq = m;
143 
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);
147 
148         return ret;
149 }
150 
151 static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
152 {
153         struct kvm *kvm = m->i_private;
154         struct kvm_ptdump_guest_state *st;
155         int ret;
156 
157         if (!kvm_get_kvm_safe(kvm))
158                 return -ENOENT;
159 
160         st = kvm_ptdump_parser_create(kvm);
161         if (IS_ERR(st)) {
162                 ret = PTR_ERR(st);
163                 goto err_with_kvm_ref;
164         }
165 
166         ret = single_open(file, kvm_ptdump_guest_show, st);
167         if (!ret)
168                 return 0;
169 
170         kfree(st);
171 err_with_kvm_ref:
172         kvm_put_kvm(kvm);
173         return ret;
174 }
175 
176 static int kvm_ptdump_guest_close(struct inode *m, struct file *file)
177 {
178         struct kvm *kvm = m->i_private;
179         void *st = ((struct seq_file *)file->private_data)->private;
180 
181         kfree(st);
182         kvm_put_kvm(kvm);
183 
184         return single_release(m, file);
185 }
186 
187 static const struct file_operations kvm_ptdump_guest_fops = {
188         .open           = kvm_ptdump_guest_open,
189         .read           = seq_read,
190         .llseek         = seq_lseek,
191         .release        = kvm_ptdump_guest_close,
192 };
193 
194 static int kvm_pgtable_range_show(struct seq_file *m, void *unused)
195 {
196         struct kvm_pgtable *pgtable = m->private;
197 
198         seq_printf(m, "%2u\n", pgtable->ia_bits);
199         return 0;
200 }
201 
202 static int kvm_pgtable_levels_show(struct seq_file *m, void *unused)
203 {
204         struct kvm_pgtable *pgtable = m->private;
205 
206         seq_printf(m, "%1d\n", KVM_PGTABLE_MAX_LEVELS - pgtable->start_level);
207         return 0;
208 }
209 
210 static int kvm_pgtable_debugfs_open(struct inode *m, struct file *file,
211                                     int (*show)(struct seq_file *, void *))
212 {
213         struct kvm *kvm = m->i_private;
214         struct kvm_pgtable *pgtable;
215         int ret;
216 
217         if (!kvm_get_kvm_safe(kvm))
218                 return -ENOENT;
219 
220         pgtable = kvm->arch.mmu.pgt;
221 
222         ret = single_open(file, show, pgtable);
223         if (ret < 0)
224                 kvm_put_kvm(kvm);
225         return ret;
226 }
227 
228 static int kvm_pgtable_range_open(struct inode *m, struct file *file)
229 {
230         return kvm_pgtable_debugfs_open(m, file, kvm_pgtable_range_show);
231 }
232 
233 static int kvm_pgtable_levels_open(struct inode *m, struct file *file)
234 {
235         return kvm_pgtable_debugfs_open(m, file, kvm_pgtable_levels_show);
236 }
237 
238 static int kvm_pgtable_debugfs_close(struct inode *m, struct file *file)
239 {
240         struct kvm *kvm = m->i_private;
241 
242         kvm_put_kvm(kvm);
243         return single_release(m, file);
244 }
245 
246 static const struct file_operations kvm_pgtable_range_fops = {
247         .open           = kvm_pgtable_range_open,
248         .read           = seq_read,
249         .llseek         = seq_lseek,
250         .release        = kvm_pgtable_debugfs_close,
251 };
252 
253 static const struct file_operations kvm_pgtable_levels_fops = {
254         .open           = kvm_pgtable_levels_open,
255         .read           = seq_read,
256         .llseek         = seq_lseek,
257         .release        = kvm_pgtable_debugfs_close,
258 };
259 
260 void kvm_s2_ptdump_create_debugfs(struct kvm *kvm)
261 {
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);
268 }
269 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php