x86: add debug functions for dumping page tables

These turned out to be quite useful when debugging MMU
issues, commit them to the tree. The output format is
virtually the same as gen_mmu_x86.py's verbose output.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2019-07-25 13:25:03 -07:00 committed by Carles Cufí
commit ea201b206f
2 changed files with 137 additions and 0 deletions

View file

@ -9,6 +9,7 @@
#include <linker/linker-defs.h>
#include <kernel_internal.h>
#include <init.h>
#include <ctype.h>
/* Common regions for all x86 processors.
* Peripheral I/O ranges configured at the SOC level
@ -44,6 +45,122 @@ MMU_BOOT_REGION((u32_t)&__kernel_ram_start, (u32_t)&__kernel_ram_size,
MMU_ENTRY_RUNTIME_USER |
MMU_ENTRY_EXECUTE_DISABLE);
/* Works for PDPT, PD, PT entries, the bits we check here are all the same.
*
* Not trying to capture every flag, just the most interesting stuff,
* Present, write, XD, user, in typically encountered combinations.
*/
static char get_entry_code(u64_t value)
{
char ret;
if ((value & MMU_ENTRY_PRESENT) == 0) {
ret = '.';
} else {
if ((value & MMU_ENTRY_WRITE) != 0) {
/* Writable page */
if ((value & MMU_ENTRY_EXECUTE_DISABLE) != 0) {
/* RW */
ret = 'w';
} else {
/* RWX */
ret = 'a';
}
} else {
if ((value & MMU_ENTRY_EXECUTE_DISABLE) != 0) {
/* R */
ret = 'r';
} else {
/* RX */
ret = 'x';
}
}
if ((value & MMU_ENTRY_USER) != 0) {
/* Uppercase indicates user mode access */
ret = toupper(ret);
}
}
return ret;
}
static void z_x86_dump_pt(struct x86_mmu_pt *pt, uintptr_t base, int index)
{
int column = 0;
printk("Page table %d for 0x%08lX - 0x%08lX at %p\n",
index, base, base + Z_X86_PT_AREA - 1, pt);
for (int i = 0; i < Z_X86_NUM_PT_ENTRIES; i++) {
printk("%c", get_entry_code(pt->entry[i].value));
column++;
if (column == 64) {
column = 0;
printk("\n");
}
}
}
static void z_x86_dump_pd(struct x86_mmu_pd *pd, uintptr_t base, int index)
{
int column = 0;
printk("Page directory %d for 0x%08lX - 0x%08lX at %p\n",
index, base, base + Z_X86_PD_AREA - 1, pd);
for (int i = 0; i < Z_X86_NUM_PD_ENTRIES; i++) {
printk("%c", get_entry_code(pd->entry[i].pt.value));
column++;
if (column == 64) {
column = 0;
printk("\n");
}
}
for (int i = 0; i < Z_X86_NUM_PD_ENTRIES; i++) {
struct x86_mmu_pt *pt;
union x86_mmu_pde_pt *pde = &pd->entry[i].pt;
if (pde->p == 0 || pde->ps == 1) {
/* Skip non-present, or 2MB directory entries, there's
* no page table to examine */
continue;
}
pt = (struct x86_mmu_pt *)(pde->pt << MMU_PAGE_SHIFT);
z_x86_dump_pt(pt, base + (i * Z_X86_PT_AREA), i);
}
}
static void z_x86_dump_pdpt(struct x86_mmu_pdpt *pdpt, uintptr_t base,
int index)
{
printk("Page directory pointer table %d for 0x%08lX - 0x%08lX at %p\n",
index, base, base + Z_X86_PDPT_AREA - 1, pdpt);
for (int i = 0; i < Z_X86_NUM_PDPT_ENTRIES; i++) {
printk("%c", get_entry_code(pdpt->entry[i].value));
}
printk("\n");
for (int i = 0; i < Z_X86_NUM_PDPT_ENTRIES; i++) {
struct x86_mmu_pd *pd;
if (pdpt->entry[i].p == 0) {
continue;
}
pd = (struct x86_mmu_pd *)(pdpt->entry[i].pd << MMU_PAGE_SHIFT);
z_x86_dump_pd(pd, base + (i * Z_X86_PD_AREA), i);
}
}
void z_x86_dump_page_tables(struct x86_mmu_pdpt *pdpt)
{
z_x86_dump_pdpt(pdpt, 0, 0);
}
void z_x86_mmu_get_flags(struct x86_mmu_pdpt *pdpt, void *addr,
x86_page_entry_data_t *pde_flags,

View file

@ -506,6 +506,26 @@ struct x86_mmu_pt {
union x86_mmu_pte entry[Z_X86_NUM_PT_ENTRIES];
};
/**
* Debug function for dumping out page tables
*
* Iterates through the entire linked set of page table structures,
* dumping out codes for the configuration of each table entry.
*
* Entry codes:
*
* . - not present
* w - present, writable, not executable
* a - present, writable, executable
* r - present, read-only, not executable
* x - present, read-only, executable
*
* Entry codes in uppercase indicate that user mode may access.
*
* @param pdpt Top-level pointer to the page tables, as programmed in CR3
*/
void z_x86_dump_page_tables(struct x86_mmu_pdpt *pdpt);
#endif /* _ASMLANGUAGE */
#endif /* ZEPHYR_ARCH_X86_INCLUDE_IA32_MMUSTRUCTS_H_ */