浅谈“设备物理地址与虚拟地址在kernel中的映射”
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
参考资料:
- Linux Kernel Source
联系邮箱 beswipe@yahoo.com.cn
racer.blog.chinaunix.net
转载须注明出处!
在编写设备驱动的时候,为什么有些设备地址可以直接拿来使用,而有些需要先映射才可使用。以下是我Track代码的一些经验。
1. 内存映射的几种情况。
第一种情况
----------
在平台的Linux启动过程中,平台底层源码会初始化一个IOTABLE。
通常的调用堆栈如下:
machine_XXX.map_io()->
iotable_init() ->
arch/arm/mm/mmu.c:alloc_init_section()
这里会把一组平台相关的物理地址(比如总线地址、设备基地址)映射到一组固定虚拟地址上,这组虚拟地址在整个内核空间可见,且从来不会被UNMAP。这个过程一般在平台初始化过程中被调用。平台厂商一般出厂前都会定义好自己的iotable,因此很多时候访问设备并不需要用户自己去做内存映射。
第二种情况
----------
驱动通过
ioremap_XXX()(定义位于"
arch/arm/include/asm/io.h")把物理地址映射成为虚拟地址。通过阅读代码'
arch/arm/mm/ioremap.c:__arm_ioremap_pfn()',发现其流程大致如下:首先通过'
get_vm_area(size, VM_IOREMAP)'获取一个空闲的虚拟内存区域,然后通过
remap_area_section()把物理地址映射到这片内存区域。 对比
mmu.c:alloc_init_section() 和
ioremap.c:remap_area_sections(),发现他们的工作内容基本一致,唯一的区别就是每次ioremap返回的虚拟地址,是随机不固定的。
第三种情况
----------
在NOMMU的情况下,物理地址就是虚拟地址,"
arm/arm/mm/nommu.c:__arm_ioremap()",此函数直接返回物理地址。
2. Samsung 6410 Kernel (linux-2.6.28), 映射连接在Xm0CSn1上的DM9000网卡基地址。
mach-smdk6410.c:smdk6410_map_io(smdk6410_iodesc) ->
plat-s3c64xx/cpu.c:s3c64xx_init_io():
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
iotable_init(mach_desc, size);
其中,s3c_iodesc数组包含了6410平台基本上所有模块的基地址映射,mach_desc则对应DMP9000的定义:
struct map_desc smdk6410_iodesc[] = {
{
.virtual = (u32)S3C64XX_VA_DM9000,
.pfn = __phys_to_pfn(S3C64XX_PA_DM9000),
.length = S3C64XX_SZ_DM9000,
.type = MT_DEVICE,
},
};
后续的DM9000驱动如何使用此虚拟地址,请看:
plat-s3c64xx/devs.c:
dm9000_resources_cs1
net/dm9000_con201.c:
if (pdev->num_resources == 2) {
base = pdev->resource[0].start;
if (!request_mem_region(base, 4, ndev->name)) {
ret = -EBUSY;
goto out;
}
ndev->base_addr = base;
ndev->irq = pdev->resource[1].start;
db->io_addr = (void __iomem *)base;
db->io_data = (void __iomem *)(base + DM9000_CMD);
}
可以看到,在iotable_init后,驱动再无映射同一物理地址。顺便提一下,此平台并没有提供自己的ioremap函数。它一直在使用arm平台的默认映射函数,其定义位于'
arch/arm/include/asm/io.h'。
3. OMAP3530 Kernel 'linux-omap-pm-2.6.38' 的ioremap函数实现。
查看源代码‘arch/arm/mach-omap2/io.c’:
iotable_init(omap34xx_io_desc, ARRAY_SIZE(omap34xx_io_desc));
// Map buses and devices base physical address:
.virtual = L3_34XX_VIRT,
.pfn = __phys_to_pfn(L3_34XX_PHYS),
.virtual = ...
.pfn = __phys_to_pfn(L4_34XX_PHYS),
.pfn = __phys_to_pfn(OMAP34XX_GPMC_PHYS),
.pfn = __phys_to_pfn(OMAP343X_SMS_PHYS),
.pfn = __phys_to_pfn(OMAP343X_SDRC_PHYS),
.pfn = __phys_to_pfn(L4_PER_34XX_PHYS),
.pfn = __phys_to_pfn(L4_EMU_34XX_PHYS),
.pfn = __phys_to_pfn(ZOOM_UART_BASE),
这里基本上映射了omap3530片內所有模块的设备基地址。后续ioremap的调用堆栈如下:
arch/arm/include/asm/io.h:250:#define ioremap_nocache(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
arch/arm/plat-omap/include/plat/io.h:298:#define __arch_ioremap omap_ioremap
arch/arm/plat-omap/io.c: void __iomem *omap_ioremap(unsigned long p, size_t size, unsigned int type)
if (BETWEEN(p, L3_34XX_PHYS, L3_34XX_SIZE))
return XLATE(p, L3_34XX_PHYS, L3_34XX_VIRT);
// #define XLATE(p,pst,vst) ((void __iomem *)((p) - (pst) + (vst)))
由此,此平台实现了自己的ioremap函数,它首先会检查需要映射的物理地址是否已经映射,如此则返回在iotable_init中已经映射了的虚拟地址。此举至少能节约系统的虚拟地址空间。
本文是作者原创,如有错误请指正!
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献7条内容
所有评论(0)