static int pt_walk_init( void )
{
unsigned long virt;
pgd_t *pgd_base;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
uint32_t pgd_valx, pmd_valx, pte_valx;
unsigned long va;
unsigned long pfn_base;
unsigned long pa;
phys_addr_t calc_pa, real_pa;
unsigned long val1,val2,val3;
unsigned int *pgd_one;
unsigned int page_mode;unsigned long offset;
// 分配内核线性映射地址
virt = (unsigned long)__get_free_page(GFP_KERNEL);
if (!virt) {
pr_err("alloc page failed\n");
return -ENOMEM;
}
//pr_info("virt1 = %08lx\n", virt);
virt = vmalloc(PAGE_SIZE);
pr_info("virt2 = %08lx\n", virt);
va = virt;
//写入 VA
*(volatile unsigned long *)va = 0x12345678; //先访问一次,触发缺页,填充真实页表
*(volatile unsigned long *)(va+4) = 0x87654321;
//pr_info("va = %08lx\n", va);
//然后用 MMU 算出的物理地址读
calc_pa = (unsigned long)virt_to_phys((void *)va);
//pr_info("calc_pa = %08lx\n", calc_pa);
val1 = *(volatile unsigned long *)phys_to_virt(calc_pa);
//pr_info("val1 = %08lx\n", val1);
//pr_info("phys_to_virt(calc_pa) = %08lx\n", phys_to_virt(calc_pa) );
//PAGE_OFFSET = 0x80000000
//PHYS_OFFSET = 0x68000000
//步骤1:打印虚拟地址拆分 (硬件行为)
pr_info("[步骤1] 虚拟地址硬件拆分 (VA = 索引 + 偏移)\n");
unsigned int idx = (va >> 20) & 0xFFF; // 高12位 → PGD索引
unsigned int off = va & 0xFFFFF; // 低20位 → 偏移
pr_info("VA [31:20] 索引 = 0x%03x\n", idx);
pr_info("VA [19:0] 偏移 = 0x%05x\n\n", off);
pr_info("[步骤2] 读取 PGD 原始值\n");
pgd_base = init_mm.pgd;
pgd = pgd_offset(&init_mm, va);
pgd_valx = pgd_val(*pgd);
pr_info( "PGD base addr = 0x%08x\n", (unsigned int)pgd_base );
pr_info( "PGD calc addr = 0x%08x\n", (unsigned int)pgd );
pr_info( "PGD value = 0x%08x\n", pgd_valx );
page_mode = pgd_valx & 0x00000003;
pgd_one = pgd;
if( page_mode ==0x02){
pr_info( "打印内核内存布局真相\n" );
pr_info( "虚拟地址 VA = 0x%08lx\n", va );
pr_info( "真实物理地址 PA = 0x%08lx\n", (unsigned long)virt_to_phys((void *)va) );
pr_info( "VA - PAGE_OFFSET = 0x%08lx\n", va - PAGE_OFFSET );
pr_info( "VA - virt_to_phys = 0x%08lx\n", va - (unsigned long)virt_to_phys((void *)va) );
}
if( page_mode == 0x02 ){
if( (virt>>20) & 1 ){
pgd_one++;
pgd_valx = pgd_one[0];
pr_info("PGD 修正值 = 0x%08x\n", pgd_valx);
}
}
//HEXDUMP PGD 内存
pr_info("PGD 内存 hexdump:\n");
print_hex_dump(KERN_INFO, "PGD: ", DUMP_PREFIX_ADDRESS, 16, 4,
pgd_base+pgd_index(va), 0x40, false);
pr_info("\n");
//步骤3:解析 PGD 位格式(ARM Section 格式)
pr_info("[步骤3] 解析 PGD 硬件格式 \n");
if( page_mode == 2 ){
pr_info("Bit[1:0] 类型 = %d (10=Section 1MB大页)\n", page_mode );
}else{
pr_info("Bit[1:0] 类型 = %d (01=Section 4kB小页)\n", page_mode );
}
pr_info("Bit[4:2] AP = %d\n", (pgd_valx >> 2) & 7);
pr_info("Bit[5] CB = %d\n", (pgd_valx >> 5) & 1);
pr_info( "Bit[31:12] 基地址 = 0x%08x\n", (unsigned int)(pgd_valx & 0xFFF00000) );
if( page_mode == 2){
//步骤4:硬件计算最终物理地址
pr_info("\n[步骤4] 硬件计算最终物理地址\n");
pfn_base = (pgd_valx & 0xFFF00000);
pa = pfn_base | (va & 0x000FFFFF);
pr_info("PA = PGD[31:12] + VA[19:0]\n");
pr_info("PA = 0x%08lx + 0x%05x = 0x%08lx\n", pfn_base, off, pa);
}else if( page_mode == 0x1 ){
unsigned long pte_phys;
pr_info("\n[步骤5] 解析 PTE 硬件格式 \n");
pr_info("=> 4KB 小页模式 (PGD → PTE)\n");
//1.从 PGD 取出 PTE 物理地址
pte_phys = pgd_valx & 0xFFFFFC00;
pr_info("PTE 物理地址 = 0x%08lx\n", pte_phys);
//2.转虚拟地址 找到对应 PTE 项
pte = (pte_t *)phys_to_virt(pte_phys);
pr_info("pte1 = 0x%08lx\n", pte);
pr_info("pte_index(va) = 0x%08lx\n", pte_index(va));
//(((addr) >> 12) & (0x1ff)) Bit[31:21] Bit[20:12] Bit[11:0]
//获取 pte的索引数 Bit[20:12]
pte = pte + pte_index(va);
//pte基地址 + 索引偏移 得到pte条目的入口
pr_info("pte2 = 0x%08lx\n", pte);
print_hex_dump(KERN_INFO, "PTE: ", DUMP_PREFIX_ADDRESS, 16, 4,
pte, 0x20, false);
pte_valx = pte_val(*pte);
//取pte条目的值
pr_info("PTE 项地址 = 0x%08x, PTE 值 = 0x%08x\n", (unsigned int)pte, pte_valx );
// 3.计算最终物理地址 (4KB)
pfn_base = pte_valx & 0xFFFFF000;
offset = va & 0x00000FFF;
pa = pfn_base | offset;
print_hex_dump(KERN_INFO, "PTE: ", DUMP_PREFIX_ADDRESS, 16, 4,
phys_to_virt(pa), 0x40, false);
pr_info("PA = 0x%08lx\n", pa);
}else {
pr_err("不支持的页表类型: %d\n", page_mode);
}
// =========================================================================
// 总结
// =========================================================================
pr_info("\n=======================================================\n");
pr_info( "[硬件流程完成]\n" );
pr_info("VA = 0x%08lx --> PA = 0x%08lx\n", va, pa);
//free_page(virt);
return 0;
}
run log:
/ # cat /dev/test_char_dev
21.843404\] virt2 = 992ed000 \[ 21.843714\] \[步骤1\] 虚拟地址硬件拆分 (VA = 索引 + 偏移) \[ 21.844116\] VA \[31:20\] 索引 = 0x992 \[ 21.844347\] VA \[19:0\] 偏移 = 0xed000 \[ 21.844347
21.844764\] \[步骤2\] 读取 PGD 原始值 \[ 21.844990\] PGD base addr = 0x80004000 \[ 21.845208\] PGD calc addr = 0x80006648 \[ 21.845428\] PGD value = 0x7edfa811 \[ 21.845695\] PGD 内存 hexdump: \[ 21.845957\] PGD: 80006648: 7edfa811 7edfac11 7ede7811 7ede7c11 \[ 21.846231\] PGD: 80006658: 7ede6811 7ede6c11 7ede5811 7ede5c11 \[ 21.846504\] PGD: 80006668: 7ede4811 7ede4c11 7ede3811 7ede3c11 \[ 21.846819\] PGD: 80006678: 7ede2811 7ede2c11 7ede1811 7ede1c11 \[ 21.847083
21.847167\] \[步骤3\] 解析 PGD 硬件格式 \[ 21.847375\] Bit\[1:0\] 类型 = 1 (01=Section 4kB小页) \[ 21.847625\] Bit\[4:2\] AP = 4 \[ 21.847802\] Bit\[5\] CB = 0 \[ 21.847962\] Bit\[31:12\] 基地址 = 0x7ed00000 \[ 21.848163
21.848163\] \[步骤5\] 解析 PTE 硬件格式 \[ 21.848444\] =\> 4KB 小页模式 (PGD → PTE) \[ 21.848677\] PTE 物理地址 = 0x7edfa800 \[ 21.849121\] pte1 = 0x96dfa800 \[ 21.849309\] pte_index(va) = 0x000000ed \[ 21.849484\] pte2 = 0x96dfabb4 \[ 21.849705\] PTE: 96dfabb4: 7ef2101f 00000000 00000000 00000000 \[ 21.849986\] PTE: 96dfabc4: 00000000 00000000 00000000 00000000 \[ 21.850266\] PTE 项地址 = 0x96dfabb4, PTE 值 = 0x7ef2101f \[ 21.850612\] PTE: 96f21000: 12345678 87654321 00000000 00000000 \[ 21.850987\] PTE: 96f21010: 00000000 00000000 00000000 00000000 \[ 21.851288\] PTE: 96f21020: 00000000 00000000 00000000 00000000 \[ 21.851609\] PTE: 96f21030: 00000000 00000000 00000000 00000000 \[ 21.852043\] PA = 0x7ef21000 \[ 21.852220
21.852220\] ======================================================= \[ 21.852561\] \[硬件流程完成
21.852754\] VA = 0x992ed000 --\> PA = 0x7ef21000 A