armv8 mmu 支持4K,16K,64K分页,在SylixOS目前实现了4K和6K分页。根据查看代码SylixOS目前使用的是Non-secure EL1、stage 1 translation、VA和PA的地址宽度都是48个bit。所以分析代码不包含arm提供的其他功能。

代码位于SylixOS/arch/arm64/mm/mmu 文件夹。

在SylixOS封装了mmu操作函数集,这样虽然不同体系结构只需要实现对应的函数功能,不影响内核。

arm64MmuMemInit函数是初始化,为pgd,pmd,pts,pte分配空间。在4K模式下使用四级分页。

/*********************************************************************************************************
** 函数名称: arm64MmuMemInit
** 功能描述: 初始化 MMU 页表内存区
** 输 入  : pmmuctx        mmu 上下文
** 输 出  : ERROR or OK
** 全局变量:
** 调用模块:
** 注  意  : ARM 体系结构要求: 一级页表基地址需要保持 16 KByte 对齐, 单条目映射 1 MByte 空间.二级页表基地址需要保持  1 KByte 对齐, 单条目映射 4 KByte 空间.
*********************************************************************************************************/
static INT  arm64MmuMemInit (PLW_MMU_CONTEXT  pmmuctx)
{
#define PGD_BLOCK_SIZE  (4  * LW_CFG_KB_SIZE)
#define PMD_BLOCK_SIZE  (4  * LW_CFG_KB_SIZE)
#define PTS_BLOCK_SIZE  (4  * LW_CFG_KB_SIZE)
#define PTE_BLOCK_SIZE  (4  * LW_CFG_KB_SIZE)PVOID  pvPgdTable;PVOID  pvPmdTable;PVOID  pvPtsTable;PVOID  pvPteTable;ULONG  ulPgdNum = bspMmuPgdMaxNum();ULONG  ulPmdNum = bspMmuPmdMaxNum();ULONG  ulPtsNum = bspMmuPtsMaxNum();ULONG  ulPteNum = bspMmuPteMaxNum();pvPgdTable = __KHEAP_ALLOC_ALIGN((size_t)ulPgdNum * PGD_BLOCK_SIZE, PGD_BLOCK_SIZE);pvPmdTable = __KHEAP_ALLOC_ALIGN((size_t)ulPmdNum * PMD_BLOCK_SIZE, PMD_BLOCK_SIZE);pvPtsTable = __KHEAP_ALLOC_ALIGN((size_t)ulPtsNum * PTS_BLOCK_SIZE, PTS_BLOCK_SIZE);pvPteTable = __KHEAP_ALLOC_ALIGN((size_t)ulPteNum * PTE_BLOCK_SIZE, PTE_BLOCK_SIZE);if (!pvPgdTable || !pvPmdTable || !pvPtsTable || !pvPteTable) {_DebugHandle(__ERRORMESSAGE_LEVEL, "can not allocate page table.\r\n");return  (PX_ERROR);}_G_hPGDPartition = API_PartitionCreate("pgd_pool", pvPgdTable, ulPgdNum, PGD_BLOCK_SIZE,LW_OPTION_OBJECT_GLOBAL, LW_NULL);_G_hPMDPartition = API_PartitionCreate("pmd_pool", pvPmdTable, ulPmdNum, PMD_BLOCK_SIZE,LW_OPTION_OBJECT_GLOBAL, LW_NULL);_G_hPTSPartition = API_PartitionCreate("pts_pool", pvPtsTable, ulPtsNum, PTS_BLOCK_SIZE,LW_OPTION_OBJECT_GLOBAL, LW_NULL);_G_hPTEPartition = API_PartitionCreate("pte_pool", pvPteTable, ulPteNum, PTE_BLOCK_SIZE,LW_OPTION_OBJECT_GLOBAL, LW_NULL);if (!_G_hPGDPartition || !_G_hPMDPartition || !_G_hPTSPartition || !_G_hPTEPartition) {_DebugHandle(__ERRORMESSAGE_LEVEL, "can not allocate page pool.\r\n");return  (PX_ERROR);}return  (ERROR_NONE);
}

可看到首先重bsp函数中获得需要分配的pgd,pmd,pte,pts数量。根据不同的硬件可以指定当前使用的数量,但是都是以4K空间为单位。在armv8A手册中规定了采用4级后各使用地址位数。

上图是手册中VA48位时4K分页传输时的结构。在armv8 可以指定地址线位数,SylixOS目前使用的是虚拟地址48位。

上图来自蜗窝科技 的ARM64的启动过程之(二),很好的解释了虚拟地址的翻译过程。

在4k分页对应关系是下表

地址翻译过程如下图:

在arm64MmuMemInit函数中创建了pgd,pmd,pts,pte的池,这个池就是为四级提前在堆栈中分配的空间。每使用一个entry就是从池中分配出来。在arm64中每个entry是8个字节。

arm64MmuGlobalInit

主要是做了与硬件相关的初始化。

/*********************************************************************************************************
** 函数名称: arm64MmuGlobalInit
** 功能描述: 调用 BSP 对 MMU 初始化
** 输 入  : pcMachineName     使用的机器名称
** 输 出  : ERROR or OK
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT  arm64MmuGlobalInit (CPCHAR  pcMachineName)
{archCacheReset(pcMachineName);arm64MmuInvalidateTLB();arm64MmuSetTCR(0x580823510);                                        /*  T0SZ = 2 ^ 48               */arm64MmuSetMAIR();return  (ERROR_NONE);
}

archCacheRest函数是无效cache清除cache。实现调用汇编参考SylixOS armv8启动分析

arm64MmuInvalidateTlB函数是无效mmu的TLB表。在mmu中存在一个类似于cache的快表。


FUNC_DEF(arm64MmuInvalidateTLB)                                         ;/* 快表中的所有项无效           */TLBI    VMALLE1IS                                                   ;/* TLBIAll, EL1, IS            */ARM_DSB()ARM_ISB()RETFUNC_END()

TLBI    VMALLE1IS 指令在armv8手册中定义如图。ARM_DSB(),ARM_ISB()是arm dsb,isb指令,是指令屏障,保证前面指令执行完,后面执行。

arm64MmuSetTCR函数是直接将值赋值给TCR_EL1寄存器。TCR_ELx 是mmu控制相关的寄存器。

FUNC_DEF(arm64MmuSetTCR)MSR     TCR_EL1 , X0RETFUNC_END()

TCR控制寄存器包含很多系统相关的内容:

T0SZ, bits [5:0]定义了虚拟地址的宽度。

EPD0 控制当虚拟地址使用TLB块表查找错误时,是否使用TTBR0_EL1 进行查找。Translation table walk 指的是从虚拟地址查找到物理地址的过程。TTBR0_EL1 存放的的pgd的基地址。当从TLB查找失败时需要使用pgd->pmd->pts->pts查找过程。

IRGN1, bits [25:24]和IRGN0, bits [9:8]用来控制页表所在memory的inner cachebility attribute的。

ORGN1, bits [27:26]和ORGN0, bits [11:10]用来控制页表所在memory的outercachebility attribute的。

SH1, bits [29:28]和SH0, bits [13:12]是用来控制页表所在memory的Shareability attribute。

TG1,bits [31:30]和TG0,bits [15:14]是用来控制page size的,可以是4K,16K或者64K。

TBI1,bit[38]和TBI0,bit[37]用来控制是否忽略地址的高8位(TBI就是Top Byte ignored的意思),如果允许忽略地址的高8位,那么MMU的硬件在进行地址比对,匹配的时候忽略高八位,这样软件可以自由的使用这个byte,例如对于一个指向动态分配内存的对象指针,可以通过高8位来表示reference counter,从而可以跟踪其使用情况,reference count等于0的时候,可以释放内存。AS bit[36]用来定义ASID(address space ID)的size,A1, bit [22]用来控制是kernel space还是user space使用ASID。ASID是和TLB操作相关,一般而言,地址翻译的时候并不是直接查找页表,而是先看TLB是否命中,具体判断的标准是虚拟地址+ASID,ASID是每一个进程分配一个,标识自己的进程地址空间。这样在切换进程的时候不需要flush TLB,从而有助于performance。 这里参考了ARM64的启动过程之(三):为打开MMU而进行的CPU初始化。

在SylixOS中在TCR寄存器设置的值为0x580823510 所以bit[38]和bit[37]都为0,不忽略高8位。控制选择4K,虚拟地址长度是48位。IPS设置为48bit,IPS(Intermediate Physical Address Size)查资料这里和虚拟化有关,具体还没搞懂。

最后是arm64MmuSetMAIR函数,MAIR_EL1是内存属性的寄存器

;/*********************************************************************************************************
;  设置域属性
;*********************************************************************************************************/#define MAIR(attr, mt)      ((attr) << ((mt) * 8))
#define MT_DEVICE_nGnRnE    0
#define MT_DEVICE_nGnRE     1
#define MT_DEVICE_nGRE      2
#define MT_DEVICE_GRE       3
#define MT_NORMAL_NC        4
#define MT_NORMAL           5
#define MT_NORMAL_WT        6FUNC_DEF(arm64MmuSetMAIR)LDR     X0 , =MAIR(0x00, MT_DEVICE_nGnRnE) | \MAIR(0x04, MT_DEVICE_nGnRE)  | \MAIR(0x08, MT_DEVICE_nGRE)   | \MAIR(0x0c, MT_DEVICE_GRE)    | \MAIR(0x44, MT_NORMAL_NC)     | \MAIR(0xff, MT_NORMAL)        | \MAIR(0xbb, MT_NORMAL_WT)MSR     MAIR_EL1 , X0RETFUNC_END()

下面在armv8A手册中对MAIR_EL寄存的介绍

根据arm官方介绍内存分为两种类型,分别是device和normal,也就是设备使用的内存和正常的内存。在arm中将寄存器操作映射到内存当中这部分内存,由于对寄存器读些映射关系呗称为设备内存。

“我们知道,ARMv8采用了weakly-order内存模型,也就是说,通俗的讲就是处理器实际对内存访问(load and store)的执行序列和program order不一定保持严格的一致,处理器可以对内存访问进行reorder。例如:对于写操作,processor可能会合并两个写的请求。处理器这么任性当然是从性能考虑,不过这大大加大了软件的复杂度(软件工程师需要理解各种memory barrier操作,例如ISB/DSB/DMB,以便控制自己程序的内存访问的order)。

地址空间那么大,是否都任由processor胡作非为呢?当然不是,例如对于外设的IO地址,处理必须要保持其order。因此memory被分成两个基本的类型:normal memory和devicememory。除了基本的memory type,还有memory attribute(例如:cacheability,shareability)来进一步进行描述,我们在下一节描述。

标识为normal memory type的memory就是我们常说的内存而已,对其访问没有副作用(side effect),也就是说第n次和第n+1次访问没有什么差别。device memory就不会这样,对一些状态寄存器有可能会read clear,因此n和n+1的内存访问结果是不一样的。正因为如此,processor可以对这些内存操作进行reorder、repeat或者merge。我们可以把程序代码和数据所在的memory设定为normal memory type,这样可以获取更高的性能。例如,在代码执行过程中,processor可能进行分支预测,从而提前加载某些代码进入pipeline(而实际上,program不一定会fetch那些指令),如果设定了不正确的memory type,那么会阻止processor进行reorder的动作,从而阻止了分支预测,进而影响性能。

对于那些外设使用的IO memory,对其的访问是有side effect的,很简单的例子就是设备的FIFO,其地址是固定不变的,但是每次访问,内部的移位寄存器就会将下一个数据移出来,因此每次访问同一个地址实际上返回的数据是不一样的。device不存在cache的设定,总是no cache的,处理器访问device memory的时候,限制会比普通memory多,例如不能进行Speculative data accesses(所谓不能进行Speculative data accesses就是说cpu对memory的访问必须由顺序执行的执行产生,不能由于自己想加快性能而投机的,提前进行某些数据访问)。” ---引用自蜗窝科技

在device和normal内存中又进行了细分,在MAIR寄存器被分为了8组,每组8位,分别代表了不同属性的内存。看手册中MAIR寄存器的定义

可以看到每一组中当4-7bit是0时为设备内存。

以下来自蜗窝科技:“

对于device type,其总是non cacheable的,而且是outer shareable,因此它的attribute不多,主要有下面几种附加的特性:

(1)Gathering 或者non Gathering (G or nG)。这个特性表示对多个memory的访问是否可以合并,如果是nG,表示处理器必须严格按照代码中内存访问来进行,不能把两次访问合并成一次。例如:代码中有2次对同样的一个地址的读访问,那么处理器必须严格进行两次read transaction。

(2)Re-ordering (R or nR)。这个特性用来表示是否允许处理器对内存访问指令进行重排。nR表示必须严格执行program order。

(3)Early Write Acknowledgement (E or nE)。PE访问memory是有问有答的(更专业的术语叫做transaction),对于write而言,PE需要write ack操作以便确定完成一个write transaction。为了加快写的速度,系统的中间环节可能会设定一些write buffer。nE表示写操作的ack必须来自最终的目的地而不是中间的write buffer。

对于normal memory,可以是non-cacheable的,也可以是cacheable的,这样就需要进一步了解Cacheable和shareable atrribute,具体如下:

(1)是否cacheable

(2)write through or write back

(3)Read allocate or write allocate

(4)transient or non-transient cache

最后一点要说明的是由于cache hierararchy的存在,memory的属性可以针对inner和outer cache分别设定,具体如何区分inner和outer cache是和具体实现相关,但通俗的讲,build in在processor内的cache是inner的,而outer cache是processor通过bus访问的。NC是no cache,也就是说MT_NORMAL_NC的memory是normal memory,但是对于这种类型的memory的访问不需要通过cache系统”

write through or write back解释如下(来自链接):

Write Back:Cache Line中的数据被CPU核修改时并不立刻写回内存,Cache Line和内存中的数据会暂时不一致,在Cache Line中有一个Dirty位标记这一情况。当一条Cache Line要被其它VA的数据替换时,如果不是Dirty的就直接替换掉,如果是Dirty的就先写回内存再替换。

Write Through:每当CPU核修改Cache Line中的数据时就立刻写回内存,Cache Line和内存中的数据总是一致的。如果有多个CPU或设备同时访问内存,例如采用双口RAM,那么Cache中的数据和内存保持一致就非常重要了,这时相关的内存页面通常配置为Write Through模式

Read allocate or write allocate 解释(来自链接):

在有cache的单机系统中,通常有两种写策略:write through和write back。这两种写策略都是针对写命中(write hit)情况而言的:write through是既写cache也写main memory;write back是只写cache,并使用dirty标志位记录cache的修改,直到被修改的cache 块被替换时,才把修改的内容写回main memory。那么在写失效(write miss)时,即所要写的地址不在cache中,该怎么办呢?一种办法就是把要写的内容直接写回main memory,这种办法叫做no write allocate policy;另一种办法就是把要写的地址所在的块先从main memory调入cache中,然后写cache,这种办法叫做write allocate policy

可以看到SylixOS给MAIR_EL1中七个条目设置属性。

第一个是MT_DEVICE_nGnRnE 代表是设备内存,必须保证严格按照代码中的访问顺序来,不允许合并对内存的访问,不允许对指令重排,写操作的ack必须来自最终的目的地而不是中间的write buffer。

第二个是MT_DEVICE_nGnRE 必须保证严格按照代码中的访问顺序来,不允许合并对内存的访问,不允许对指令重排,但是写操作的ack不必须来自最终的目的地而不是中间的write buffer。

第三个 MT_DEVICE_nGRE是不能对内存访问合并,但是指令可以重排,ack不必须来自最终的目的地。

第四个MT_DEVICE_GRE 是对内存访问可以合并,指令可以重排,ack不必须来自最终的目的地。

第五个 MT_NORMAL_NC是不带cache的内存。

第六个MT_NORMAL 是正常的内存。

第七个MT_NORMAL_WT 是带 write Through的。

MAIR寄存器含有8组,最后使用时在描述符属性里的AttrIndx[2:0]里指定使用MAIR寄存器中那一组属性。

上图是armv8手册中定义属性位置, 但是SylixOS有一层自己封装的属性,在最后pte和物理地址绑定时将SylixOS属性转化为上图所示的属性。

arm64MmuPgdAlloc

此函数是分配一个pgd 基地址。也就是从pgd池中分配出一个表的首地址。然后将表清空。pgd比较特殊因为它是第一个,所以没有去把地址填入过程。并且直接返回相应的entry。

/*********************************************************************************************************
** 函数名称: arm64MmuPgdAlloc
** 功能描述: 分配 PGD 项
** 输 入  : pmmuctx        mmu 上下文
**           ulAddr         虚拟地址 (参数 0 即偏移量为 0 , 需要返回页表基地址)
** 输 出  : 分配 PGD 地址
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static LW_PGD_TRANSENTRY *arm64MmuPgdAlloc (PLW_MMU_CONTEXT  pmmuctx, addr_t  ulAddr)
{REGISTER LW_PGD_TRANSENTRY  *p_pgdentry;REGISTER ULONG               ulPgdNum;p_pgdentry = (LW_PGD_TRANSENTRY *)API_PartitionGet(_G_hPGDPartition);if (!p_pgdentry) {return  (LW_NULL);}lib_bzero(p_pgdentry, PGD_BLOCK_SIZE);                              /*  新的 PGD 无有效的页表项     */ulAddr    &= LW_CFG_VMM_PGD_MASK;ulPgdNum   = ulAddr >> LW_CFG_VMM_PGD_SHIFT;p_pgdentry = (LW_PGD_TRANSENTRY *)((addr_t)p_pgdentry |(ulPgdNum * sizeof(LW_PGD_TRANSENTRY)));               /*  获得一级页表描述符地址      */return  (p_pgdentry);
}

首先获取pgd池的基地址,然后保留ulAddr 地址的39到47位。通过右移39位,求出当前地址在pgd页表中的的位置。求出一个entry的地址。

arm64MmuPmdAlloc

主要是分配一个pmd表的首地址,将这个首地址填入到pgd 条目中。然后计算当前地址在pmd表中的entry,返回当前的pmd条目

/*********************************************************************************************************
** 函数名称: arm64MmuPmdAlloc
** 功能描述: 分配 PMD 项
** 输 入  : pmmuctx        mmu 上下文
**           p_pgdentry     pgd 入口地址
**           ulAddr         虚拟地址
** 输 出  : 分配 PMD 地址
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static LW_PMD_TRANSENTRY *arm64MmuPmdAlloc (PLW_MMU_CONTEXT     pmmuctx, LW_PGD_TRANSENTRY  *p_pgdentry,addr_t              ulAddr)
{
#if LW_CFG_CACHE_EN > 0INTREG  iregInterLevel;
#endif                                                                  /*  LW_CFG_CACHE_EN > 0         */LW_PMD_TRANSENTRY  *p_pmdentry = (LW_PMD_TRANSENTRY *)API_PartitionGet(_G_hPMDPartition);if (!p_pmdentry) {return  (LW_NULL);}lib_bzero(p_pmdentry, PMD_BLOCK_SIZE);*p_pgdentry = arm64MmuBuildPgdEntry((addr_t)p_pmdentry,             /*  设置一级页表描述符          */ARM64_MMU_NS_SECURE,ARM64_MMU_AP_NO_EFFECT,ARM64_MMU_XN_NO_EFFECT,ARM64_MMU_PXN_NO_EFFECT,ARM64_PGD_TYPE_TABLE);#if LW_CFG_CACHE_EN > 0iregInterLevel = KN_INT_DISABLE();arm64DCacheFlush((PVOID)p_pgdentry, (PVOID)p_pgdentry, 32);         /*  第三个参数无影响            */KN_INT_ENABLE(iregInterLevel);
#endif                                                                  /*  LW_CFG_CACHE_EN > 0         */return  (arm64MmuPmdOffset(p_pgdentry, ulAddr));
}

pts,pte分配和pmd分配相同。arm64MmuBuildPgdEntry函数在这里保留了地址的12-47位,其他位加入了系统需要的标志信息。

arm64MmuPgdIsOk  判断是否pgd当前 entry有效

/*********************************************************************************************************
** 函数名称: arm64MmuPgdIsOk
** 功能描述: 判断 PGD 项的描述符是否正确
** 输 入  : pgdentry       PGD 项描述符
** 输 出  : 是否正确
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static BOOL  arm64MmuPgdIsOk (LW_PGD_TRANSENTRY  pgdentry)
{return  (((pgdentry & ARM64_PGD_TYPE_MASK) == ARM64_PGD_TYPE_TABLE) ? LW_TRUE : LW_FALSE);
}

判断方式很简单 就是检测ARM64_PMD_TYPE_MASK 这个掩码,这个掩码上面pmd分配函数里,在把pmd地址写入到pgd entry时加入一些标志位,检测到这个存在说明当前pgd被填写了pmd数据。pmdOk等函数与这原理相同。

/*********************************************************************************************************
** 函数名称: arm64MmuPgdOffset
** 功能描述: 通过虚拟地址计算 PGD 项
** 输 入  : pmmuctx        mmu 上下文
**           ulAddr         虚拟地址
** 输 出  : 对应的 PGD 表项地址
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static LW_PGD_TRANSENTRY *arm64MmuPgdOffset (PLW_MMU_CONTEXT  pmmuctx, addr_t  ulAddr)
{REGISTER LW_PGD_TRANSENTRY  *p_pgdentry = pmmuctx->MMUCTX_pgdEntry;REGISTER ULONG               ulPgdNum;ulAddr    &= LW_CFG_VMM_PGD_MASK;ulPgdNum   = ulAddr >> LW_CFG_VMM_PGD_SHIFT;                        /*  计算 PGD 号                 */p_pgdentry = (LW_PGD_TRANSENTRY *)((addr_t)p_pgdentry |(ulPgdNum * sizeof(LW_PGD_TRANSENTRY)));               /*  获得一级页表描述符地址      */return  (p_pgdentry);
}

全部的偏移函数都是首先获得当前自己的表的基地址,上面函数是首先获得 自己表的首地址,然后保留39-47数值,通过

(ulPgdNum * sizeof(LW_PGD_TRANSENTRY)) 这句话算出在表内的偏移值。返回对应的entry。

arm64MmuPtePhysGet  是将当前的pte存放地址丢掉低12bit,返回。

arm64MmuMakeTrans 

此函数的主要作用是将物理地址和虚拟地址表中的pte建立联系。也就是需要填充pte表。上面的alloc函数最终只把pts填充了pte的地址。所以在这里填充pte。

/*********************************************************************************************************
** 函数名称: arm64MmuMakeTrans
** 功能描述: 设置页面映射关系
** 输 入  : pmmuctx        mmu 上下文
**           p_pteentry     对应的页表项
**           ulVirtualAddr  虚拟地址
**           paPhysicalAddr 物理地址
**           ulFlag         对应的类型
** 输 出  : NONE
** 全局变量:
** 调用模块:
** 注  意  : 这里不需要清除快表 TLB, 因为 VMM 自身会作此操作.
*********************************************************************************************************/
static VOID  arm64MmuMakeTrans (PLW_MMU_CONTEXT     pmmuctx,LW_PTE_TRANSENTRY  *p_pteentry,addr_t              ulVirtualAddr,phys_addr_t         paPhysicalAddr,ULONG               ulFlag)
{UINT8               ucGuard;                                        /*  严格的权限检查              */UINT8               ucXN;                                           /*  存储权限                    */UINT8               ucPXN;                                          /*  域                          */UINT8               ucContiguous;                                   /*  CACHE 与缓冲区控制          */UINT8               ucnG;                                           /*  存储权限                    */UINT8               ucAF;                                           /*  CACHE 与缓冲区控制          */UINT8               ucSH;                                           /*  永不执行位                  */UINT8               ucAP;UINT8               ucNS;UINT8               ucAttrIndx;UINT8               ucType;if (ulFlag & LW_VMM_FLAG_ACCESS) {ucType = ARM64_PTE_TYPE_PAGE;} else {ucType = ARM64_PTE_TYPE_FAULT;                                  /*  访问将失效                  */}if (arm64MmuFlags2Attr(ulFlag,&ucGuard,&ucXN, &ucPXN,&ucContiguous,&ucnG, &ucAF,&ucSH, &ucAP,&ucNS, &ucAttrIndx) < 0) {                   /*  无效的映射关系              */return;}*p_pteentry = arm64MmuBuildPtentry((addr_t)paPhysicalAddr,ucGuard,ucXN, ucPXN,ucContiguous,ucnG, ucAF,ucSH, ucAP,ucNS, ucAttrIndx,ucType);#if LW_CFG_CACHE_EN > 0arm64DCacheFlush((PVOID)p_pteentry, (PVOID)p_pteentry, 32);         /*  第三个参数无影响            */
#endif                                                                  /*  LW_CFG_CACHE_EN > 0         */
}

首先将SylixoS标志位转换为物理地址的标志位。这里的ucAttrIndx 就是上面MAIR寄存器讲到有8个属性条目,其他可以在上面的图中看到。物理地址因为是4K页对齐所以也是保留 12-47位。最后根据虚拟地址中的offset计算出当前实际访问物理地址。

 arm64MmuMakeCurCtx

此函数用来设置TTBR0_EL1寄存器。TTBR0_EL1 寄存器器用来保存pgd表的基地址。

arm64MmuInvTLB 

函数使用来实现无线Tlb块表的,通过传入地址和无效的数量,当无效数量大于16时直接使用arm64MmuInvalidateTLB函数无效全部TLB。当无效数量小于16时调用arm64MmuInvalidateTLBMVA函数,将虚拟地址低12位抛弃,然后无效当前地址的。

FUNC_DEF(arm64MmuInvalidateTLBMVA)LSR     X0 , X0 , #12                                               ;/* 虚拟地址移出取低 12 位       */TLBI    VAE1IS , X0                                                 ;/* TLBIVA, All ASID, EL1, IS   */ARM_DSB()ARM_ISB()RETFUNC_END()

使能和关闭mmu都是通过设置SCTLR_EL1系统控制寄存器。

arm64MmuFlagGet 是通过虚拟地址四级查表获得pte存放的物理地址值,然后解析当前值对应的属性位。

arm64MmuFlagSet 函数是通过四级页表查找获得pte存放的物理地址,然后将SylixOS系统内存属性解析为硬件mmu的属性。然后将新设置的值写入到pte当中。

上面是mmu操作函数集,封装好了后还需要调用来建立mmu映射,在SylixOS中操作函数使用了宏定义,类似于下图

SylixOS armv8 mmu相关推荐

  1. ARMv8 MMU及Linux页表映射:TLB

    <ARM SMMU原理与IOMMU技术("VT-d" DMA.I/O虚拟化.内存虚拟化)> <Linux内存管理:分页机制> <Linux内存管理:内 ...

  2. ARMv8 MMU及Linux页表映射 LoyenWang

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  3. armv8 mmu The Access flag and The dirty state

    D5.4.7 The Access flag page或者section的内存第一次被访问时,会设置AF标志位,如果是armv8.0需要软件管理,如果是armv8.1则是硬件自动管理. The Acc ...

  4. armv8 mmu 内存页表属性

    armv8中的页表项 D5.3 VMSAv8-64 translation table format descriptors In general, a descriptor is one of: 页 ...

  5. [mmu/cache]-ARMV8 MMU内存管理中的Memory attributes和Cache policies

    快速链接: .

  6. 8名清华北大研究生不出国,街道办事处上班!难道公务员比互联网还卷?

    点击"开发者技术前线",选择"星标????" 在看|星标|留言,  真爱 来自:中产之路 编辑:可可 什么行业工资高发展好,看看当年大学毕业生的去处就知道了. ...

  7. 南方医科大学近日拟对11名博士研究生、5名硕士研究生作退学处理!

    点击"开发者技术前线",选择"星标????" 在看|星标|留言,  真爱 来源:中国青年报(ID:zqbcyol)综合南方科技大学研究生院.各高校官网.青年湖南 ...

  8. u-boot代码之启动

    u-boot版本,u-boot-2016.11 硬件环境,  HI3559av100 u-boot系统启动流程分为stage1和stage2两部分,stage1是依赖于CPU体系结构的代码.通常sta ...

  9. ARMv8 的MMU

    文章目录 MMU总览 相关的限制 相关的控制 MMU限制 size 第一个size相关 : TCR.TGx 第二个size相关 : TCR.TxSZ 第三个size 相关 : TCR.IPS 内存属性 ...

  10. [mmu/cache]-ARMV8的cache的维护指令介绍

    ★★★ 个人博客导读首页-点击此处 ★★★ Armv8里定义的Cache的管理的操作有三种: 无效(Invalidate) 整个高速缓存或者某个高速缓存行.高速缓存上的数据会被丢弃. 清除(Clean ...

最新文章

  1. asp.net 发布程序到iis后无法连接到oralce数据库问题
  2. FAT16文件系统结构扇区数据分析
  3. Chapter 5 带颜色的同心圆
  4. vscode前端常用插件整理(vuejs)
  5. U-Mail邮件系统客户无需担心OpenSSL心脏出血漏洞
  6. 基于Session共享的单点登录或通行证系统方案
  7. win7 git 添加 ssh key
  8. Windows多线程多任务设计初步zz
  9. typename与class
  10. 凭什么相信你,我的CNN模型
  11. MyCat 主键ID自增长配置
  12. JAVA50道基础编程题
  13. Echarts2的使用——绘制中国地图
  14. 在线长图片自动裁剪工具
  15. ra寄存器定位core
  16. 全方位保护您在 Amazon S3 的数据资产-访问控制详解
  17. 为什么ES不适合做数据存储
  18. 特斯联门禁支持nfc_特斯联lite钥匙怎么分享
  19. 16个优秀的域名查询工具
  20. SV学习笔记—覆盖组及覆盖率数据采样方法

热门文章

  1. python3怎么安装opencv_如何在Ubuntu 16.04上安装支持Python 3.x的OpenCV
  2. 杭州云栖大会10月起航,这里有一份最全的大会剧透
  3. 更新CocoaPods碰到的问题及知识点
  4. 理解函数:对象(this,arguments),方法(apply(),call(),bind())。
  5. Linux 加入域的那些事儿!
  6. SqlServer修改密码后登陆不上
  7. LoggerFactory.getLogger
  8. Spanning Tree Protocol (STP) in NetScaler Appliance
  9. Redis(一)入门
  10. TFS源代码管理的8大注意事项