SylixOS 内存管理源代码分析--pageTable.c

此文件内函数基本和映射相关。会调用mmu操作集函数,操作集函数参考https://blog.csdn.net/qq_28637193/article/details/88546354,因为不同体系结构mmu操作函数集不同,这里是以arm64为例

__vmmLibVirtualToPhysical

此函数来查找虚拟地址映射的物理地址。

/*********************************************************************************************************
** 函数名称: __vmmLibVirtualToPhysical
** 功能描述: 通过虚拟地址查询对应的物理地址
** 输 入  : ulVirtualAddr      虚拟地址
**           pulPhysicalAddr    返回的物理地址
** 输 出  : 虚拟地址, 错误返回 (void *)-1 地址.
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
ULONG  __vmmLibVirtualToPhysical (addr_t  ulVirtualAddr, addr_t  *pulPhysicalAddr)
{
    INT                      iError;
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN == 0
    PLW_MMU_CONTEXT          pmmuctx = __vmmGetCurCtx();
    LW_PGD_TRANSENTRY       *p_pgdentry;
    LW_PMD_TRANSENTRY       *p_pmdentry;
#if LW_CFG_VMM_PAGE_4L_EN > 0
    LW_PTS_TRANSENTRY       *p_ptsentry;
#endif                                                                  /*  LW_CFG_VMM_PAGE_4L_EN > 0   */
    LW_PTE_TRANSENTRY       *p_pteentry;
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN > 0
    iError = __VMM_MMU_PHYS_GET(ulVirtualAddr, pulPhysicalAddr);
    if (iError < 0) {
        return  (ERROR_VMM_LOW_LEVEL);
    }
    
    return  (ERROR_NONE);

#else
    p_pgdentry = __VMM_MMU_PGD_OFFSET(pmmuctx, ulVirtualAddr);
    if (__VMM_MMU_PGD_NONE((*p_pgdentry))) {                            /*  判断 PGD 条目正确性         */
        goto    __error_handle;
    }
    
    p_pmdentry = __VMM_MMU_PMD_OFFSET(p_pgdentry, ulVirtualAddr);
    if (__VMM_MMU_PMD_NONE((*p_pmdentry))) {                            /*  判断 PMD 条目正确性         */
        goto    __error_handle;
    }
    
#if LW_CFG_VMM_PAGE_4L_EN > 0
    p_ptsentry = __VMM_MMU_PTS_OFFSET(p_pmdentry, ulVirtualAddr);
    if (__VMM_MMU_PTS_NONE((*p_ptsentry))) {                            /*  判断 PTS 条目正确性         */
        goto    __error_handle;
    }
    p_pteentry = __VMM_MMU_PTE_OFFSET(p_ptsentry, ulVirtualAddr);
#else
    p_pteentry = __VMM_MMU_PTE_OFFSET(p_pmdentry, ulVirtualAddr);
#endif                                                                  /*  LW_CFG_VMM_PAGE_4L_EN > 0   */
    
    if (__VMM_MMU_PTE_NONE((*p_pteentry))) {                            /*  判断 PTE 条目正确性         */
        goto    __error_handle;
    }
    
    iError = __VMM_MMU_PHYS_GET((*p_pteentry), pulPhysicalAddr);        /*  查询页面基地址              */
    if (iError < 0) {
        return  (ERROR_VMM_LOW_LEVEL);
    
    } else {
        *pulPhysicalAddr = (ulVirtualAddr & (LW_CFG_VMM_PAGE_SIZE - 1))
                         + (*pulPhysicalAddr);                          /*  加入页内偏移量              */
        return  (ERROR_NONE);
    }
    
__error_handle:
    _ErrorHandle(ERROR_VMM_PAGE_INVAL);
    return  (ERROR_VMM_PAGE_INVAL);
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */
}

此函数首先获取mmu上下文,在SylixOS中mmu上下文是一个全局变量,SylixOS是共用全局空间不是像linux那种每个进程都是虚拟的单独空间。__VMM_MMU_PGD_OFFSET ,__VMM_MMU_PMD_OFFSET,__VMM_MMU_PTS_OFFSET,__VMM_MMU_PTE_OFFSET宏定义会调用mmu操作集函数中对应的函数,函数如下:

依次关系是pgd->pmd->pts->pte->物理地址

__VMM_MMU_PHYS_GET宏会使用 调用arm64MmuPtePhysGet函数获得虚拟地址对应的物理地址。由于获得地址是物理是4k对齐的,虚拟地址的最后12位是偏移值,所以将其相加算出最终的物理地址。

__vmmLibSetFlag

此函数主要功能是给物理地址设置属性,SylixOS有一套自己的属性标志,需要将其转换成体系结构对应物理页面属性。

/*********************************************************************************************************
** 函数名称: __vmmLibSetFlag
** 功能描述: 设置指定逻辑地址的访问权限
** 输 入  : ulVirtualAddr         需要映射的虚拟地址
**           ulPageNum             页面数量
**           ulFlag                页面标志
**           bFlushTlb             是否清除 TLB.
** 输 出  : ERROR CODE
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
ULONG  __vmmLibSetFlag (addr_t  ulVirtualAddr, ULONG   ulPageNum, ULONG  ulFlag, BOOL  bFlushTlb)
{
    INTREG              iregInterLevel;
    INT                 i;
    PLW_MMU_CONTEXT     pmmuctx = __vmmGetCurCtx();
    INT                 iError;
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN == 0
    addr_t              ulVirtualTlb = ulVirtualAddr;
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */

    for (i = 0; i < ulPageNum; i++) {                                   /*  重新映射这些页面            */
        iregInterLevel = KN_INT_DISABLE();                              /*  关闭中断                    */
        iError = __VMM_MMU_FLAG_SET(pmmuctx, ulVirtualAddr, ulFlag);
        KN_INT_ENABLE(iregInterLevel);                                  /*  打开中断                    */
        
        _BugHandle((iError < 0), LW_FALSE, "set page flag error,\r\n");
        ulVirtualAddr += LW_CFG_VMM_PAGE_SIZE;
    }
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN == 0
    if (bFlushTlb) {
        __vmmLibFlushTlb(pmmuctx, ulVirtualTlb, ulPageNum);             /*  同步刷新所有 CPU TLB        */
    }
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */
    
    return  (ERROR_NONE);
}

此函数首先使用__VMM_MMU_FLAG_SET这个宏调用arm64MmuFlagSet函数,这个函数是mmu操作集中的函数,它首先将SylixOS标志转换为armv8体系结构对应的物理页面属性,通过pgd->pmd->pts->pte 将虚拟地址映射的物理地址找到物理地址,然后根据属性重新生成地址填写到pts中。

__vmmLibGetFlag

此函数和上面函数相反,通过读取物理地址的属性转换为SylixOS对应的页面访问标志。

/*********************************************************************************************************
** 函数名称: __vmmLibGetFlag
** 功能描述: 获取指定逻辑地址的访问权限
** 输 入  : ulVirtualAddr         需要映射的虚拟地址
**           pulFlag               页面标志
** 输 出  : ERROR CODE
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
ULONG  __vmmLibGetFlag (addr_t  ulVirtualAddr, ULONG  *pulFlag)
{
    PLW_MMU_CONTEXT     pmmuctx = __vmmGetCurCtx();
    ULONG               ulFlag;

    ulFlag = __VMM_MMU_FLAG_GET(pmmuctx, ulVirtualAddr);
    if (pulFlag) {
        *pulFlag = ulFlag;
    }
    
    if (ulFlag & LW_VMM_FLAG_VALID) {
        return  (ERROR_NONE);
    
    } else {
        _ErrorHandle(ERROR_VMM_PAGE_INVAL);
        return  (ERROR_VMM_PAGE_INVAL);
    }
}

__VMM_MMU_FLAG_GET 调用的是arm64MmuFlagGet函数。

__vmmLibPageMap2

此函数是将虚拟和物理地址建立映射。

/*********************************************************************************************************
** 函数名称: __vmmLibPageMap2
** 功能描述: 将物理页面重新映射 (for ioremap2 )
** 输 入  : paPhysicalAddr        物理页面地址
**           ulVirtualAddr         需要映射的虚拟地址
**           ulPageNum             需要映射的页面个数
**           ulFlag                页面标志
** 输 出  : ERROR CODE
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
ULONG  __vmmLibPageMap2 (phys_addr_t paPhysicalAddr, addr_t ulVirtualAddr, ULONG ulPageNum, ULONG ulFlag)
{
    ULONG                    i;
    PLW_MMU_CONTEXT          pmmuctx = __vmmGetCurCtx();
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN == 0
    INTREG                   iregInterLevel;
    addr_t                   ulVirtualTlb = ulVirtualAddr;
    
    LW_PGD_TRANSENTRY       *p_pgdentry;
    LW_PMD_TRANSENTRY       *p_pmdentry;
#if LW_CFG_VMM_PAGE_4L_EN > 0
    LW_PTS_TRANSENTRY       *p_ptsentry;
#endif                                                                  /*  LW_CFG_VMM_PAGE_4L_EN > 0   */
    LW_PTE_TRANSENTRY       *p_pteentry;
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */
    
#if LW_CFG_VMM_L4_HYPERVISOR_EN > 0
    if (ulFlag & LW_VMM_FLAG_ACCESS) {
        for (i = 0; i < ulPageNum; i++) {
            if (__VMM_MMU_PAGE_MAP(pmmuctx, paPhysicalAddr, 
                                   ulVirtualAddr, ulFlag) < ERROR_NONE) {
                return  (ERROR_VMM_LOW_LEVEL);
            }
            paPhysicalAddr += LW_CFG_VMM_PAGE_SIZE;
            ulVirtualAddr  += LW_CFG_VMM_PAGE_SIZE;
        }
    
    } else {
        for (i = 0; i < ulPageNum; i++) {
            if (__VMM_MMU_PAGE_UNMAP(pmmuctx, ulVirtualAddr) < ERROR_NONE) {
                return  (ERROR_VMM_LOW_LEVEL);
            }
            ulVirtualAddr += LW_CFG_VMM_PAGE_SIZE;
        }
    }
    
#else
    for (i = 0; i < ulPageNum; i++) {
        p_pgdentry = __vmm_pgd_alloc(pmmuctx, ulVirtualAddr);
        if (p_pgdentry == LW_NULL) {
            return  (ERROR_VMM_LOW_LEVEL);
        }
        
        p_pmdentry = __vmm_pmd_alloc(pmmuctx, p_pgdentry, ulVirtualAddr);
        if (p_pmdentry == LW_NULL) {
            return  (ERROR_VMM_LOW_LEVEL);
        }
        
#if LW_CFG_VMM_PAGE_4L_EN > 0
        p_ptsentry = __vmm_pts_alloc(pmmuctx, p_pmdentry, ulVirtualAddr);
        if (p_ptsentry == LW_NULL) {
            return  (ERROR_VMM_LOW_LEVEL);
        }
        
        p_pteentry = __vmm_pte_alloc(pmmuctx, p_ptsentry, ulVirtualAddr);
#else
        p_pteentry = __vmm_pte_alloc(pmmuctx, p_pmdentry, ulVirtualAddr);
#endif                                                                  /*  LW_CFG_VMM_PAGE_4L_EN > 0   */

        if (p_pteentry == LW_NULL) {
            return  (ERROR_VMM_LOW_LEVEL);
        }
        
        iregInterLevel = KN_INT_DISABLE();                              /*  关闭中断                    */
        __VMM_MMU_MAKE_TRANS(pmmuctx, p_pteentry,
                             ulVirtualAddr,
                             paPhysicalAddr, ulFlag);                   /*  创建映射关系                */
        KN_INT_ENABLE(iregInterLevel);                                  /*  打开中断                    */
        
        paPhysicalAddr += LW_CFG_VMM_PAGE_SIZE;
        ulVirtualAddr  += LW_CFG_VMM_PAGE_SIZE;
    }
    
    __vmmLibFlushTlb(pmmuctx, ulVirtualTlb, ulPageNum);                 /*  同步刷新所有 CPU TLB        */
#endif                                                                  /* !LW_CFG_VMM_L4_HYPERVISOR_EN */
    
    return  (ERROR_NONE);
}

__vmm_pxx_alloc  xx代表gd,md等等这几个分配函数内容是相同的,都是根据虚拟地址查看上一级中是否存在地址,如果没有就分配一个,如果存在就算出在当前表中的偏移。调用不同体系结构分配和偏移函数。这也是mmu相关的操作要封装函数集的原因,内核可以不管底层不同的体系结构。算出pte后需要将物理地址填充到pte。 __VMM_MMU_MAKE_TRANS 宏会调用arm64中arm64MmuMakeTrans函数实现将物理地址和属性一起填写到pte中。在最后需要对块表tlb刷新。

刷新函数,通过定义好的宏调用mmu操作函数集中的关闭mmu功能,然后刷新TLB,然后打开mmu。最后通知其他cpu刷新页表 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值