SylixOS armv8 mmu
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相关推荐
- ARMv8 MMU及Linux页表映射:TLB
<ARM SMMU原理与IOMMU技术("VT-d" DMA.I/O虚拟化.内存虚拟化)> <Linux内存管理:分页机制> <Linux内存管理:内 ...
- ARMv8 MMU及Linux页表映射 LoyenWang
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- armv8 mmu The Access flag and The dirty state
D5.4.7 The Access flag page或者section的内存第一次被访问时,会设置AF标志位,如果是armv8.0需要软件管理,如果是armv8.1则是硬件自动管理. The Acc ...
- armv8 mmu 内存页表属性
armv8中的页表项 D5.3 VMSAv8-64 translation table format descriptors In general, a descriptor is one of: 页 ...
- [mmu/cache]-ARMV8 MMU内存管理中的Memory attributes和Cache policies
快速链接: .
- 8名清华北大研究生不出国,街道办事处上班!难道公务员比互联网还卷?
点击"开发者技术前线",选择"星标????" 在看|星标|留言, 真爱 来自:中产之路 编辑:可可 什么行业工资高发展好,看看当年大学毕业生的去处就知道了. ...
- 南方医科大学近日拟对11名博士研究生、5名硕士研究生作退学处理!
点击"开发者技术前线",选择"星标????" 在看|星标|留言, 真爱 来源:中国青年报(ID:zqbcyol)综合南方科技大学研究生院.各高校官网.青年湖南 ...
- u-boot代码之启动
u-boot版本,u-boot-2016.11 硬件环境, HI3559av100 u-boot系统启动流程分为stage1和stage2两部分,stage1是依赖于CPU体系结构的代码.通常sta ...
- ARMv8 的MMU
文章目录 MMU总览 相关的限制 相关的控制 MMU限制 size 第一个size相关 : TCR.TGx 第二个size相关 : TCR.TxSZ 第三个size 相关 : TCR.IPS 内存属性 ...
- [mmu/cache]-ARMV8的cache的维护指令介绍
★★★ 个人博客导读首页-点击此处 ★★★ Armv8里定义的Cache的管理的操作有三种: 无效(Invalidate) 整个高速缓存或者某个高速缓存行.高速缓存上的数据会被丢弃. 清除(Clean ...
最新文章
- asp.net 发布程序到iis后无法连接到oralce数据库问题
- FAT16文件系统结构扇区数据分析
- Chapter 5 带颜色的同心圆
- vscode前端常用插件整理(vuejs)
- U-Mail邮件系统客户无需担心OpenSSL心脏出血漏洞
- 基于Session共享的单点登录或通行证系统方案
- win7 git 添加 ssh key
- Windows多线程多任务设计初步zz
- typename与class
- 凭什么相信你,我的CNN模型
- MyCat 主键ID自增长配置
- JAVA50道基础编程题
- Echarts2的使用——绘制中国地图
- 在线长图片自动裁剪工具
- ra寄存器定位core
- 全方位保护您在 Amazon S3 的数据资产-访问控制详解
- 为什么ES不适合做数据存储
- 特斯联门禁支持nfc_特斯联lite钥匙怎么分享
- 16个优秀的域名查询工具
- SV学习笔记—覆盖组及覆盖率数据采样方法
热门文章
- python3怎么安装opencv_如何在Ubuntu 16.04上安装支持Python 3.x的OpenCV
- 杭州云栖大会10月起航,这里有一份最全的大会剧透
- 更新CocoaPods碰到的问题及知识点
- 理解函数:对象(this,arguments),方法(apply(),call(),bind())。
- Linux 加入域的那些事儿!
- SqlServer修改密码后登陆不上
- LoggerFactory.getLogger
- Spanning Tree Protocol (STP) in NetScaler Appliance
- Redis(一)入门
- TFS源代码管理的8大注意事项