鸿蒙轻内核A核源码分析系列五(3) 虚实映射

举报
zhushy 发表于 2021/11/25 16:57:45 2021/11/25
【摘要】 5、虚实映射解除函数LOS_ArchMmuUnmap虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项地址。⑵处计算需要解除的无效映射的数量。如果页表描述符映射类型为Section,并且映射的数量超过256,则执行⑶解除映射Section。如果页表描述符映射类型为Page Tab...

5、虚实映射解除函数LOS_ArchMmuUnmap

虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项地址。⑵处计算需要解除的无效映射的数量。如果页表描述符映射类型为Section,并且映射的数量超过256,则执行⑶解除映射Section。如果页表描述符映射类型为Page Table,则执行⑷先解除二级页表映射,然后解除一级页表映射,涉及的2个函数后文详细分析。⑹处函数使TLB失效,涉及些cp15寄存器和汇编,后续再分析。

STATUS_T LOS_ArchMmuUnmap(LosArchMmu *archMmu, VADDR_T vaddr, size_t count)
{
    PTE_T l1Entry;
    INT32 unmapped = 0;
    UINT32 unmapCount = 0;

    while (count > 0) {
⑴      l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);
        if (OsIsPte1Invalid(l1Entry)) {
⑵          unmapCount = OsUnmapL1Invalid(&vaddr, &count);
        } else if (OsIsPte1Section(l1Entry)) {
            if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) && count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) {
⑶              unmapCount = OsUnmapSection(archMmu, &vaddr, &count);
            } else {
                LOS_Panic("%s %d, unimplemented\n", __FUNCTION__, __LINE__);
            }
        } else if (OsIsPte1PageTable(l1Entry)) {
⑷          unmapCount = OsUnmapL2PTE(archMmu, vaddr, &count);
            OsTryUnmapL1PTE(archMmu, vaddr, OsGetPte2Index(vaddr) + unmapCount,
                            MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - unmapCount);
⑸          vaddr += unmapCount << MMU_DESCRIPTOR_L2_SMALL_SHIFT;
        } else {
            LOS_Panic("%s %d, unimplemented\n", __FUNCTION__, __LINE__);
        }
        unmapped += unmapCount;
    }OsArmInvalidateTlbBarrier();
    return unmapped;
}

5.1 函数OsUnmapL1Invalid

函数OsUnmapL1Invalid用于解除无效的映射,会把虚拟地址增加,映射的数量减少。⑴处的MMU_DESCRIPTOR_L1_SMALL_SIZE表示1MiB大小,*vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE对1MiB取余,向右偏移12位>>MMU_DESCRIPTOR_L2_SMALL_SHIFT表示大小转换为内存页数量。(为啥相减TODO?)。⑵处把解除映射的内存页数量左移12位转换为地址长度,然后更新虚拟地址。⑶处减去已经解除映射的数量。

STATIC INLINE UINT32 OsUnmapL1Invalid(vaddr_t *vaddr, UINT32 *count)
{
    UINT32 unmapCount;

⑴  unmapCount = MIN2((MMU_DESCRIPTOR_L1_SMALL_SIZE - (*vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE)) >>
        MMU_DESCRIPTOR_L2_SMALL_SHIFT, *count);*vaddr += unmapCount << MMU_DESCRIPTOR_L2_SMALL_SHIFT;*count -= unmapCount;

    return unmapCount;
}

5.2 函数OsUnmapSection

函数OsUnmapSection用于接触一级页表的Section映射。⑴处把虚拟地址对应的页表项基地址设置为0。⑵处使TLB寄存器失效,⑶更新虚拟地址和映射数量。

STATIC UINT32 OsUnmapSection(LosArchMmu *archMmu, vaddr_t *vaddr, UINT32 *count)
{OsClearPte1(OsGetPte1Ptr((PTE_T *)archMmu->virtTtb, *vaddr));OsArmInvalidateTlbMvaNoBarrier(*vaddr);*vaddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;
    *count -= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;

    return MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
}

5.3 函数OsUnmapL2PTE

函数OsUnmapL2PTE用于。⑴处先调用函数OsGetPte1计算虚拟地址对应页表项,然后调用函数OsGetPte2BasePtr计算二级页表基地址。⑵处获取虚拟地址的二级页表项索引。⑶计算需要解除映射的数量(为啥取最小值 TODO)。⑷处依次解除各个二级页表的映射。⑸使TLB失效。

STATIC UINT32 OsUnmapL2PTE(const LosArchMmu *archMmu, vaddr_t vaddr, UINT32 *count)
{
    UINT32 unmapCount;
    UINT32 pte2Index;
    PTE_T *pte2BasePtr = NULL;

⑴  pte2BasePtr = OsGetPte2BasePtr(OsGetPte1((PTE_T *)archMmu->virtTtb, vaddr));
    if (pte2BasePtr == NULL) {
        LOS_Panic("%s %d, pte2 base ptr is NULL\n", __FUNCTION__, __LINE__);
    }

⑵  pte2Index = OsGetPte2Index(vaddr);
⑶  unmapCount = MIN2(MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 - pte2Index, *count);

    /* unmap page run */OsClearPte2Continuous(&pte2BasePtr[pte2Index], unmapCount);

    /* invalidate tlb */OsArmInvalidateTlbMvaRangeNoBarrier(vaddr, unmapCount);

    *count -= unmapCount;
    return unmapCount;
}

6 其他函数

6.1 映射属性修改函数LOS_ArchMmuChangeProt

函数LOS_ArchMmuChangeProt用于修改进程空间虚拟地址区间的映射属性,其中参数archMmu为进程空间的MMU信息,vaddr为虚拟地址,count为映射的页数,flags为映射使用的新标签属性信息。⑴处对参数进行校验,⑵处查询虚拟地址映射的物理地址,如果没有映射则执行⑶把虚拟地址增加1个内存页大小继续修改下一个内存页的属性。⑷处先解除当前内存页的映射,然后执行⑸使用新的映射属性重新映射,⑹处虚拟地址增加1个内存页大小继续修改下一个内存页的属性。

STATUS_T LOS_ArchMmuChangeProt(LosArchMmu *archMmu, VADDR_T vaddr, size_t count, UINT32 flags)
{
    STATUS_T status;
    PADDR_T paddr = 0;if ((archMmu == NULL) || (vaddr == 0) || (count == 0)) {
        VM_ERR("invalid args: archMmu %p, vaddr %p, count %d", archMmu, vaddr, count);
        return LOS_NOK;
    }

    while (count > 0) {
⑵      count--;
        status = LOS_ArchMmuQuery(archMmu, vaddr, &paddr, NULL);
        if (status != LOS_OK) {
⑶          vaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
            continue;
        }

⑷      status = LOS_ArchMmuUnmap(archMmu, vaddr, 1);
        if (status < 0) {
            VM_ERR("invalid args:aspace %p, vaddr %p, count %d", archMmu, vaddr, count);
            return LOS_NOK;
        }

⑸      status = LOS_ArchMmuMap(archMmu, vaddr, paddr, 1, flags);
        if (status < 0) {
            VM_ERR("invalid args:aspace %p, vaddr %p, count %d",
                   archMmu, vaddr, count);
            return LOS_NOK;
        }
⑹      vaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
    }
    return LOS_OK;
}

6.2 映射转移函数LOS_ArchMmuMove

函数LOS_ArchMmuMove用于将进程空间一个虚拟地址区间的映射关系转移至另一块未使用的虚拟地址区间重新做映射,其中参数oldVaddr为老的虚拟地址,newVaddr为新的虚拟内存地址,flags在重新映射时可以更改映射属性信息。⑴处先查询老的虚拟地址映射的物理内存。如果没有映射关系,把新旧虚拟内存都增加一个内存页的大小,⑵处取消老的虚拟地址的映射,⑶处使用新的虚拟内存重新映射到查询到的物理内存地址。⑷把新旧虚拟内存都增加一个内存页的大小,继续处理下一个内存页。

STATUS_T LOS_ArchMmuMove(LosArchMmu *archMmu, VADDR_T oldVaddr, VADDR_T newVaddr, size_t count, UINT32 flags)
{
    STATUS_T status;
    PADDR_T paddr = 0;

    if ((archMmu == NULL) || (oldVaddr == 0) || (newVaddr == 0) || (count == 0)) {
        VM_ERR("invalid args: archMmu %p, oldVaddr %p, newVddr %p, count %d",
               archMmu, oldVaddr, newVaddr, count);
        return LOS_NOK;
    }

    while (count > 0) {
        count--;
⑴      status = LOS_ArchMmuQuery(archMmu, oldVaddr, &paddr, NULL);
        if (status != LOS_OK) {
            oldVaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
            newVaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
            continue;
        }
        // we need to clear the mapping here and remain the phy page.
⑵      status = LOS_ArchMmuUnmap(archMmu, oldVaddr, 1);
        if (status < 0) {
            VM_ERR("invalid args: archMmu %p, vaddr %p, count %d",
                   archMmu, oldVaddr, count);
            return LOS_NOK;
        }

⑶      status = LOS_ArchMmuMap(archMmu, newVaddr, paddr, 1, flags);
        if (status < 0) {
            VM_ERR("invalid args:archMmu %p, old_vaddr %p, new_addr %p, count %d",
                   archMmu, oldVaddr, newVaddr, count);
            return LOS_NOK;
        }
⑷      oldVaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
        newVaddr += MMU_DESCRIPTOR_L2_SMALL_SIZE;
    }

    return LOS_OK;
}

小结

本文介绍了MMU虚实映射的基本概念,运行机制,分析了映射初始化、映射查询、映射虚拟内存和物理内存,解除虚实映射,更改映射属性,重新映射等常用接口的代码。感谢阅读,有什么问题,请留言。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。