实战 | RISC-V Linux入口地址2M预留内存优化

简介: 实战 | RISC-V Linux入口地址2M预留内存优化

上篇分析了RISC-V Linux启动的页表创建,提到RISC-V Linux入口地址必须2M对齐,今天讲讲如何解决2M对齐的问题,或者说如何优化部分内存。

注意:本文基于linux5.10.111内核

内存占用分析

每颗芯片在出厂时,其bootrom就已经固化在芯片内部,假设bootrom的地址是0x0,即上电后,会从0x0地址处开始运行程序。

在启动RISC-V Linux之前,需要先运行opensbi,因此应该把opensbi放到地址0x0处,这样芯片上电后,就会从0x0地址处执行opensbi。在opensbi运行完后,会跳转到opensbi运行地址偏移2M的位置去执行下一级boot(这里下一级boot是kernel),即跳转到0x200000地址处运行kernel,因此应该把kernel放到内存的0x200000处。

内存分布示意图如下:

对于kernel来说,在启动时会从自己的kernel加载地址处(即0x200000)开始建立页表映射,只有对物理内存建立了页表映射,后面才能访问这些内存。而kernel加载地址前面的2M内存(即0x0 - 0x200000)将被kernel忽略,不会对这2M内存建立页表,即kernel无法访问这2M内存。

在QEMU上RISC-V Linux的启动信息:

但opensbi实际不需要使用2M这么大的范围,默认是512KB,opensbi的pmp会保护这512KB内存,不让其他程序访问。

因此在Kernel和opensbi之间会存在1.5M的内存空隙,并且这部分内存空隙没有程序使用,这就会造成内存浪费,那如何让kernel将前面的一部分内存也利用起来呢?

优化方案

对这2M内存的优化,有两种方案:

方案一:将opensbi放到内存的最后面,kernel入口地址仍然保持2M对齐。

方案二:opensbi仍然放到内存的起始位置,通过修改内核源码,解除2M对齐限制,即可将kernel地址往前挪。

方案一

我们将opensbi放到内存的最后面,kernel入口地址仍然保持2M对齐。

即kernel放到内存的最前面,opensbi放到后面:

例如kernel放到内存的0x0地址处,opensbi放到内存的0x10000000地址处。这样kernel前面就不会有预留内存,只不过这样需要修改bootrom的地址,将地址从0x0修改为0x0x10000000。这种方案只适合芯片还没出厂前,因为用户无法修改bootrom的地址,芯片出厂后,bootrom地址是固定的,假设bootrom地址为0x0,那么芯片上电后,就会从0x0开始运行程序,所以opensbi必须放到0x0地址处,这样必然kernel只能往后偏移2M。

方案二

我们也可以修改RISC-V Linux的内核源码,解除2M对齐的限制。我们只需要在setup_vm()函数中,将原来的二级页表改为三级页表,这样kernel入口地址只需要4K对齐,因此就能将kernel往前挪,从而利用前面的内存。

修改代码

路径:arch/riscv/mm/init.c

注释原来的2M对齐检查:

对kernel的前2M页表映射由二级页表改为三级页表:

//新增一个PTE
pte_t trampoline_pte[PTRS_PER_PTE] __page_aligned_bss;
create_pgd_mapping(trampoline_pg_dir,PAGE_OFFSET,
                   (uintptr_t)trampoline_pmd,PGDIR_SIZE,PAGE_TABLE);
create_pmd_mapping(trampoline_pmd,PAGE_OFFSET,
                   (uintptr_t)trampoline_pte,PMD_SIZE,PAGE_TABLE);
end_va = PAGE_OFFSET + PMD_SIZE;
for (va = PAGE_OFFSET; va < end_va; va += PAGE_SIZE)
{
    create_pte_mapping(trampoline_pte,PAGE_OFFSET,
                   load_pa + (va - PAGE_OFFSET),
                       PAGE_SIZE,PAGE_KERNEL_EXEC);
}

对整个kernel的页表映射由二级页表改为三级页表:

假设kernel大小为4M+

//定义三个PTE
pte_t load_sz_pte[PTRS_PER_PTE] __page_aligned_bss;
pte_t load_sz_pte1[PTRS_PER_PTE] __page_aligned_bss;
pte_t load_sz_pte2[PTRS_PER_PTE] __page_aligned_bss;
//=======0-2M======
create_pgd_mapping(early_pg_dir,PAGE_OFFSET,
                   (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE);
create_pmd_mapping(early_pmd,PAGE_OFFSET,
                   (uintptr_t)load_sz_pte,PMD_SIZE,PAGE_TABLE);
end_va = PAGE_OFFSET + PMD_SIZE;
for (va = PAGE_OFFSET; va < end_va; va += PAGE_SIZE)
{
    create_pte_mapping(load_sz_pte,PAGE_OFFSET,
                   load_pa + (va - PAGE_OFFSET),
                       PAGE_SIZE,PAGE_KERNEL_EXEC);
}
//=======2-4M==========
create_pgd_mapping(early_pg_dir,PAGE_OFFSET + PMD_SIZE,
                   (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE);
create_pmd_mapping(early_pmd,PAGE_OFFSET,
                   (uintptr_t)load_sz_pte1,PMD_SIZE,PAGE_TABLE);
end_va = PAGE_OFFSET + (PMD_SIZE * 2);
for (va = PAGE_OFFSET + PMD_SIZE; va < end_va; va += PAGE_SIZE)
{
    create_pte_mapping(load_sz_pte1,va,
                   load_pa + (va - PAGE_OFFSET),
                       PAGE_SIZE,PAGE_KERNEL_EXEC);
}
//=======4-6M==========
create_pgd_mapping(early_pg_dir,PAGE_OFFSET + (PMD_SIZE*2),
                   (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE);
create_pmd_mapping(early_pmd,PAGE_OFFSET,
                   (uintptr_t)load_sz_pte2,PMD_SIZE,PAGE_TABLE);
end_va = PAGE_OFFSET + (PMD_SIZE * 3);
for (va = PAGE_OFFSET + (PMD_SIZE*2); va < end_va; va += PAGE_SIZE)
{
    create_pte_mapping(load_sz_pte2,va,
                   load_pa + (va - PAGE_OFFSET),
                       PAGE_SIZE,PAGE_KERNEL_EXEC);
}

通过以上的代码修改,就能将Kernel入口地址往前挪1.5M,只给opensbi预留512KB,这样RISC-V Linux启动之后,可用物理内存就会增加。

总结

RISC-V Linux入口地址2M对齐的操作目前还没看到有人解释,不过应该就是为了给opensbi预留2M,于是kernel只建立了二级页表,使得入口地址必须2M对齐。对这部分内存的优化解决方案,目前也还没人给出,希望本文的优化方案能够帮助到有些人,也希望能够给大家一些启发。

猜你喜欢:

RISC-V Linux启动之页表创建分析

RISC-V Linux汇编启动过程分析

RISC-V 入门笔记(新手必看!)

写给新手的MMU工作原理

OpenSBI三种固件的区别

内核调试之devmem直接读写寄存器

教你在QEMU上运行RISC-V Linux

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
相关文章
|
21天前
|
缓存 Java Linux
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
104 48
|
21天前
|
缓存 Linux
如何检查 Linux 内存使用量是否耗尽?
何检查 Linux 内存使用量是否耗尽?
|
1月前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
13天前
|
存储 算法 安全
深入理解Linux内核的内存管理机制
本文旨在深入探讨Linux操作系统内核的内存管理机制,包括其设计理念、实现方式以及优化策略。通过详细分析Linux内核如何处理物理内存和虚拟内存,揭示了其在高效利用系统资源方面的卓越性能。文章还讨论了内存管理中的关键概念如分页、交换空间和内存映射等,并解释了这些机制如何协同工作以提供稳定可靠的内存服务。此外,本文也探讨了最新的Linux版本中引入的一些内存管理改进,以及它们对系统性能的影响。
|
1月前
|
存储 缓存 监控
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
55 2
|
2月前
|
存储 缓存 监控
Linux中内存和性能问题
【10月更文挑战第5天】
40 4
|
2月前
|
算法 Linux
Linux中内存问题
【10月更文挑战第6天】
60 2
|
1月前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
36 0
|
2月前
|
存储 缓存 固态存储

热门文章

最新文章