3、虚实映射函数LOS_ArchMmuMap
从上文可知,用户程序加载启动时,会将代码段、数据段映射进虚拟内存空间,此时并没有物理页做实际的映射;程序执行时,如下图(图片来自OpenHarmony docs开源站点)粗箭头所示,CPU访问虚拟地址,通过MMU查找是否有对应的物理内存,若该虚拟地址无对应的物理地址则触发缺页异常,内核申请物理内存并将虚实映射关系及对应的属性配置信息写进页表,并把页表条目缓存至TLB,接着CPU可直接通过转换关系访问实际的物理内存;若CPU访问已缓存至TLB的页表条目,无需再访问保存在内存中的页表,可加快查找速度。本小节我们就详细分析下虚实映射函数的实现代码。
3.1 函数LOS_ArchMmuMap
函数LOS_ArchMmuMap
用于映射进程空间虚拟地址区间与物理地址区间,其中输入参数archMmu
为MMU结构体,vaddr
和paddr
分别是虚拟内存和物理内存的开始地址;count
为虚拟地址和物理地址映射的内存页数量;flags
为映射标签。
⑴处进行函数参数校验,不支持NON-SECURE的标记,虚拟地址和物理地址需要内存页4KiB对齐,参数检查函数代码简单,自行查看即可。
⑵处当虚拟地址、物理地址基于1MiB对齐,并且数量count
大于256时,使用Section页表项格式。
⑶处生成L1 section类型页表项并保存,下文详细分析该函数OsMapSection()
。如果不满足⑵处条件,需要使用L2映射。首先执行⑷处获取虚拟地址vaddr
对应的L1页表项,接着执行⑸处判断是否映射,如果没有对应的映射,则执行⑹处的函数OsMapL1PTE
生成L1 page table类型页表项并保存,然后执行函数OsMapL2PageContinous
生成L2 页表项并保存。如果已经映射为L1 page table页表项类型,则执行函数OsMapL2PageContinous
生成L2 页表项并保存。如果不是支持的页表项类型,则执行LOS_Panic()
触发异常。⑺处统计生成映射的调试,最终会返回映射成功的数量。
可以看出,在给定虚实内存地址和映射的内存页数后,使用L1页表映射还是L2页表映射的判断条件是:虚实内存地址是否1MiB内存对齐,并且映射数量是否大于256。当使用L1映射时,映射为Section页表项类型。当使用L2映射时,根据L1页表项类型,分布处理无效页表项和Page Table页表项类型这2种情况。具体的映射方式见下文。
status_t LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags)
{
PTE_T l1Entry;
UINT32 saveCounts = 0;
INT32 mapped = 0;
INT32 checkRst;
⑴ checkRst = OsMapParamCheck(flags, vaddr, paddr);
if (checkRst < 0) {
return checkRst;
}
/* see what kind of mapping we can use */
while (count > 0) {
⑵ if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) &&
MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(paddr) &&
count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) {
/* compute the arch flags for L1 sections cache, r ,w ,x, domain and type */
⑶ saveCounts = OsMapSection(archMmu, flags, &vaddr, &paddr, &cou