文章目录
- 1.1 背景
- 1.1.1 ARM32 内存空间
- 1.1.2 ioremap 实现
- 1.1.3 Linux内存属性
上篇文章:ARM Linux 内存管理入门及渐进 4 - 常用接口实现(memcpy/copy_to_user)
1.1 背景
在编写 linux 驱动过程中,不可避免的会涉及操作外设,而外设的地址空间与 DDR的地址空间一般不连续,在 linux上电时,并不会为外设地址空间建立页表,又因为linux 访问内存使用的都是虚拟地址,因此如果想访问外设的寄存器(一般包括数据寄存器、控制寄存器与状态寄存器),需要在驱动初始化中将外设所处的物理地址映射为虚拟地址,linux 为应对该问题提供了较多接口以应对不同的场景需求,arch/arm64(arm)/include/asm/io.h中有如下几种 ioremap
接口:
ioremap
ioremap_wc
devm_ioremap
devm_ioremap_resource
- ioremap:用来映射 memory type 为 device memory 的设备,同时不使用cache(device memory本身就没有 cacheable 这个属性),即 CPU 的读写操作直接操作设备内存。
- ioremap_nocache:的实现与 ioremap 完全相同,保留该符号是因为向后兼容使用ioremap_nocache接口的驱动程序。
- ioremap_cached 用来映射 memory type 为 normal memory 的设备,同时使用cache,这会提高内存的访问速度,提高系统的性能。
ARM Linux 引入设备树特性后,一些支持设备树的设备驱动不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的内部仍然会调用 ioremap()
1.1.1 ARM32 内存空间
通常进程的4GB内存空间被人为的分为两个部分:
- 用户空间与, 用户空间地址分布从0到3GB;
- 内核空间,3GB-4GB。
内核空间中,从3G到vmalloc_start
这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表mem_map等等)。在物理内存映射区之后,就是vmalloc
区域,vmalloc_end
的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)。
1.1.2 ioremap 实现
arch/arm64/include/asm/io.h
#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 iounmap __iounmap
从上面的接口定义看,除了addr和size外还有一个__pgprot()
参数,上面接口中参数类型有:
- PROT_DEVICE_nGnRE;
- PROT_NORMAL_NC。
两种类型。实际在 pgtable-prot.h 中还有PROT_NORMAL_WT
和PROT_NORMAL
。ioremap_xxx 接口的功能属性都是由__pgprot()决定的,那__pgprot()具体是什么含义呢?
1.1.3 Linux内存属性
在内核源码memory.h中有定义ioremap接口中可用的内存类型:
arch/arm64/include/asm/memory.h
/*
* Memory types available.
*/
#define MT_DEVICE_nGnRnE 0
#define MT_DEVICE_nGnRE 1
#define MT_DEVICE_GRE 2
#define MT_NORMAL_NC 3
#define MT_NORMAL 4
#define MT_NORMAL_WT 5
/*
* Memory types for Stage-2 translation
*/
#define MT_S2_NORMAL 0xf
#define MT_S2_DEVICE_nGnRE 0x1
上面 ioremap_xxx 接口中用到的属性参数主要是PROT_DEVICE_nGnRE
和PROT_NORMAL_NC
。看下这两个prot_val分别代表什么含义:
#define PROT_DEFAULT (_PROT_DEFAULT | PTE_MAYBE_NG)
#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)
#define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
#define PROT_NORMAL_WT (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
#define PROT_SECT_NORMAL_EXEC (PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
参照ARMv8手册中对内存属性的描述,内存可以分为DEVICE
和NORMAL
两大类型以及Device memory依据是否可合并等属性。
- 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寄存器定义如下:
Linux 预先定义了6种内存属性,分别存在MAIR寄存器的attr0~attr5。内存页表属性部分可以选择这个寄存器的某个index,范围(0~5)作为自己的属性。
上篇文章:ARM Linux 内存管理入门及渐进 4 - 常用接口实现(memcpy/copy_to_user)