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

举报
zhushy 发表于 2021/11/25 16:56:51 2021/11/25
【摘要】 2.3 函数OsKSectionNewAttrEnable函数OsKSectionNewAttrEnable释放临时TTB。代码看不懂TODO 以后慢慢看。⑴处获取内核虚拟进程空间,⑵处设置进程空间MMU的虚拟地址转化表基地址TTB,设置物理内存地址转换表基地址。⑶处从CP15 C2寄存器读取TTB地址,取高20位。⑷处将内核页表基地址(逻辑与的什么?TODO)写入CP15 c2 TTB寄...

2.3 函数OsKSectionNewAttrEnable

函数OsKSectionNewAttrEnable释放临时TTB。代码看不懂TODO 以后慢慢看。⑴处获取内核虚拟进程空间,⑵处设置进程空间MMU的虚拟地址转化表基地址TTB,设置物理内存地址转换表基地址。⑶处从CP15 C2寄存器读取TTB地址,取高20位。⑷处将内核页表基地址(逻辑与的什么?TODO)写入CP15 c2 TTB寄存器。⑸处清空TLB缓冲区,然后释放内存。

STATIC VOID OsKSectionNewAttrEnable(VOID)
{
⑴  LosVmSpace *kSpace = LOS_GetKVmSpace();
    paddr_t oldTtPhyBase;

⑵  kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable;
    kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb);

    /* we need free tmp ttbase */
⑶  oldTtPhyBase = OsArmReadTtbr0();
    oldTtPhyBase = oldTtPhyBase & MMU_DESCRIPTOR_L2_SMALL_FRAME;OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS);
    ISB;

    /* we changed page table entry, so we need to clean TLB here */OsCleanTLB();

    (VOID)LOS_MemFree(m_aucSysMem0, (VOID *)(UINTPTR)(oldTtPhyBase - SYS_MEM_BASE + KERNEL_VMM_BASE));
}

3、虚实映射函数LOS_ArchMmuMap

虚实映射的知识点TODO

3.1 函数LOS_ArchMmuMap

函数LOS_ArchMmuMap用于映射进程空间虚拟地址区间与物理地址区间,其中输入参数archMmu为MMU配置结构体,vaddrpaddr分别是虚拟内存和物理内存的开始地址;count为虚拟地址和物理地址映射的数量;flags为映射标签。⑴处进行函数参数校验,不支持NON-SECURE的标记,虚拟地址和物理地址需要内存页4KiB对齐。⑵处当虚拟地址、物理地址基于1MiB对齐,并且数量count大于256时,使用Section页表项格式。⑶处生成L1 section类型页表项并保存,下文详细分析该函数。如果不满足⑵处条件,需要使用L2映射。首先执行⑷处获取虚拟地址对应的L1页表项,接着执行⑸处判断是否映射,如果没有对应的映射,则执行⑹处的函数OsMapL1PTE生成L1 page table类型页表项并保存,然后执行函数OsMapL2PageContinous生成L2 页表项目并保存。如果已经映射为L1 page table页表项类型,则重新映射。如果不是支持的页表项类型,则执行LOS_Panic()触发异常。⑺处统计生成映射的调试,最终会返回映射成功的数量。

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, &count);
        } else {
            /* have to use a L2 mapping, we only allocate 4KB for L1, support 0 ~ 1GB */
⑷          l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);if (OsIsPte1Invalid(l1Entry)) {OsMapL1PTE(archMmu, &l1Entry, vaddr, flags);
                saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);
            } else if (OsIsPte1PageTable(l1Entry)) {
                saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);
            } else {
                LOS_Panic("%s %d, unimplemented tt_entry %x/n", __FUNCTION__, __LINE__, l1Entry);
            }
        }
⑺      mapped += saveCounts;
    }

    return mapped;
}

3.2 函数OsMapSection

函数OsMapSection生成L1 section类型页表项并保存。⑴处转换为MMU标签。 ⑵处内联函数OsGetPte1Ptr(archMmu->virtTtb, *vaddr)用于获取虚拟地址对应的页表项索引地址,等于页表项基地址加上虚拟地址的高20位;OsTruncPte1(*paddr) | mmuFlags | MMU_DESCRIPTOR_L1_TYPE_SECTION)为虚拟地址的高12位+MMU标签+页表项Section类型值。该行语句的作用是把虚拟地址和物理地理映射,映射关系维护在页表项。⑶处把虚拟地址和物理地址增加1MiB的大小,映射数量减去256。

STATIC UINT32 OsMapSection(const LosArchMmu *archMmu, UINT32 flags, VADDR_T *vaddr,
                           PADDR_T *paddr, UINT32 *count)
{
    UINT32 mmuFlags = 0;

⑴  mmuFlags |= OsCvtSecFlagsToAttrs(flags);OsSavePte1(OsGetPte1Ptr(archMmu->virtTtb, *vaddr),
        OsTruncPte1(*paddr) | mmuFlags | MMU_DESCRIPTOR_L1_TYPE_SECTION);*count -= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
    *vaddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;
    *paddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;

    return MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
}

3.3 函数OsGetL2Table

函数OsGetL2Table用于生成L2页表,函数参数中archMmu是MMU,l1Index是L1页表项,ppa属于输出参数,保存L2页表项基地址。⑴处计算L2页表项偏移值(为啥这么计算 看不懂 TODO)。⑵处查询遍历是否存在L2页表,⑶处获取页表项基地址,然后判断是否页表类型,如果是则返回L2页表项基地址。

如果没有存在的页表,则为L2页表申请内存,如果支持虚拟地址,执行⑷使用LOS_PhysPageAlloc申请内存页;如果不支持虚拟地址,执行⑸使用LOS_MemAlloc申请内存。⑹处转换为物理地址,然后返回L2页表项基地址。

STATIC STATUS_T OsGetL2Table(LosArchMmu *archMmu, UINT32 l1Index, paddr_t *ppa)
{
    UINT32 index;
    PTE_T ttEntry;
    VADDR_T *kvaddr = NULL;
⑴  UINT32 l2Offset = (MMU_DESCRIPTOR_L2_SMALL_SIZE / MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) *
        (l1Index & (MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE - 1));
    /* lookup an existing l2 page table */for (index = 0; index < MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE; index++) {
⑶      ttEntry = archMmu->virtTtb[ROUNDDOWN(l1Index, MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) + index];
        if ((ttEntry & MMU_DESCRIPTOR_L1_TYPE_MASK) == MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE) {
            *ppa = (PADDR_T)ROUNDDOWN(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry), MMU_DESCRIPTOR_L2_SMALL_SIZE) +
                l2Offset;
            return LOS_OK;
        }
    }

#ifdef LOSCFG_KERNEL_VM
    /* not found: allocate one (paddr) */
⑷  LosVmPage *vmPage = LOS_PhysPageAlloc();
    if (vmPage == NULL) {
        VM_ERR("have no memory to save l2 page");
        return LOS_ERRNO_VM_NO_MEMORY;
    }
    LOS_ListAdd(&archMmu->ptList, &vmPage->node);
    kvaddr = OsVmPageToVaddr(vmPage);
#else
⑸  kvaddr = LOS_MemAlloc(OS_SYS_MEM_ADDR, MMU_DESCRIPTOR_L2_SMALL_SIZE);
    if (kvaddr == NULL) {
        VM_ERR("have no memory to save l2 page");
        return LOS_ERRNO_VM_NO_MEMORY;
    }
#endif
    (VOID)memset_s(kvaddr, MMU_DESCRIPTOR_L2_SMALL_SIZE, 0, MMU_DESCRIPTOR_L2_SMALL_SIZE);

    /* get physical address */*ppa = LOS_PaddrQuery(kvaddr) + l2Offset;
    return LOS_OK;
}

3.4 函数OsMapL1PTE

函数OsMapL1PTE用于生成L1 page table类型页表项并保存,其中函数参数pte1Ptr是L1页表项基地址。⑴处获取L2页表项基地址, ⑵处把L2页表项基地址加上描述符类型赋值给L1页表项基地址。⑶设置标签,⑷处保存页表项基地址。

STATIC VOID OsMapL1PTE(LosArchMmu *archMmu, PTE_T *pte1Ptr, vaddr_t vaddr, UINT32 flags)
{
    paddr_t pte2Base = 0;if (OsGetL2Table(archMmu, OsGetPte1Index(vaddr), &pte2Base) != LOS_OK) {
        LOS_Panic("%s %d, failed to allocate pagetable\n", __FUNCTION__, __LINE__);
    }*pte1Ptr = pte2Base | MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE;if (flags & VM_MAP_REGION_FLAG_NS) {
        *pte1Ptr |= MMU_DESCRIPTOR_L1_PAGETABLE_NON_SECURE;
    }
    *pte1Ptr &= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_MASK;
    *pte1Ptr |= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_CLIENT; // use client APOsSavePte1(OsGetPte1Ptr(archMmu->virtTtb, vaddr), *pte1Ptr);
}

4、虚实映射查询函数LOS_ArchMmuQuery

4.1 函数LOS_ArchMmuQuery

函数LOS_ArchMmuQuery用于获取进程空间虚拟地址对应的物理地址以及映射属性,其中输入参数为虚拟地址vaddr,输出参数为物理地址*paddr和标签*flags。⑴处获取虚拟地址对应的页表项。⑵处如果虚拟地址对应的页表项描述符类型无效,返回错误码。⑶处如果页表项描述符类型为Section,则执行⑷获取映射的物理地址,其中MMU_DESCRIPTOR_L1_SECTION_ADDR(l1Entry)为页表项的高12位,(vaddr & (MMU_DESCRIPTOR_L1_SMALL_SIZE - 1))为虚拟地址的低20位,即页内偏移值。⑸处获取映射的标签值。

虚拟地址对应的页表项描述符类型为页表Page Table,则执行⑹调用内联函数OsGetPte2BasePtr()计算L2页表项基地址,计算方法为:取页表项的高22位,低10位置0,转化为虚拟地址。⑺处计算虚拟地址对应的L2页表项数值。如果L2页表项描述符类型为小页,则执行⑻计算物理地址,然后计算相应的标签值。⑼处表示当前轻内核还不支持大页类型。

STATUS_T LOS_ArchMmuQuery(const LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T *paddr, UINT32 *flags)
{
⑴  PTE_T l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);
    PTE_T l2Entry;
    PTE_T* l2Base = NULL;if (OsIsPte1Invalid(l1Entry)) {
        return LOS_ERRNO_VM_NOT_FOUND;} else if (OsIsPte1Section(l1Entry)) {
        if (paddr != NULL) {*paddr = MMU_DESCRIPTOR_L1_SECTION_ADDR(l1Entry) + (vaddr & (MMU_DESCRIPTOR_L1_SMALL_SIZE - 1));
        }

        if (flags != NULL) {OsCvtSecAttsToFlags(l1Entry, flags);
        }
    } else if (OsIsPte1PageTable(l1Entry)) {
⑹      l2Base = OsGetPte2BasePtr(l1Entry);
        if (l2Base == NULL) {
            return LOS_ERRNO_VM_NOT_FOUND;
        }
⑺      l2Entry = OsGetPte2(l2Base, vaddr);
        if (OsIsPte2SmallPage(l2Entry) || OsIsPte2SmallPageXN(l2Entry)) {
            if (paddr != NULL) {*paddr = MMU_DESCRIPTOR_L2_SMALL_PAGE_ADDR(l2Entry) + (vaddr & (MMU_DESCRIPTOR_L2_SMALL_SIZE - 1));
            }

            if (flags != NULL) {
                OsCvtPte2AttsToFlags(l1Entry, l2Entry, flags);
            }} else if (OsIsPte2LargePage(l2Entry)) {
            LOS_Panic("%s %d, large page unimplemented\n", __FUNCTION__, __LINE__);
        } else {
            return LOS_ERRNO_VM_NOT_FOUND;
        }
    }

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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