[TZ]内存与IO访问(4)-IO内存静态映射
2015-12-10 10:46
411 查看
转载请注明原文地址:http://blog.csdn.net/ts_dchs/article/details/50246543
Linux提供外设IO内存物理地址到Linux虚拟地址的静态映射。静态映射是指通过map_desc结构体静态创建I/O资源映射表。
在上面的代码中,
在这个函数中包含我们自己定义的创建静态I/O映射表的函数,在移植的时候可能需要我们自己实现。
上代码中
其中的
其中
这个结构体内容的来源在LinuxSrc/arch/arm/mach-s5pv210/cpu.c中。
其中的函数:
通过物理地址右移(除以)页大小,得到物理地址页号。
注意有时有的平台IO映射被分为多各部分,也就是说有多个
有的平台通过宏填写
这个查看源码,在得到结构体需要的虚拟地址VA时,使用了
如对于上面例子GPIO的静态映射:
最终的虚拟地址的位置是虚基址加上OFFSET(0x02200000)
Source Code Src
http://lxr.oss.org.cn
源码中
Fit all our registers in at 0xF6000000 upwards, trying to use as little of the VA space as possible so vmalloc and friends have a better chance of getting memory.
这表示相距4G,保留了160M的映射空间吗?(之后有描述)
同时也说明这种映射的结果是在内核空间。
在声明map的文件中做映射准备,创建map_des,添加到数组,以便系统启动时遍历数组可以完成这个映射的建立。
定义宏XXX_SRAM_BASE物理地址0x30000000
写Module直接测试设备(用户空间的应用程序无法访问设备)
在Module中直接访问地址(使用了一个网上的例子,原理类似)IO_ADDRESS宏的功能与S3C_ADDR_BASE类似。
或者自己写驱动程序让应用程序间接访问内核空间内存。
附加一些内存布局的内容来探索这部分映射的虚拟地址究竟在哪里:
首先是在VMALLOC_END之后,VMALLOC区域用于
这恰好就是之前
在Menory文档中描述:
0xfeffffff - 0xf6000000 = 9*2^24 B = 144MB 这段空间可以用作mapping
notification
source: 《Linux设备驱动开发详解》(第二版),内容为读书笔记和网络资料,有些资料原始来源不详,分享为了方便自己和他人查阅。如有侵权请及时告知,对于带来的不便非常抱歉。转载请注明来源。个人所学有限,若有错误和不足还请不吝赐教,我会及时更正。Terrence Zhou.
http://blog.csdn.net/ts_dchs
reference
[1] 静态地址映射,http://www.cnblogs.com/qiaoge/archive/2012/04/23/2467052.html
[2] (最好教材)源码,http://lxr.oss.org.cn
1 流程分析
(本节以s5pv210(ARM A8) of Linux 3.0.26为例)Linux提供外设IO内存物理地址到Linux虚拟地址的静态映射。静态映射是指通过map_desc结构体静态创建I/O资源映射表。
struct map_desc结构体包含虚拟地址,设备物理地址。常用于寄存器资源映射,这样静态映射之后在编写内核代码或驱动时就不需要再ioremap。系统启动时创建好静态映射,程序直接通过映射后的虚拟地址去访问它们。 先说启动时做初始化的工作。内核提供了一个重要的结构体[code]struct machine_desc ,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。为了初始化工作,包括map_io, init_irq, init_machine以及phys_io , timer成员等。 这里的map_io成员即内核提供给用户的创建外设I/O资源到内核虚拟地址静态映射表的接口函数。map_io成员函数会在系统初始化过程中Start_kernel -> setup_arch() –> paging_init() –> devicemaps_init()中被调用。结构体通过[code]MACHINE_START宏初始化。这部分根据平台不同设计。 [code]//LinuxSrc/arch/arm/mach-s5pv210/mach-smdkv210.c MACHINE_START(SMDKV210, "SMDKV210") /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */ .boot_params = S5P_PA_SDRAM + 0x100, .init_irq = s5pv210_init_irq, .map_io = smdkv210_map_io, .init_machine = smdkv210_machine_init, .timer = &s5p_timer, MACHINE_END
在上面的代码中,
map_io初始化为
smdkv210_map_iocode。如下:
//LinuxSrc/arch/arm/mach-s5pv210/mach-smdkv210.c static void __init smdkv210_map_io(void) { s5p_init_io(NULL, 0, S5P_VA_CHIPID); s3c24xx_init_clocks(24000000); s3c24xx_init_uarts(smdkv210_uartcfgs, ARRAY_SIZE(smdkv210_uartcfgs)); s5p_set_timer_source(S5P_PWM2, S5P_PWM4); }
在这个函数中包含我们自己定义的创建静态I/O映射表的函数,在移植的时候可能需要我们自己实现。
上代码中
s5p_init_io(NULL, 0, S5P_VA_CHIPID);函数中代码如下: [code]//LinuxSrc/arch/arm/plat-s5p/cpu.c void __init s5p_init_io(struct map_desc *mach_desc, int size, void __iomem *cpuid_addr) { unsigned long idcode; /* initialize the io descriptors we need for initialization */ iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc));/* register our io-tables */ if (mach_desc) iotable_init(mach_desc, size); idcode = __raw_readl(cpuid_addr); s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); }
其中的
iotale_init()就是最终建立页映射的函数。代码如下: [code]//LiuxSrc/arch/arm/mm/mmu.c void __init iotable_init(struct map_desc *io_desc, int nr) { int i; for (i = 0; i < nr; i++) create_mapping(io_desc + i); }//__
其中
create_mapping函数就是通过多个[code]map_desc提供的信息创建线性映射表的。
这个结构体内容的来源在LinuxSrc/arch/arm/mach-s5pv210/cpu.c中。
static struct map_desc s5pv210_iodesc[] __initdata。形如: [code]//LinuxSrc/arch/arm/mach-s5pv210/cpu.c //这个数组描述了每个映射的信息 static struct map_desc s5pv210_iodesc[] __initdata = { { .virtual = (unsigned long)S5P_VA_SYSTIMER, .pfn = __phys_to_pfn(S5PV210_PA_SYSTIMER), .length = SZ_4K, .type = MT_DEVICE, }, { .virtual = (unsigned long)S5P_VA_GPIO, //LinuxSrc/arch/arm/plat-s5p/include/plat/map-s5p.h 对用到的虚拟地址做了定义 //这个值即该I/O资源映射后的内核虚拟地址,创建映射表成功后,便可以在内核或驱动中直接通过该虚拟地址访问这个I/O资源。 //#define S5P_VA_GPIO S3C_ADDR(0x02200000) .pfn = __phys_to_pfn(S5PV210_PA_GPIO), //LinuxSrc/arch/arm/mach-s5pv210/include/mach/map.h 对用到的物理地址做了定义 //#define S5PV210_PA_GPIO 0xE0200000 .length = SZ_4K, .type = MT_DEVICE, }, { .virtual = (unsigned long)VA_VIC0, .pfn = __phys_to_pfn(S5PV210_PA_VIC0), .length = SZ_16K, .type = MT_DEVICE, }, ... }
其中的函数:
#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
通过物理地址右移(除以)页大小,得到物理地址页号。
注意有时有的平台IO映射被分为多各部分,也就是说有多个
map_desc[]。
有的平台通过宏填写
map_desc[]。如s3c2410在结构体数组中有
IODESC_ENT(LCD),。
这个查看源码,在得到结构体需要的虚拟地址VA时,使用了
S3C_ADDR的宏:
//LinuxSrc/arch/arm/plat-samsung/include/plat/map-base.h #define S3C_ADDR_BASE 0xF6000000 #ifndef __ASSEMBLY__ #define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x)) #else #define S3C_ADDR(x) (S3C_ADDR_BASE + (x)) #endif
如对于上面例子GPIO的静态映射:
//LinuxSrc/arch/arm/plat-s5p/include/plat/map-s5p.h #define S5P_VA_GPIO S3C_ADDR(0x02200000)
最终的虚拟地址的位置是虚基址加上OFFSET(0x02200000)
Source Code Src
http://lxr.oss.org.cn
2 一个简单例子 移植时来实现自己的静态映射
首先知道要进行静态映射的四个参数。物理地址是硬件定义好的,虚拟地址的使用需要避开冲突。源码中
S3C_ADDR_BASE是0xF6000000,注释中也提到:
Fit all our registers in at 0xF6000000 upwards, trying to use as little of the VA space as possible so vmalloc and friends have a better chance of getting memory.
这表示相距4G,保留了160M的映射空间吗?(之后有描述)
同时也说明这种映射的结果是在内核空间。
在声明map的文件中做映射准备,创建map_des,添加到数组,以便系统启动时遍历数组可以完成这个映射的建立。
定义宏XXX_SRAM_BASE物理地址0x30000000
static struct map_desc xxx_iodesc[] __initdata = { ... { .virtual = XXX_VA, .pfn = __phys_to_pfn(XXX_PA), .length = SZ_4K, .type = MT_DEVICE }, };
写Module直接测试设备(用户空间的应用程序无法访问设备)
在Module中直接访问地址(使用了一个网上的例子,原理类似)IO_ADDRESS宏的功能与S3C_ADDR_BASE类似。
//init函数中申请IO内存 - request_mem_region struct resource * ret = request_mem_region(SRAM_BASE, SRAM_SIZE, "SRAM Region");//检查内存可用,声明占有 test() //下面是test()的内容 char str[] = "Hello/n"; void * sram_p; sram_p = (void *)IO_ADDRESS (XXX_SRAM_BASE); memcpy(sram_p, str, sizeof(str)); printk(sram_p); printk("/n"); //exit中 - release_mem_region
或者自己写驱动程序让应用程序间接访问内核空间内存。
附加一些内存布局的内容来探索这部分映射的虚拟地址究竟在哪里:
首先是在VMALLOC_END之后,VMALLOC区域用于
vmalloc()/
ioremap()。
//LinuxSrc/arch/arm/include/asm/highmem.h #define VMALLOC_END 0xF6000000UL
这恰好就是之前
S3C_ADDR_BASE的来源。那么有多大的空间可以做映射呢?
在Menory文档中描述:
//LinuxSrc/Documentation/arm/memory.txt VMALLOC_END feffffff Free for platform use, recommended. VMALLOC_END must be aligned to a 2MB boundary.
0xfeffffff - 0xf6000000 = 9*2^24 B = 144MB 这段空间可以用作mapping
notification
source: 《Linux设备驱动开发详解》(第二版),内容为读书笔记和网络资料,有些资料原始来源不详,分享为了方便自己和他人查阅。如有侵权请及时告知,对于带来的不便非常抱歉。转载请注明来源。个人所学有限,若有错误和不足还请不吝赐教,我会及时更正。Terrence Zhou.
http://blog.csdn.net/ts_dchs
reference
[1] 静态地址映射,http://www.cnblogs.com/qiaoge/archive/2012/04/23/2467052.html
[2] (最好教材)源码,http://lxr.oss.org.cn
相关文章推荐
- #新闻拍一拍# IBM 招聘广告要求应聘者具备至少 12 年 K8S 使用经验
- vivi下重新调整分区
- ARM Linux系统启动
- Linux及ARM Linux程序开发笔记(零基础入门篇)
- IE7降低内存和降低CPU的几个技巧
- 如何高效的使用内存
- DOS下内存的配置
- XP/win2003下发现1G的内存比512M还慢的解决方法
- PowerShell实现动态获取当前脚本运行时消耗的内存
- C#实现把dgv里的数据完整的复制到一张内存表的方法
- SQL语句实现查询SQL Server内存使用状况
- C语言内存对齐实例详解
- 深入学习C语言中memset()函数的用法
- 全局变量与局部变量在内存中的区别详细解析
- VB读取线程、句柄及写入内存的API代码实例
- php运行提示:Fatal error Allowed memory size内存不足的解决方法
- IE浏览器IFrame对象内存不释放问题解决方法
- C#之CLR内存深入分析
- JavaScript 变量、作用域及内存
- JavaScript避免内存泄露及内存管理技巧