页面加载中,请稍候
来源:嵌入式Linux系统开发发布时间:2023-05-201270浏览
询问 AI这里只考虑有 MMU 的芯片,Linux 为了实现进程虚拟地址空间,在启用 MMU 后,在内核中操作的都是虚拟地址,内核访问不到物理地址。
如果在驱动里直接访问物理地址,等于访问了一个非法地址,会导致内核崩溃,下面会有一个相关的小实验。
通过 ioremap 将物理地址映射为虚拟地址后,内核就能通过 ioremap() 返回的虚拟地址,以 虚拟地址->mmu页表映射-> 物理地址 的形式正确地访问到物理地址了。
ARM Linux 引入设备树特性后,一些支持设备树的设备驱动不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的内部仍然会调用 ioremap(),例如:
clk-rk3288.c(drivers\clk\rockchip)2. ioremap() 实验
staticvoidrk3288_clk_init(structdevice_node*np){
rk3288_cru_base=of_iomap(np,0);
[...]
}
实验环境:
实验代码:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/sched.h>
#include<asm/io.h>
#defineUSE_IOREMAP
#defineH3_GPIO_BASE(0x01C20800)
staticvolatileunsignedlong*gpio_regs=NULL;
staticint__initioremap_mod_init(void)
{
inti=0;
printk(KERN_INFO"ioremap_modinit\n");
#ifdefUSE_IOREMAP
gpio_regs=(volatileunsignedlong*)ioremap(H3_GPIO_BASE,1024);
#else
gpio_regs=(volatileunsignedlong*)H3_GPIO_BASE;
#endif
for(i=0;i<3;i++)
printk(KERN_INFO"reg[%d]=%lx\n",i,gpio_regs[i]);
return0;
}
module_init(ioremap_mod_init);
staticvoid__exitioremap_mod_exit(void)
{
printk(KERN_INFO"ioremap_modexit\n");
#ifdefUSE_IOREMAP
iounmap(gpio_regs);
#endif
}
module_exit(ioremap_mod_exit);
MODULE_AUTHOR("es-hacker");
MODULE_LICENSE("GPLv2");
实验结果:
使用了 ioremap()
$insmodioremap
ioremap_modinit
reg[0]=71227722
reg[1]=33322177
reg[2]=773373
未使用 ioremap():
$insmodioremap_mod.ko3. ioremap() 的实现内幕
Unabletohandlekernelpagingrequestatvirtualaddress01c20800
pgd=c9ece7c0
[01c20800]*pgd=6ddd7003,*pmd=00000000
Internalerror:Oops:206[#1]SMPARM
CPU:1PID:1253Comm:insmodTainted:GO4.14.111#116
Hardwarename:sun8i
task:ef15d140task.stack:edc50000
PCisatioremap_mod_init+0x3c/0x1000[ioremap_mod]
LRisatioremap_mod_init+0x14/0x1000[ioremap_mod]
pc:[<bf5d903c>]lr:[<bf5d9014>]psr:600e0013
sp:edc51df8ip:00000007fp:118fa95c
r10:00000001r9:ee7056c0r8:bf5d6048
r7:bf5d6000r6:00000000r5:bf5d6200r4:00000000
r3:01c20800r2:01c20800r1:00000000r0:bf5d5048
Flags:nZCvIRQsonFIQsonModeSVC_32ISAARMSegmentuser
Control:30c5387dTable:49ece7c0DAC:b106c794
Processinsmod(pid:1253,stacklimit=0xedc50210)
[<bf5d903c>](ioremap_mod_init[ioremap_mod])from[<c0201a70>](do_one_initcall+0x40/0x16c)
[<c0201a70>](do_one_initcall)from[<c02b20c8>](do_init_module+0x60/0x1f0)
[<c02b20c8>](do_init_module)from[<c02b1214>](load_module+0x1b48/0x2250)
[<c02b1214>](load_module)from[<c02b1ad8>](SyS_finit_module+0x8c/0x9c)
[<c02b1ad8>](SyS_finit_module)from[<c0221f80>](ret_fast_syscall+0x0/0x4c)
Code:e5953000e3050048e1a01004e34b0f5d(e7932104)
---[endtrace928c64a33a054308]---
Segmentationfault
ioremap() 的实现内幕会涉及到比较多的内存管理的知识,这里我们抛开代码细节简单了解一下原理就好。
ioremap() 将 vmalloc 区的某段虚拟内存块映射到 io memory,其实现原理与vmalloc() 类似,都是通过在 vmalloc 区分配虚拟地址块,然后修改内核页表的方式将其映射到设备的 I/O 地址空间。
与 vmalloc() 不同的是,ioremap 并不需要通过伙伴系统去分配物理页,因为ioremap 要映射的目标地址是 io memory,不是物理内存 (RAM)。
函数调用流程:

总结一下:
相关检查;
分配一个 vm_struct 结构体,内核在管理虚拟内存中的 vmalloc 区时,内核必须跟踪哪些子区域被使用、哪些是空闲的,对应的数据结构就是 vm_strcut。
初始化 vm_struct;
建立页表;
深入理解 Linux 内核 / 8.3.2
深入 Linux 内核架构 / 3.5.7
深入理解Linux设备驱动程序内核机制 / 3.5.3
https://blog.csdn.net/njuitjf/article/details/40745227
新闻来源:嵌入式Linux系统开发,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
2026-07-07

2026-06-12

2026-06-05