此文件内函数基本和映射相关。会调用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刷新页表 。