原文地址:https://blog.csdn.net/suifengershi2000/article/details/122805722


内存属性

内存属性分为2类:

  • Normal型:SRAM或者DRAM那样的内存空间,一般都是过cache的(当然也可不过cache,如外设访问的地址空间,标记为NC)
  • device型:设备寄存器那样的IO空间,都不会过cache。Device属性的内存空间还有下面三种子属性,都有打开和关闭的定义。
    • G(gather:对多个memory的访问可以合并);nG与之相反
    • R(Reordering:对内存访问指令进行重排);nR与之相反
    • E(Early Write Acknowledgement hint:写操作的ack可提早应答);nE与之相反

MAIR配置

MAIR寄存器定义如下:
在这里插入图片描述
Linux预先定义了6种内存属性,分别存在MAIR寄存器的attr0~attr5。内存页表属性部分可以选择这个寄存器的某个index,范围(0~5)作为自己的属性。

  /*
   * 上面内容我们说到了, 页表entry表明内存是普通内存, 就是结合这里的初始化来指明的,
   * PMD_ATTRINDX(MT_NORMAL)是4, 这其实是一个index, 指向MAIR寄存器的[4*8+7:4*8],
   * MAIR寄存器一共有8组, KERNEL用了6组, 每组有8bit, 每个bit都有相应的含义.
   * 具体参考手册, 这里就不细说了, 点到为止
   */

   /*
    * Memory region attributes for LPAE:
    *
    *   n = AttrIndx[2:0]
    *                  n   MAIR
    *   DEVICE_nGnRnE  000 00000000
    *   DEVICE_nGnRE   001 00000100
    *   DEVICE_GRE     010 00001100
    *   NORMAL_NC      011 01000100
    *   NORMAL         100 11111111
    *   NORMAL_WT      101 10111011
    */
	ldr x5, = MAIR(0x00, MT_DEVICE_nGnRnE) | \	
			MAIR(0x04, MT_DEVICE_nGnRE) | \	
			MAIR(0x0c, MT_DEVICE_GRE) | \	
			MAIR(0x44, MT_NORMAL_NC) | \	
			MAIR(0xff, MT_NORMAL) | \	
			MAIR(0xbb, MT_NORMAL_WT)
	
	msr mair_el1, x5	# 写入预定义的5种内存属性值

在这里插入图片描述
在这里插入图片描述

Linux kernel中的页表属性

如上所述,Linux在cpu初始化时建立了6种页表属性索引

ARM64 cpu可以通过页表中设置的页表属性配置,决定其内存或寄存器访问行为(DEVICE_nGnRE/nGnRE/GRE、NORMAL_NC/WT/NORMAL)。

#define ioremap(addr, size)     __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 
#define ioremap_nocache(addr, size)  __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 
#define ioremap_wc(addr, size)  __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
 
#define ioremap_wt(addr, size)  __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 
 
#define PROT_DEVICE_nGnRnE  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRIDX(MT_DEVICE_nGnRnE))
 
#define PROT_DEVICE_nGnRE  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRIDX(MT_DEVICE_nGnRE))
 
#define PROT_NORMAL_NC  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRIDX(MT_NORMAL_NC))
 
#define PROT_NORMAL_WT  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRIDX(MT_NORMAL_WT))
 
#define PROT_NORMAL  (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRIDX(MT_NORMAL))
 
 
#define PTE_ATTRINDX(t)   (_AT(pteval_t, (t)) << 2)

PTE_ATTRINDX在页表中的位置 bit[4:2]
在这里插入图片描述
关于使用页表的场景(参考 linux kernel 4.9):

  1. 内核代码中使用alloc_pages从伙伴系统内存中申请

  2. 设备驱动代码使用device-tree预留内存地址建立页表映射访问(内存或寄存器,使用ioremap较多)

    ioremap() / ioremap_wc()
    
    pci_iomap()
    |
    |-> pci_iomap_range()
        |
        |-> ioremap()
        
    pci_iomap_wc()
    |
    |-> pci_iomap_wc_range()
        |
        |-> ioremap_wc()
    
  3. 设备驱动一致性内存,dma_alloc_coherent如何使用内存?attrindex使用哪个?

    arch/arm64/mm/dma-mapping.c
    |
    |-> swiotlb_dma_ops
        |
        |-> __dma_alloc(dev, size, dma_handle, flags, attrs)
            |
            |-> prot = __get_dma_pgprot(attr, PAGE_KERNEL, false); // PAGE_KERNEL = __pgprot(PROT_NORMAL)       
            |                                                      // coherent=false
            |                                                      // prot最终修改为 PTE_ATTRINDX(MT_NORMAL_NC)
            |-> ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs);
            |
            |-> coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP, prot, NULL)
                |
                |-> dma_common_pages_remap(pages, size, VM_USERMAP, prot, NULL); 	//建立NORMAL页表
    

问题

  1. PoC与PoU的区别和使用范围?
  2. 内存Inner和Outer域如何界定?
    • 多核cpu或多cluster cpu如果跑一个系统则是inner的
    • 一般cpu与DMA,cpu与GPU都是outer的
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐