Commit 1f568d36 authored by Brijesh Singh's avatar Brijesh Singh Committed by Borislav Petkov (AMD)

x86/fault: Add helper for dumping RMP entries

This information will be useful for debugging things like page faults
due to RMP access violations and RMPUPDATE failures.

  [ mdr: move helper to standalone patch, rework dump logic as suggested
    by Boris. ]
Signed-off-by: default avatarBrijesh Singh <brijesh.singh@amd.com>
Signed-off-by: default avatarAshish Kalra <ashish.kalra@amd.com>
Signed-off-by: default avatarMichael Roth <michael.roth@amd.com>
Signed-off-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20240126041126.1927228-8-michael.roth@amd.com
parent 94b36bc2
...@@ -247,9 +247,11 @@ static inline u64 sev_get_status(void) { return 0; } ...@@ -247,9 +247,11 @@ static inline u64 sev_get_status(void) { return 0; }
#ifdef CONFIG_KVM_AMD_SEV #ifdef CONFIG_KVM_AMD_SEV
bool snp_probe_rmptable_info(void); bool snp_probe_rmptable_info(void);
int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level); int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level);
void snp_dump_hva_rmpentry(unsigned long address);
#else #else
static inline bool snp_probe_rmptable_info(void) { return false; } static inline bool snp_probe_rmptable_info(void) { return false; }
static inline int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level) { return -ENODEV; } static inline int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level) { return -ENODEV; }
static inline void snp_dump_hva_rmpentry(unsigned long address) {}
#endif #endif
#endif #endif
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
* Family 19h Model 01h, Rev B1 processor. * Family 19h Model 01h, Rev B1 processor.
*/ */
struct rmpentry { struct rmpentry {
union {
struct {
u64 assigned : 1, u64 assigned : 1,
pagesize : 1, pagesize : 1,
immutable : 1, immutable : 1,
...@@ -44,7 +46,10 @@ struct rmpentry { ...@@ -44,7 +46,10 @@ struct rmpentry {
vmsa : 1, vmsa : 1,
validated : 1, validated : 1,
rsvd2 : 1; rsvd2 : 1;
u64 rsvd3; };
u64 lo;
};
u64 hi;
} __packed; } __packed;
/* /*
...@@ -263,3 +268,77 @@ int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level) ...@@ -263,3 +268,77 @@ int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(snp_lookup_rmpentry); EXPORT_SYMBOL_GPL(snp_lookup_rmpentry);
/*
* Dump the raw RMP entry for a particular PFN. These bits are documented in the
* PPR for a particular CPU model and provide useful information about how a
* particular PFN is being utilized by the kernel/firmware at the time certain
* unexpected events occur, such as RMP faults.
*/
static void dump_rmpentry(u64 pfn)
{
u64 pfn_i, pfn_end;
struct rmpentry *e;
int level;
e = __snp_lookup_rmpentry(pfn, &level);
if (IS_ERR(e)) {
pr_err("Failed to read RMP entry for PFN 0x%llx, error %ld\n",
pfn, PTR_ERR(e));
return;
}
if (e->assigned) {
pr_info("PFN 0x%llx, RMP entry: [0x%016llx - 0x%016llx]\n",
pfn, e->lo, e->hi);
return;
}
/*
* If the RMP entry for a particular PFN is not in an assigned state,
* then it is sometimes useful to get an idea of whether or not any RMP
* entries for other PFNs within the same 2MB region are assigned, since
* those too can affect the ability to access a particular PFN in
* certain situations, such as when the PFN is being accessed via a 2MB
* mapping in the host page table.
*/
pfn_i = ALIGN_DOWN(pfn, PTRS_PER_PMD);
pfn_end = pfn_i + PTRS_PER_PMD;
pr_info("PFN 0x%llx unassigned, dumping non-zero entries in 2M PFN region: [0x%llx - 0x%llx]\n",
pfn, pfn_i, pfn_end);
while (pfn_i < pfn_end) {
e = __snp_lookup_rmpentry(pfn_i, &level);
if (IS_ERR(e)) {
pr_err("Error %ld reading RMP entry for PFN 0x%llx\n",
PTR_ERR(e), pfn_i);
pfn_i++;
continue;
}
if (e->lo || e->hi)
pr_info("PFN: 0x%llx, [0x%016llx - 0x%016llx]\n", pfn_i, e->lo, e->hi);
pfn_i++;
}
}
void snp_dump_hva_rmpentry(unsigned long hva)
{
unsigned long paddr;
unsigned int level;
pgd_t *pgd;
pte_t *pte;
pgd = __va(read_cr3_pa());
pgd += pgd_index(hva);
pte = lookup_address_in_pgd(pgd, hva, &level);
if (!pte) {
pr_err("Can't dump RMP entry for HVA %lx: no PTE/PFN found\n", hva);
return;
}
paddr = PFN_PHYS(pte_pfn(*pte)) | (hva & ~page_level_mask(level));
dump_rmpentry(PHYS_PFN(paddr));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment