RISC-V架构的Linux内核启动过程中,页表创建是一个关键步骤,它实现了从物理地址到虚拟地址的映射,为内核运行提供了内存管理基础。本文将详细分析RISC-V Linux启动过程中页表的创建流程。
在RISC-V Linux启动过程中,内存管理经历了几个阶段:
在arch/riscv/kernel/head.S
中,内核首先建立早期页表:
#ifdef CONFIG_MMU
/* 设置早期页表 */
la a0, early_pg_dir
call setup_vm
#endif
setup_vm
函数负责建立早期虚拟内存映射:
void __init setup_vm(uintptr_t dtb_pa)
{
uintptr_t pa = load_pa;
uintptr_t va = PAGE_OFFSET;
/* 创建1:1映射 */
create_pgd_mapping(early_pg_dir, va, pa,
PGDIR_SIZE, PAGE_KERNEL_EXEC);
/* 创建线性映射 */
create_pgd_mapping(early_pg_dir, kernel_map.virt_addr,
kernel_map.phys_addr, kernel_map.size,
PAGE_KERNEL_EXEC);
}
RISC-V使用三级页表结构(Sv39/Sv48): - PGD (Page Global Directory) - PUD (Page Upper Directory) - PMD (Page Middle Directory) - PTE (Page Table Entry)
typedef struct {
unsigned long pgd;
} pgd_t;
typedef struct {
unsigned long pud;
} pud_t;
typedef struct {
unsigned long pmd;
} pmd_t;
typedef struct {
unsigned long pte;
} pte_t;
RISC-V页表项包含以下关键标志位: - V (Valid) - R (Readable) - W (Writable) - X (eXecutable) - U (User accessible) - G (Global) - A (Accessed) - D (Dirty)
早期页表初始化:
setup_vm
中创建1:1映射和线性映射create_pgd_mapping
函数建立映射关系临时页表切换:
early_pg_dir
加载到satp
寄存器最终页表创建:
paging_init
中调用setup_vm_final
static void __init create_pgd_mapping(pgd_t *pgdp,
uintptr_t va, uintptr_t pa,
uintptr_t sz, pgprot_t prot)
{
/* 逐级创建页表项 */
pgd_t *pgd = pgd_offset_pgd(pgdp, va);
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
if (pgd_none(*pgd)) {
pud = early_alloc(PTRS_PER_PUD * sizeof(pud_t));
pgd_populate(pgdp, pgd, pud);
}
pud = pud_offset(pgd, va);
if (pud_none(*pud)) {
pmd = early_alloc(PTRS_PER_PMD * sizeof(pmd_t));
pud_populate(pud, pmd);
}
pmd = pmd_offset(pud, va);
if (pmd_none(*pmd)) {
pte = early_alloc(PTRS_PER_PTE * sizeof(pte_t));
pmd_populate(pmd, pte);
}
pte = pte_offset_kernel(pmd, va);
set_pte(pte, pfn_pte(pa >> PAGE_SHIFT, prot));
}
void __init setup_vm_final(void)
{
/* 创建swapper_pg_dir */
create_pgd_mapping(swapper_pg_dir, FIXADDR_START,
__pa_symbol(fixmap_pgd_next),
PGDIR_SIZE, PAGE_TABLE);
/* 映射内核 */
map_kernel();
/* 映射设备树 */
map_fdt();
/* 加载新页表 */
csr_write(CSR_SATP, PFN_DOWN(__pa_symbol(swapper_pg_dir)) | SATP_MODE);
local_flush_tlb_all();
}
查看页表内容:
cat /proc/meminfo | grep PageTables
内核调试打印:
#define DEBUG_PGTABLE 1
QEMU调试:
qemu-system-riscv64 -d guest_errors,page,trace:riscv_*_pagetable
RISC-V Linux的页表创建过程从简单的1:1映射开始,逐步建立完整的虚拟内存系统。理解这一过程对于内核开发、性能调优和问题诊断都至关重要。随着RISC-V生态的发展,页表管理可能会引入更多优化和新特性。