ptdump.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. #include <asm/kvm_mmu.h>
  13. #include <asm/kvm_pgtable.h>
  14. #include <asm/ptdump.h>
  15. #define MARKERS_LEN 2
  16. #define KVM_PGTABLE_MAX_LEVELS (KVM_PGTABLE_LAST_LEVEL + 1)
  17. struct kvm_ptdump_guest_state {
  18. struct kvm *kvm;
  19. struct ptdump_pg_state parser_state;
  20. struct addr_marker ipa_marker[MARKERS_LEN];
  21. struct ptdump_pg_level level[KVM_PGTABLE_MAX_LEVELS];
  22. struct ptdump_range range[MARKERS_LEN];
  23. };
  24. static const struct ptdump_prot_bits stage2_pte_bits[] = {
  25. {
  26. .mask = PTE_VALID,
  27. .val = PTE_VALID,
  28. .set = " ",
  29. .clear = "F",
  30. },
  31. {
  32. .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R,
  33. .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R,
  34. .set = "R",
  35. .clear = " ",
  36. },
  37. {
  38. .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W,
  39. .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W,
  40. .set = "W",
  41. .clear = " ",
  42. },
  43. {
  44. .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN,
  45. .val = 0b00UL << __bf_shf(KVM_PTE_LEAF_ATTR_HI_S2_XN),
  46. .set = "px ux ",
  47. },
  48. {
  49. .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN,
  50. .val = 0b01UL << __bf_shf(KVM_PTE_LEAF_ATTR_HI_S2_XN),
  51. .set = "PXNux ",
  52. },
  53. {
  54. .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN,
  55. .val = 0b10UL << __bf_shf(KVM_PTE_LEAF_ATTR_HI_S2_XN),
  56. .set = "PXNUXN",
  57. },
  58. {
  59. .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN,
  60. .val = 0b11UL << __bf_shf(KVM_PTE_LEAF_ATTR_HI_S2_XN),
  61. .set = "px UXN",
  62. },
  63. {
  64. .mask = KVM_PTE_LEAF_ATTR_LO_S2_AF,
  65. .val = KVM_PTE_LEAF_ATTR_LO_S2_AF,
  66. .set = "AF",
  67. .clear = " ",
  68. },
  69. {
  70. .mask = PMD_TYPE_MASK,
  71. .val = PMD_TYPE_SECT,
  72. .set = "BLK",
  73. .clear = " ",
  74. },
  75. };
  76. static int kvm_ptdump_visitor(const struct kvm_pgtable_visit_ctx *ctx,
  77. enum kvm_pgtable_walk_flags visit)
  78. {
  79. struct ptdump_pg_state *st = ctx->arg;
  80. struct ptdump_state *pt_st = &st->ptdump;
  81. note_page(pt_st, ctx->addr, ctx->level, ctx->old);
  82. return 0;
  83. }
  84. static int kvm_ptdump_build_levels(struct ptdump_pg_level *level, u32 start_lvl)
  85. {
  86. u32 i;
  87. u64 mask;
  88. if (WARN_ON_ONCE(start_lvl >= KVM_PGTABLE_LAST_LEVEL))
  89. return -EINVAL;
  90. mask = 0;
  91. for (i = 0; i < ARRAY_SIZE(stage2_pte_bits); i++)
  92. mask |= stage2_pte_bits[i].mask;
  93. for (i = start_lvl; i < KVM_PGTABLE_MAX_LEVELS; i++) {
  94. snprintf(level[i].name, sizeof(level[i].name), "%u", i);
  95. level[i].num = ARRAY_SIZE(stage2_pte_bits);
  96. level[i].bits = stage2_pte_bits;
  97. level[i].mask = mask;
  98. }
  99. return 0;
  100. }
  101. static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm)
  102. {
  103. struct kvm_ptdump_guest_state *st;
  104. struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
  105. struct kvm_pgtable *pgtable = mmu->pgt;
  106. int ret;
  107. st = kzalloc_obj(struct kvm_ptdump_guest_state, GFP_KERNEL_ACCOUNT);
  108. if (!st)
  109. return ERR_PTR(-ENOMEM);
  110. ret = kvm_ptdump_build_levels(&st->level[0], pgtable->start_level);
  111. if (ret) {
  112. kfree(st);
  113. return ERR_PTR(ret);
  114. }
  115. st->ipa_marker[0].name = "Guest IPA";
  116. st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);
  117. st->range[0].end = BIT(pgtable->ia_bits);
  118. st->kvm = kvm;
  119. st->parser_state = (struct ptdump_pg_state) {
  120. .marker = &st->ipa_marker[0],
  121. .level = -1,
  122. .pg_level = &st->level[0],
  123. .ptdump.range = &st->range[0],
  124. .start_address = 0,
  125. };
  126. return st;
  127. }
  128. static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
  129. {
  130. int ret;
  131. struct kvm_ptdump_guest_state *st = m->private;
  132. struct kvm *kvm = st->kvm;
  133. struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
  134. struct ptdump_pg_state *parser_state = &st->parser_state;
  135. struct kvm_pgtable_walker walker = (struct kvm_pgtable_walker) {
  136. .cb = kvm_ptdump_visitor,
  137. .arg = parser_state,
  138. .flags = KVM_PGTABLE_WALK_LEAF,
  139. };
  140. parser_state->seq = m;
  141. write_lock(&kvm->mmu_lock);
  142. ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
  143. write_unlock(&kvm->mmu_lock);
  144. return ret;
  145. }
  146. static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
  147. {
  148. struct kvm *kvm = m->i_private;
  149. struct kvm_ptdump_guest_state *st;
  150. int ret;
  151. if (!kvm_get_kvm_safe(kvm))
  152. return -ENOENT;
  153. st = kvm_ptdump_parser_create(kvm);
  154. if (IS_ERR(st)) {
  155. ret = PTR_ERR(st);
  156. goto err_with_kvm_ref;
  157. }
  158. ret = single_open(file, kvm_ptdump_guest_show, st);
  159. if (!ret)
  160. return 0;
  161. kfree(st);
  162. err_with_kvm_ref:
  163. kvm_put_kvm(kvm);
  164. return ret;
  165. }
  166. static int kvm_ptdump_guest_close(struct inode *m, struct file *file)
  167. {
  168. struct kvm *kvm = m->i_private;
  169. void *st = ((struct seq_file *)file->private_data)->private;
  170. kfree(st);
  171. kvm_put_kvm(kvm);
  172. return single_release(m, file);
  173. }
  174. static const struct file_operations kvm_ptdump_guest_fops = {
  175. .open = kvm_ptdump_guest_open,
  176. .read = seq_read,
  177. .llseek = seq_lseek,
  178. .release = kvm_ptdump_guest_close,
  179. };
  180. static int kvm_pgtable_range_show(struct seq_file *m, void *unused)
  181. {
  182. struct kvm_pgtable *pgtable = m->private;
  183. seq_printf(m, "%2u\n", pgtable->ia_bits);
  184. return 0;
  185. }
  186. static int kvm_pgtable_levels_show(struct seq_file *m, void *unused)
  187. {
  188. struct kvm_pgtable *pgtable = m->private;
  189. seq_printf(m, "%1d\n", KVM_PGTABLE_MAX_LEVELS - pgtable->start_level);
  190. return 0;
  191. }
  192. static int kvm_pgtable_debugfs_open(struct inode *m, struct file *file,
  193. int (*show)(struct seq_file *, void *))
  194. {
  195. struct kvm *kvm = m->i_private;
  196. struct kvm_pgtable *pgtable;
  197. int ret;
  198. if (!kvm_get_kvm_safe(kvm))
  199. return -ENOENT;
  200. pgtable = kvm->arch.mmu.pgt;
  201. ret = single_open(file, show, pgtable);
  202. if (ret < 0)
  203. kvm_put_kvm(kvm);
  204. return ret;
  205. }
  206. static int kvm_pgtable_range_open(struct inode *m, struct file *file)
  207. {
  208. return kvm_pgtable_debugfs_open(m, file, kvm_pgtable_range_show);
  209. }
  210. static int kvm_pgtable_levels_open(struct inode *m, struct file *file)
  211. {
  212. return kvm_pgtable_debugfs_open(m, file, kvm_pgtable_levels_show);
  213. }
  214. static int kvm_pgtable_debugfs_close(struct inode *m, struct file *file)
  215. {
  216. struct kvm *kvm = m->i_private;
  217. kvm_put_kvm(kvm);
  218. return single_release(m, file);
  219. }
  220. static const struct file_operations kvm_pgtable_range_fops = {
  221. .open = kvm_pgtable_range_open,
  222. .read = seq_read,
  223. .llseek = seq_lseek,
  224. .release = kvm_pgtable_debugfs_close,
  225. };
  226. static const struct file_operations kvm_pgtable_levels_fops = {
  227. .open = kvm_pgtable_levels_open,
  228. .read = seq_read,
  229. .llseek = seq_lseek,
  230. .release = kvm_pgtable_debugfs_close,
  231. };
  232. void kvm_s2_ptdump_create_debugfs(struct kvm *kvm)
  233. {
  234. debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry,
  235. kvm, &kvm_ptdump_guest_fops);
  236. debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry, kvm,
  237. &kvm_pgtable_range_fops);
  238. debugfs_create_file("stage2_levels", 0400, kvm->debugfs_dentry,
  239. kvm, &kvm_pgtable_levels_fops);
  240. }