浅谈“设备物理地址与虚拟地址在kernel中的映射”

参考资料:

  • Linux Kernel Source
作者:agan
联系邮箱 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中已经映射了的虚拟地址。此举至少能节约系统的虚拟地址空间。
本文是作者原创,如有错误请指正!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值