0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

页表创建源码分析RISC-V Linux

麦辣鸡腿堡 来源:嵌入式Linux充电站 作者:Vincent 2023-10-08 11:22 次阅读

页表创建源码分析

RISC-V Linux启动,经历了两次页表创建过程,第一次使用C函数setup_vm()创建临时页表,第二次使用C函数setup_vm_final()创建最终页表。

具体细节参考代码中的注释,下面的代码省略了一些不重要的部分。

setup_vm()

asmlinkage void __init setup_vm(uintptr_t dtb_pa)
{
 uintptr_t va, pa, end_va;
 uintptr_t load_pa = (uintptr_t)(&_start);
 uintptr_t load_sz = (uintptr_t)(&_end) - load_pa;
 uintptr_t map_size;
 //load_pa就是kernel加载的其实物理地址
    //load_sz就是kernel的实际大小

    //page_offset就是kernel的起始物理地址对应的虚拟地址,va_pa_offset是他们的偏移量
 va_pa_offset = PAGE_OFFSET - load_pa;
    
    //计算得到kernel起始物理地址的物理页,PFN_DOWN是将物理地址右移12位,因为sv39的物理地址的低12位是pa_offset,所以右移12位,得到pfn
 pfn_base = PFN_DOWN(load_pa);

 map_size = PMD_SIZE;//PMD_SIZE为2M,在当前,map_size只能为PGDIR_SIZE或PMD_SIZE。这时kernel默认不允许建立PTE。

 //检查PAGE_OFFSET是否1G对齐,以及kernel入口地址是否2M对齐
 BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0);
 BUG_ON((load_pa % map_size) != 0);

    //allc_pte_early里面是BUG(),对于临时页表,kernel不允许我们建立PTE
 pt_ops.alloc_pte = alloc_pte_early;
 pt_ops.get_pte_virt = get_pte_virt_early;
#ifndef __PAGETABLE_PMD_FOLDED
 pt_ops.alloc_pmd = alloc_pmd_early;
 pt_ops.get_pmd_virt = get_pmd_virt_early;
#endif
 /* 设置 early PGD for fixmap */
 create_pgd_mapping(early_pg_dir, FIXADDR_START,
      (uintptr_t)fixmap_pgd_next, PGDIR_SIZE, PAGE_TABLE);


 /* 设置 fixmap PMD */
 create_pmd_mapping(fixmap_pmd, FIXADDR_START,
      (uintptr_t)fixmap_pte, PMD_SIZE, PAGE_TABLE);
 /* 设置 trampoline PGD and PMD */
 create_pgd_mapping(trampoline_pg_dir, PAGE_OFFSET,
      (uintptr_t)trampoline_pmd, PGDIR_SIZE, PAGE_TABLE);
 create_pmd_mapping(trampoline_pmd, PAGE_OFFSET,
      load_pa, PMD_SIZE, PAGE_KERNEL_EXEC);

 /*
  * 设置覆盖整个内核的早期PGD,这将使我们能够达到paging_init()。
  * 稍后在下面的 setup_vm_final() 中映射所有内存。
  */
 end_va = PAGE_OFFSET + load_sz;
 for (va = PAGE_OFFSET; va < end_va; va += map_size)
  create_pgd_mapping(early_pg_dir, va,
       load_pa + (va - PAGE_OFFSET),
       map_size, PAGE_KERNEL_EXEC);

 /* 为dtb创建早期的PMD */
 create_pgd_mapping(early_pg_dir, DTB_EARLY_BASE_VA,
      (uintptr_t)early_dtb_pmd, PGDIR_SIZE, PAGE_TABLE);
 /* 为 FDT 早期扫描创建两个连续的 PMD 映射 */
 pa = dtb_pa & ~(PMD_SIZE - 1);
 create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA,
      pa, PMD_SIZE, PAGE_KERNEL);
 create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA + PMD_SIZE,
      pa + PMD_SIZE, PMD_SIZE, PAGE_KERNEL);
 dtb_early_va = (void *)DTB_EARLY_BASE_VA + (dtb_pa & (PMD_SIZE - 1));
 ......

}

setup_vm()在最开始就进行了kernel入口地址的对齐检查,要求入口地址2M对齐。假设内存起始地址为0x80000000,那么kernel只能放在0x80000000、0x80200000等2M对齐处。为什么会有这种对齐要求呢?

我猜测单纯是为给opensbi预留了2M空间,因为kernel之前还有opensbi,而opensbi运行完之后,默认跳转地址就是偏移2M,kernel只是为了跟opensbi对应,所以设置了2M对齐。

那opensbi需要占用2M这么大?实际上只需要几百KB,因此opensbi和kernel中间有一段内存是空闲的,没有人使用。这个问题我们下篇再讲。

setup_vm_final()

在该函数中开始为整个物理内存做内存映射,通过swapper页表来管理,并且清除掉汇编阶段的页表。

static void __init setup_vm_final(void)
{
 uintptr_t va, map_size;
 phys_addr_t pa, start, end;
 u64 i;

 /**
  * 此时MMU已经开启,但是页表还没完全建立。
  */
 pt_ops.alloc_pte = alloc_pte_fixmap;
 pt_ops.get_pte_virt = get_pte_virt_fixmap;
#ifndef __PAGETABLE_PMD_FOLDED
 pt_ops.alloc_pmd = alloc_pmd_fixmap;
 pt_ops.get_pmd_virt = get_pmd_virt_fixmap;
#endif
 /* Setup swapper PGD for fixmap */
 create_pgd_mapping(swapper_pg_dir, FIXADDR_START,
      __pa_symbol(fixmap_pgd_next),
      PGDIR_SIZE, PAGE_TABLE);

 /* 为整个物理内存创建页表 */
 for_each_mem_range(i, &start, &end) {
  if (start >= end)
   break;
  if (start <= __pa(PAGE_OFFSET) &&
      __pa(PAGE_OFFSET) < end)
   start = __pa(PAGE_OFFSET);

        //best_map_size是选择合适的映射大小,kernel入口地址2M对齐或者kernel大小能被2M整除时,map_size就是2M,否则就是4K。
  map_size = best_map_size(start, end - start);
  for (pa = start; pa < end; pa += map_size) {
   va = (uintptr_t)__va(pa);
   create_pgd_mapping(swapper_pg_dir, va, pa,
        map_size, PAGE_KERNEL_EXEC);
  }
 }

 /* 清除fixmap的PMD和PTE */
 clear_fixmap(FIX_PTE);
 clear_fixmap(FIX_PMD);

 /* 切换到swapper页表,这个是最终的页表,汇编阶段relocate开启MMU的操作,跟下面这句是一样的。 */
 csr_write(CSR_SATP, PFN_DOWN(__pa_symbol(swapper_pg_dir)) | SATP_MODE);
 local_flush_tlb_all();//刷新TLB

 ......
}

说明:

在setup_vm_final()函数中,通过swapper_pg_dir页表来管理整个物理内存的访问。并且清除汇编阶段的页表fixmap_pte和early_pg_dir。(本质上就是把该页表项的内容清0,即赋值为0)

最终把swapper_pg_dir页表的物理地址赋值给SATP寄存器。这样CPU就可以通过该页表访问整个物理内存。

切换页表通过如下实现:

csr_write(CSR_SATP,PFN_DOWN(_pa(swapper_pg_dir))|SATP_MODE);

在swapper_pg_dir管理的kernel space中,其虚拟地址与物理地址空间的偏移是固定的,为va_pa_offset(定义在arch/riscv/mm/init.c中的一个全局变量)

注意:swapper_pg_dir管理的是kernel space的页表,即它把物理内存映射到的虚拟地址空间是只能kernel访问的。user space不能访问,用户空间如果访问,必须自行建立页表,把物理地址映射到user space的虚拟地址空间。kernel线程共享这个swapper_pg_dir页表。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11304

    浏览量

    209503
  • 代码
    +关注

    关注

    30

    文章

    4788

    浏览量

    68613
  • RISC
    +关注

    关注

    6

    文章

    462

    浏览量

    83733
收藏 人收藏

    评论

    相关推荐

    自制RISC-V源码与设计流程案例分析

    Overlay 简介 RISC-V-On-PYNQ Overlay实现了在PYNQ-Z2板上的RISC-V处理器及工具链集成,并提供了完整的RISC-V源码与设计流程,得益于PYNQ软
    的头像 发表于 11-08 10:05 6587次阅读
    自制<b class='flag-5'>RISC-V</b><b class='flag-5'>源码</b>与设计流程案例<b class='flag-5'>分析</b>

    如何使用RISC-V创建自定义处理器

    采用RISC-V来替代其自定义体系结构。该体系结构吸引人的一个关键特征是,CPU开发人员可以在不牺牲为基本标准创建的工具和库的适用性的情况下,使RISC-V功能适应其需求。适应的关键在于了解R
    的头像 发表于 04-14 15:55 4154次阅读
    如何使用<b class='flag-5'>RISC-V</b><b class='flag-5'>创建</b>自定义处理器

    RISC-V能否复制Linux 的成功?》

    的产品,Linux成为开源软件发展的基石。 这种成功是否可以复制到开源硬件上呢?RISC-V这样的指令集架构(ISA)是否也可以像Linux内核作为开源软件的基础一样,成为开源硬件发展的基石呢? 这个
    发表于 11-26 20:20

    基于risc-v架构的芯片与linux系统兼容性讨论

    一直对基于RISC-V架构的芯片与Linux系统的兼容性比较感兴趣,查了各种资料,众说纷纭,在此整理一下学习内容,以备后用。这个复杂而重要的话题,涉及多个方面的考量。下面谈谈我的学习总结
    发表于 11-30 17:20

    科普RISC-V生态架构(认识RISC-V)

    RISC-V,逐步完成全线产品迁移到RISC-V定制架构;MicroSemi提供基于Risc-V+Linux+CNN加速器的AI解决方案;印度***则大力资助基于RISC-V的处理器项
    发表于 08-02 11:50

    分析RISC-V架构的不同之处

    0 RISC-V和其他开放架构有何不同如果仅从“免费”或“开放”这两点来评判,RISC-V架构并不是第一个做到免费或开放的处理器架构。在开始之前,我们先通过论述几个具有代表性的开放架构,来分析
    发表于 07-26 06:58

    RISC-V-Reader-Chinese-v2p1 RISC-V手册(中文) RISC-V开源指令集的指南

    RISC-V 手册 一本开源指令集的指南 本书是由 RISC-V 设计者 DAVID PATTERSON等亲自写的书。书写的非常精彩,和Risc-V一样非常简洁明了,没有废话,书本身也不厚,114
    发表于 04-22 18:04

    【转载】RISC-V 能打 50 年!risc-v 现在和未来的发展

    人性化的设定,大大降低了芯片的研究门槛,使 RISC-V 被誉为“半导体行业的 Linux”,广受开发者欢迎。据分析机构 Semico Research 的《RISC-V 市场
    发表于 02-27 20:02

    我了解的RISC-V

    几十条指令。 RISC-V的规范文档仅有145,而“特权架构文档”的篇幅也仅为91。 易于移植*nix 现代操作系统都做了特权级指令和用户级指令的分离,特权指令只能操作系统调用,而用户级指令才能
    发表于 03-19 10:52

    RISC-V的商业模式,国内外RISC-V的产业生态分析

    RISC-V将是智慧物联网创新发展的新机遇,本文首先介绍了什么是指令集架构,其次介绍了RISC-V的历史机遇、RISC-V的商业模式及中国CPU的未来展望,最后对国内外RISC-V的发
    的头像 发表于 06-22 16:14 3549次阅读
    <b class='flag-5'>RISC-V</b>的商业模式,国内外<b class='flag-5'>RISC-V</b>的产业生态<b class='flag-5'>分析</b>

    RISC-V的挑战分析

    虽然RISC-V相关的开发和应用方面已经取得了长足进步,但前面还有一些障碍。普林斯顿大学的研究人员发现了RISC-V开源处理器内核的一些缺陷,他们认为这些缺陷关系重大。他们发现,在RISC-V处理器
    的头像 发表于 02-25 09:41 2794次阅读

    RISC-VRISC-V AI的未来(特邀讲座)

    主题演讲:RISC-VRISC-V AI的未来(特邀讲座)ppt分享
    发表于 07-14 17:15 16次下载

    RISC-V设计支持工具,支持RISC-V技术的基础

    RISC-V设计支持工具,支持RISC-V技术的基础 ppt分享
    发表于 07-14 17:15 12次下载

    Arch Linux RISC-V 端口及相关作品简介

    Arch Linux RISC-V 端口及相关作品简介 演讲ppt分享
    发表于 07-17 16:34 6次下载

    RISC-V Linux创建

    临时分析 MMU开启前,需要建立好kernel、dtb、trampoline等。以便MMU开启后,并且在内存管理模块运行之前,ker
    的头像 发表于 10-08 11:14 580次阅读