1 // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 3 * Debug helper used to dump the stage-2 paget 4 * associated permissions. 5 * 6 * Copyright (C) Google, 2024 7 * Author: Sebastian Ene <sebastianene@google. 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_L 19 20 struct kvm_ptdump_guest_state { 21 struct kvm *kvm; 22 struct ptdump_pg_state parser_state; 23 struct addr_marker ipa_marker[MAR 24 struct ptdump_pg_level level[KVM_PGTA 25 struct ptdump_range range[MARKERS_ 26 }; 27 28 static const struct ptdump_prot_bits stage2_pt 29 { 30 .mask = PTE_VALID, 31 .val = PTE_VALID, 32 .set = " ", 33 .clear = "F", 34 }, { 35 .mask = KVM_PTE_LEAF_ATTR_LO 36 .val = KVM_PTE_LEAF_ATTR_LO 37 .set = "R", 38 .clear = " ", 39 }, { 40 .mask = KVM_PTE_LEAF_ATTR_LO 41 .val = KVM_PTE_LEAF_ATTR_LO 42 .set = "W", 43 .clear = " ", 44 }, { 45 .mask = KVM_PTE_LEAF_ATTR_HI 46 .val = PTE_VALID, 47 .set = " ", 48 .clear = "X", 49 }, { 50 .mask = KVM_PTE_LEAF_ATTR_LO 51 .val = KVM_PTE_LEAF_ATTR_LO 52 .set = "AF", 53 .clear = " ", 54 }, { 55 .mask = PTE_TABLE_BIT | PTE_ 56 .val = PTE_VALID, 57 .set = "BLK", 58 .clear = " ", 59 }, 60 }; 61 62 static int kvm_ptdump_visitor(const struct kvm 63 enum kvm_pgtable 64 { 65 struct ptdump_pg_state *st = ctx->arg; 66 struct ptdump_state *pt_st = &st->ptdu 67 68 note_page(pt_st, ctx->addr, ctx->level 69 70 return 0; 71 } 72 73 static int kvm_ptdump_build_levels(struct ptdu 74 { 75 u32 i; 76 u64 mask; 77 78 if (WARN_ON_ONCE(start_lvl >= KVM_PGTA 79 return -EINVAL; 80 81 mask = 0; 82 for (i = 0; i < ARRAY_SIZE(stage2_pte_ 83 mask |= stage2_pte_bits[i].mas 84 85 for (i = start_lvl; i < KVM_PGTABLE_MA 86 snprintf(level[i].name, sizeof 87 88 level[i].num = ARRAY_SIZE(s 89 level[i].bits = stage2_pte_b 90 level[i].mask = mask; 91 } 92 93 return 0; 94 } 95 96 static struct kvm_ptdump_guest_state *kvm_ptdu 97 { 98 struct kvm_ptdump_guest_state *st; 99 struct kvm_s2_mmu *mmu = &kvm->arch.mm 100 struct kvm_pgtable *pgtable = mmu->pgt 101 int ret; 102 103 st = kzalloc(sizeof(struct kvm_ptdump_ 104 if (!st) 105 return ERR_PTR(-ENOMEM); 106 107 ret = kvm_ptdump_build_levels(&st->lev 108 if (ret) { 109 kfree(st); 110 return ERR_PTR(ret); 111 } 112 113 st->ipa_marker[0].name = "Gue 114 st->ipa_marker[1].start_address = BIT( 115 st->range[0].end = BIT( 116 117 st->kvm = kvm; 118 st->parser_state = (struct ptdump_pg_s 119 .marker = &st->ipa_mar 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_fi 130 { 131 int ret; 132 struct kvm_ptdump_guest_state *st = m- 133 struct kvm *kvm = st->kvm; 134 struct kvm_s2_mmu *mmu = &kvm->arch.mm 135 struct ptdump_pg_state *parser_state = 136 struct kvm_pgtable_walker walker = (st 137 .cb = kvm_ptdump_visitor, 138 .arg = parser_state, 139 .flags = KVM_PGTABLE_WALK_LEA 140 }; 141 142 parser_state->seq = m; 143 144 write_lock(&kvm->mmu_lock); 145 ret = kvm_pgtable_walk(mmu->pgt, 0, BI 146 write_unlock(&kvm->mmu_lock); 147 148 return ret; 149 } 150 151 static int kvm_ptdump_guest_open(struct inode 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_gue 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 177 { 178 struct kvm *kvm = m->i_private; 179 void *st = ((struct seq_file *)file->p 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 188 .open = kvm_ptdump_guest_ope 189 .read = seq_read, 190 .llseek = seq_lseek, 191 .release = kvm_ptdump_guest_clo 192 }; 193 194 static int kvm_pgtable_range_show(struct seq_f 195 { 196 struct kvm_pgtable *pgtable = m->priva 197 198 seq_printf(m, "%2u\n", pgtable->ia_bit 199 return 0; 200 } 201 202 static int kvm_pgtable_levels_show(struct seq_ 203 { 204 struct kvm_pgtable *pgtable = m->priva 205 206 seq_printf(m, "%1d\n", KVM_PGTABLE_MAX 207 return 0; 208 } 209 210 static int kvm_pgtable_debugfs_open(struct ino 211 int (*show 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 229 { 230 return kvm_pgtable_debugfs_open(m, fil 231 } 232 233 static int kvm_pgtable_levels_open(struct inod 234 { 235 return kvm_pgtable_debugfs_open(m, fil 236 } 237 238 static int kvm_pgtable_debugfs_close(struct in 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_pgtabl 247 .open = kvm_pgtable_range_op 248 .read = seq_read, 249 .llseek = seq_lseek, 250 .release = kvm_pgtable_debugfs_ 251 }; 252 253 static const struct file_operations kvm_pgtabl 254 .open = kvm_pgtable_levels_o 255 .read = seq_read, 256 .llseek = seq_lseek, 257 .release = kvm_pgtable_debugfs_ 258 }; 259 260 void kvm_s2_ptdump_create_debugfs(struct kvm * 261 { 262 debugfs_create_file("stage2_page_table 263 kvm, &kvm_ptdump_g 264 debugfs_create_file("ipa_range", 0400, 265 &kvm_pgtable_range 266 debugfs_create_file("stage2_levels", 0 267 kvm, &kvm_pgtable_ 268 } 269
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.