/*
* Debug helper to dump the current kernel pagetables of the system
* so that we can see what the various memory ranges are set to.
*
* Derived from x86 implementation:
* (C) Copyright 2008 Intel Corporation
*
* Author: Arjan van de Ven <arjan@linux.intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*/
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <asm/fixmap.h>
#include <asm/pgtable.h>
struct addr_marker {
unsigned long start_address;
const char *name;
};
static struct addr_marker address_markers[] = {
{ MODULES_VADDR, "Modules" },
{ PAGE_OFFSET, "Kernel Mapping" },
{ 0, "vmalloc() Area" },
{ VMALLOC_END, "vmalloc() End" },
{ FIXADDR_START, "Fixmap Area" },
{ CONFIG_VECTORS_BASE, "Vectors" },
{ CONFIG_VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" },
{ -1, NULL },
};
static struct addr_marker address_markersxxxx[] = {
{ MODULES_VADDR, "Modules" },
//{ PAGE_OFFSET, "Kernel Mapping" },
//{ 0, "vmalloc() Area" },
//{ VMALLOC_END, "vmalloc() End" },
//{ FIXADDR_START, "Fixmap Area" },
//{ CONFIG_VECTORS_BASE, "Vectors" },
//{ CONFIG_VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" },
{ -1, NULL },
};
struct pg_state {
struct seq_file *seq;
const struct addr_marker *marker;
unsigned long start_address;
unsigned level;
u64 current_prot;
};
struct prot_bits {
u64 mask;
u64 val;
const char *set;
const char *clear;
};
//[ 0.910709] i = 4
//[ 0.912889] pg_level[i].mask = 7bc
// 0111 1011 1100
// 10 9 8 7 5 4 2 3
static const struct prot_bits pte_bits[] = {
{
.mask = L_PTE_USER, //8
.val = L_PTE_USER,
.set = "USR",
.clear = " ",
}, {
.mask = L_PTE_RDONLY, //7
.val = L_PTE_RDONLY,
.set = "ro",
.clear = "RW",
}, {
.mask = L_PTE_XN, //9
.val = L_PTE_XN,
.set = "NX",
.clear = "x ",
}, {
.mask = L_PTE_SHARED, //10
.val = L_PTE_SHARED,
.set = "SHD",
.clear = " ",
}, {
.mask = L_PTE_MT_MASK, //2 3 4 5
.val = L_PTE_MT_UNCACHED,
.set = "SO/UNCACHED",
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_BUFFERABLE,
.set = "MEM/BUFFERABLE/WC",
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_WRITETHROUGH,
.set = "MEM/CACHED/WT",
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_WRITEBACK,
.set = "MEM/CACHED/WBRA",
#ifndef CONFIG_ARM_LPAE
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_MINICACHE,
.set = "MEM/MINICACHE",
#endif
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_WRITEALLOC,
.set = "MEM/CACHED/WBWA",
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_DEV_SHARED,
.set = "DEV/SHARED",
#ifndef CONFIG_ARM_LPAE
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_DEV_NONSHARED,
.set = "DEV/NONSHARED",
#endif
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_DEV_WC,
.set = "DEV/WC",
}, {
.mask = L_PTE_MT_MASK,
.val = L_PTE_MT_DEV_CACHED,
.set = "DEV/CACHED",
},
};
//[ 0.904633] i = 3
//[ 0.906787] pg_level[i].mask = 18c10
static const struct prot_bits section_bits[] = {
#ifdef CONFIG_ARM_LPAE
sssss
{
.mask = PMD_SECT_USER,
.val = PMD_SECT_USER,
.set = "USR",
}, {
.mask = L_PMD_SECT_RDONLY | PMD_SECT_AP2,
.val = L_PMD_SECT_RDONLY | PMD_SECT_AP2,
.set = "ro",
.clear = "RW",
#elif __LINUX_ARM_ARCH__ >= 6
//xxxxxxxx
{
.mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_APX | PMD_SECT_AP_WRITE,
.set = " ro",
}, {
.mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_WRITE,
.set = " RW",
}, {
.mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_READ,
.set = "USR ro",
}, {
.mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.set = "USR RW",
#else /* ARMv4/ARMv5 */
kkkkk
/* These are approximate */
{
.mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = 0,
.set = " ro",
}, {
.mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_WRITE,
.set = " RW",
}, {
.mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_READ,
.set = "USR ro",
}, {
.mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
.set = "USR RW",
#endif
}, {
.mask = PMD_SECT_XN,
.val = PMD_SECT_XN,
.set = "NX",
.clear = "x ",
}, {
.mask = PMD_SECT_S,
.val = PMD_SECT_S,
.set = "SHD",
.clear = " ",
},
};
int g_debug_en = 0x00;
struct pg_level {
const struct prot_bits *bits;
size_t num;
u64 mask;
};
static struct pg_level pg_level[] = {
{
},
{ /* pgd */
},
{ /* pud */
},
{ /* pmd */
.bits = section_bits,
.num = ARRAY_SIZE(section_bits),
},
{ /* pte */
.bits = pte_bits,
.num = ARRAY_SIZE(pte_bits),
},
};
static void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t num)
{
unsigned i;
for (i = 0; i < num; i++, bits++) {
const char *s;
if ((st->current_prot & bits->mask) == bits->val)
s = bits->set;
else
s = bits->clear;
if (s){
//seq_printf(st->seq, " %s", s);
printk(" %s", s);
}
}
}
static void note_page(struct pg_state *st, unsigned long addr, unsigned level, u64 val)
{
static const char units[] = "KMGTPE";
u64 prot = val & pg_level[level].mask;
if( g_debug_en && val ){
printk("addr = %x\n", addr );
printk("val = %llx\n", val);
printk("st->current_prot = %llx\n", st->current_prot );
printk("prot = %llx\n", prot );
}
if (!st->level) {
st->level = level;
st->current_prot = prot;
//seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
printk("---[ %s ]---\n", st->marker->name);
} else if( prot != st->current_prot || level != st->level || addr >= st->marker[1].start_address ){
const char *unit = units;
unsigned long delta;
if (st->current_prot) {
//seq_printf(st->seq, "0x%08lx-0x%08lx ", st->start_address, addr);
printk( "0x%08lx-0x%08lx ", st->start_address, addr);
delta = (addr - st->start_address) >> 10;
while (!(delta & 1023) && unit[1]) {
delta >>= 10;
unit++;
}
//seq_printf(st->seq, "%9lu%c", delta, *unit);
printk("%9lu%c", delta, *unit);
if (pg_level[st->level].bits){
dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num);
}
//seq_printf(st->seq, "\n");
printk("\n");
}
if (addr >= st->marker[1].start_address) {
st->marker++;
//seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
printk("---[ %s ]---\n", st->marker->name);
}
st->start_address = addr;
st->current_prot = prot;
st->level = level;
}
}
static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
{
pte_t *pte = pte_offset_kernel(pmd, 0);
unsigned long addr;
unsigned i;
//return x - PHYS_OFFSET + PAGE_OFFSET;
//printk("PTRS_PER_PTE = %x\n", PTRS_PER_PTE );
//printk("PAGE_SIZE = %x\n", PAGE_SIZE );
//PHYS_OFFSET = 40000000
//PAGE_OFFSET = c0000000
//PTRS_PER_PTE = 0x200
//PAGE_SIZE = 0x1000
//walk_pte: start: 0xf19c5000, pte: 0xee1e3714, *pte: 0x405c5653
//walk_pte: start: 0xf19c6000, pte: 0xee1e3718, *pte: 0x405c6653
printk( "\n\npmd = %p, *pmd = %x\n", pmd, *pmd );
printk("walk_pte: start_addr: %lx, pte: %x, *pte: %x\n", start, pte, *pte );
//如果是两级页表的第一级,bit0是1,bit1是0.
//最后一个!pmd_present在这里跟pmd_none是一个逻辑
//如果是段表,第12行的note_page会被调用,这个处理的是pmd[0],处理完后
//第17行判断如果pmd[1]如果也是段表的话,再处理pmd[1]
//这里的SECTION_SIZE是1MB
//如果是二级页表的第一级的话,walk_pte会被调用
//这个函数会找到第二级页表的基地址,然后遍历其中的每一个二级页表项
//pmd = c0007ff8, *pmd = 6b7fd861
//walk_pte: start_addr: ffe00000, pte: eb7fd000, *pte: 0
//*c0007ff8 = 0x6b7fd 861 *c0007ffc=0x6b7fd c61
//861 说明是一个页表描述符 一级页表地址为eb7fd000 - eb7fe000
//取得虚地址 0xeb7fd000 (0x6b7fd861 & 0xfffff000)+ 0x8000 0000
//eb7fd7c0: df e5 7f 6b df f4 7f 6b 00 00 00 00 00 00 00 00
// 7c0 /4 = 1f0
//从ffe00000 地址开始 每4k占用一个1级页表项
//1f0*4k=1F0000
//ffe00000 + 1F0000 = ffff0000
//虚拟地址 ffff0000对应的二级页表描述符是 df e5 7f 6b, 0x6b7fe5df
//0xeb7fe000
//[ 4657.441225] wsm_buf.data1x: eb7fe000: ff 03 00 ea 65 04 00 ea f0 ff 9f e5 43 04 00 ea ....e.......C...
//[ 4657.452660] wsm_buf.data1x: eb7fe010: 22 04 00 ea 81 04 00 ea 00 04 00 ea 87 04 00 ea "...............
//[ 4657.463714] wsm_buf.data1x: eb7fe020: f1 de fd e7 f1 de fd e7 f1 de fd e7 f1 de fd e7 ................
//[ 4657.474955] wsm_buf.data1x: eb7fe030: f1 de fd e7 f1 de fd e7 f1 de fd e7 f1 de fd e7 ................
//从数据观察 应该是Vectors
#if 0
[ 2604.230527] addr = ffff0000
[ 2604.230529] val = 6b7fe5df
[ 2604.230530] st->current_prot = 0
[ 2604.230531] prot = 59c
[ 2604.230533] ---[ Vectors ]---
[ 2604.230534] addr = ffff1000
[ 2604.230535] val = 6b7ff4df
[ 2604.230537] st->current_prot = 59c
[ 2604.230538] prot = 49c
[ 2604.230546] 0xffff0000-0xffff1000 4K USR ro x SHD MEM/CACHED/WBWA
[ 2604.230554] 0xffff1000-0xffff2000 4K ro x SHD MEM/CACHED/WBWA
[ 2604.230555] ---[ Vectors End ]---
[ 2604.230600] parse.2
[ 2604.230601] parse.3
#endif
// 如果采用页表映射的方式,段映射表就变成一级映射表(Linux中称为PGD),其页表项提供的不再是物理地址,而是二级页表的基地址。
//32位虚拟地址的高12位(bit[31:20])作为访问一级页表的索引值,找到相应的表项,每个表项指向一个二级页表。
//以虚拟地址的次8位(bit[19:12])作为访问二级页表的索引值,得到相应的页表项,从这个页表项中找到20位的物理页面地址。
//最后将这20位物理页面地址和虚拟地址的低12位拼凑在一起,得到最终的32位物理地址。
//这个过程在ARM32架构中由MMU硬件完成,软件不需要介入。
if( start == 0xffe00000 ){
g_debug_en = 0x01;
}else{
g_debug_en = 0x00;
}
for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
addr = start + i * PAGE_SIZE;
if( g_debug_en){
//printk("walk_pte: start_addr: %lx, pte: %x, *pte: %x\n", addr, pte, *pte );
}
note_page(st, addr, 4, pte_val(*pte));
}
}
static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
{
pmd_t *pmd = pmd_offset(pud, 0);
unsigned long addr;
unsigned i;
//printk( "PTRS_PER_PMD = 0x%x\n", PTRS_PER_PMD );
//printk( "PMD_SIZE = 0x%x\n", PMD_SIZE );
//printk( "SECTION_SIZE = 0x%x\n", SECTION_SIZE );
//PTRS_PER_PMD = 0x1
//PMD_SIZE = 0x200000
//SECTION_SIZE = 0x100000
if( pmd < 0xc0007000 ){
//return;
}
if( pmd > 0xc0007100 ){
//printk("debug quit!!!\n");
//return;
}
//g_debug_en = 0x01;
for( i=0; i<PTRS_PER_PMD; i++, pmd++ ){
addr = start + i * PMD_SIZE;
//printk( "pmd = 0x%p\n", pmd );
//printk( "*pmd = 0x%x\n", *pmd );
//pmd_large判断段表还是两级页表的第一级,这里实际上是判断表项的[1:0]
//如果是段表,bit0任意,bit1位1; 1e 14 01 40
//0x400 1141e e 1110 1141e 是段描述符条目的属性
//对于段映射 假设逻辑地址是0xc0000001
//cpu首先从页表基址寄存器 取得页表基址 0xc0004000
//从0xc0004000开始 每4字节 为一个条目 每条目寻址1M
//0x1000 4k字节有1k的条目数 可寻址1G
//0xc0004000-0xc0005000 映射逻辑地址 0x00000000-0x40000000
//0xc0005000-0xc0006000 映射逻辑地址 0x40000000-0x80000000
//0xc0006000-0xc0007000 映射逻辑地址 0x80000000-0xc0000000
//上面这些表项都是0 用户空间 0-3G
//由此开始则是内核空间的映射 3G-4G
//*0xc0007000 = 0x4001141e,由e判断出是一个段映射 这个条目管1M的空间
//0x4000 0000-0x4010 0000
//也就是说物理地址 0x4000 0000-0x4010 0000 对应的内核态 虚拟地址是 0xc000 0000 - 0xc010 0000
//如果是两级页表的第一级,bit0是1,bit1是0.
//最后一个!pmd_present在这里跟pmd_none是一个逻辑
//如果是段表,第12行的note_page会被调用,这个处理的是pmd[0],处理完后
//第17行判断如果pmd[1]如果也是段表的话,再处理pmd[1]
//这里的SECTION_SIZE是1MB
//如果是二级页表的第一级的话,walk_pte会被调用
//这个函数会找到第二级页表的基地址,然后遍历其中的每一个二级页表项
//pmd = c0007ff8, *pmd = 6b7fd861
//walk_pte: start_addr: ffe00000, pte: eb7fd000, *pte: 0
//*c0007ff8 = 0x6b7fd861 *c0007ffc=0x6b7fdc61
if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd)){
//printk( "pmd.q\n" );
//表项为0 或者是段表项
//printk( "\n\npmd.seg table0:\n" );
note_page(st, addr, 3, pmd_val(*pmd));
}else{
//printk( "\n\npmd.page table:\n" );
//非0的页表项 二级页表映射
walk_pte(st, pmd, addr);
}
if( SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])){
//printk( "pmd.seg table1:\n" );
note_page(st, addr + SECTION_SIZE, 3, pmd_val(pmd[1]));
//下一个段表项条目
}
}
}
static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
{
pud_t *pud = pud_offset(pgd, 0);
unsigned long addr;
unsigned i;
//printk( "PTRS_PER_PUD = 0x%x\n", PTRS_PER_PUD );
//printk( "PUD_SIZE = 0x%x\n", PUD_SIZE );
//PTRS_PER_PUD = 0x1
//PUD_SIZE = 0x200000
for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
addr = start + i * PUD_SIZE;
if (!pud_none(*pud)) {
//printk("parse.4 \n");
walk_pmd(st, pud, addr);
} else {
printk("parse.5 \n");
note_page(st, addr, 2, pud_val(*pud));
}
}
}
static void walk_pgd(struct seq_file *m)
{
pgd_t *pgd = swapper_pg_dir;
struct pg_state st;
unsigned long addr;
unsigned i;
memset(&st, 0, sizeof(st));
st.seq = m;
st.marker = address_markers;
//printk("PTRS_PER_PGD = 0x%x\n", PTRS_PER_PGD);
//printk("PGDIR_SIZE = 0x%x\n", PGDIR_SIZE);
//PTRS_PER_PGD = 0x800
//PGDIR_SIZE = 0x200000
//i 2048次循环
//每次的地址是2M
//2k * 2M = 4G
for( i=0; i<PTRS_PER_PGD; i++,pgd++ ){
addr = i * PGDIR_SIZE;
//printk("walk_pgd pgd = 0x%p\n", pgd);
//printk("walk_pgd addr = 0x%x\n", addr);
if( !pgd_none(*pgd) ){
//printk("parse.0 \n");
walk_pud(&st, pgd, addr);
} else {
printk("parse.1 \n");
note_page(&st, addr, 1, pgd_val(*pgd));
}
}
printk("parse.2 \n");
note_page(&st, 0, 0, 0);
printk("parse.3 \n");
}
static int ptdump_show(struct seq_file *m, void *v)
{
walk_pgd(m);
return 0;
}
static int ptdump_open(struct inode *inode, struct file *file)
{
return single_open(file, ptdump_show, NULL);
}
static const struct file_operations ptdump_fops = {
.open = ptdump_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int pttest_show(struct seq_file *m, void *v)
{
unsigned int pgd;
printk("in pttest_show\n");
pgd = cpu_get_pgd();
printk("pgd = 0x%lx\n", pgd);
printk("init_mm.pgd = 0x%lx\n", init_mm.pgd);
return 0;
}
static int pttest_open(struct inode *inode, struct file *file)
{
return single_open(file, pttest_show, NULL);
}
static unsigned char k_buff[1024];
static ssize_t pttest_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
struct seq_file *sf = (struct seq_file *)file->private_data;
unsigned long testcase;
int ret;
ret = kstrtoul_from_user(buf, count, 16, &testcase);
if (ret){
return ret;
}
printk("testcase = 0x%lx\n", testcase);
print_hex_dump_bytes("wsm_buf.data1x: ", DUMP_PREFIX_ADDRESS, (u8 *)(testcase), 0x100 );
return count;
}
static const struct file_operations pttest_fops = {
.open = pttest_open,
.read = seq_read,
.write = pttest_write,
.llseek = seq_lseek,
.release = single_release,
};
static int ptdump_init( void )
{
struct dentry *pe;
unsigned i, j;
//#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
//printk( "in ptdump_init\n" );
//printk( "ARRAY_SIZE(pg_level) = %d\n", ARRAY_SIZE(pg_level) );
//printk( "VMALLOC_START = %x\n", VMALLOC_START );
//printk( "high_memory = %x\n", high_memory );
//[ 0.893023] ARRAY_SIZE(pg_level) = 5
//[ 0.896949] VMALLOC_START = ec000000
//[ 0.900896] high_memory = eb800000
//[ 0.904633] i = 3
//[ 0.906787] pg_level[i].mask = 18c10
//[ 0.910709] i = 4
//[ 0.912889] pg_level[i].mask = 7bc
for( i=0; i<ARRAY_SIZE(pg_level); i++ ){
if( pg_level[i].bits ){
for( j=0; j<pg_level[i].num; j++ ){
pg_level[i].mask |= pg_level[i].bits[j].mask;
}
//printk( "i = %x\n", i );
//printk( "pg_level[i].mask = %llx\n", pg_level[i].mask );
}
}
address_markers[2].start_address = VMALLOC_START;
debugfs_create_file("pttest", 0400, NULL, NULL, &pttest_fops);
pe = debugfs_create_file("kernel_page_tables", 0400, NULL, NULL, &ptdump_fops);
return pe ? 0 : -ENOMEM;
}
__initcall(ptdump_init);
本文档详细介绍了Linux内核在不同体系结构下如何遍历和解析页表,包括从pgd、pud、pmd到pte的逐级遍历,以及在特定体系结构(如ARM)中的页表属性处理。通过地址标记和页表级别,展示了内存区域的划分和权限设置。此外,还包含了调试辅助函数以输出当前系统的页表信息。
7396

被折叠的 条评论
为什么被折叠?



