你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

Linux操作寄存器要ioremap的原因

[复制链接]
gaosmile 发布时间:2020-10-30 18:23
1. 原因
  • 这里只考虑有 MMU 的芯片,Linux 为了实现进程虚拟地址空间,在启用 MMU 后,在内核中操作的都是虚拟地址,内核访问不到物理地址。

  • 如果在驱动里直接访问物理地址,等于访问了一个非法地址,会导致内核崩溃,下面会有一个相关的小实验。

  • 通过 ioremap 将物理地址映射为虚拟地址后,内核就能通过 ioremap() 返回的虚拟地址,以 虚拟地址->mmu页表映射-> 物理地址 的形式正确地访问到物理地址了。

  • ARM Linux 引入设备树特性后,一些支持设备树的设备驱动不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的内部仍然会调用 ioremap(),例如:


clk-rk3288.c (drivers\clk\rockchip)
static void rk3288_clk_init(struct device_node *np) {
    rk3288_cru_base = of_iomap(np, 0);
    [...]
}
2. ioremap() 实验

实验环境:

  • Linux-4.14 + Allwinner/H3。

实验代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/io.h>

#define USE_IOREMAP

#define H3_GPIO_BASE (0x01C20800)

static volatile unsigned long *gpio_regs = NULL;

static int __init ioremap_mod_init(void)
{

    int i = 0;
    printk(KERN_INFO "ioremap_mod init\n");

#ifdef USE_IOREMAP
    gpio_regs = (volatile unsigned long *)ioremap(H3_GPIO_BASE, 1024);
#else
    gpio_regs = (volatile unsigned long *)H3_GPIO_BASE;
#endif

    for (i=0; i<3; i++)
        printk(KERN_INFO "reg[%d] = %lx\n", i, gpio_regs);

    return 0;
}
module_init(ioremap_mod_init);

static void __exit ioremap_mod_exit(void)
{
    printk(KERN_INFO "ioremap_mod exit\n ");

#ifdef USE_IOREMAP
    iounmap(gpio_regs);
#endif
}

module_exit(ioremap_mod_exit);

MODULE_AUTHOR("es-hacker");
MODULE_LICENSE("GPL v2");

实验结果:

使用了 ioremap()

$ insmod ioremap
ioremap_mod init
reg[0] = 71227722
reg[1] = 33322177
reg[2] = 773373

未使用 ioremap():

$ insmod ioremap_mod.ko

Unable to handle kernel paging request at virtual address 01c20800
pgd = c9ece7c0
[01c20800] *pgd=6ddd7003, *pmd=00000000
Internal error: Oops: 206 [#1] SMP ARM
CPU: 1 PID: 1253 Comm: insmod Tainted: G           O    4.14.111 #116
Hardware name: sun8i
task: ef15d140 task.stack: edc50000
PC is at ioremap_mod_init+0x3c/0x1000 [ioremap_mod]
LR is at ioremap_mod_init+0x14/0x1000 [ioremap_mod]
pc : [<bf5d903c>]    lr : [<bf5d9014>]    psr: 600e0013
sp : edc51df8  ip : 00000007  fp : 118fa95c
r10: 00000001  r9 : ee7056c0  r8 : bf5d6048
r7 : bf5d6000  r6 : 00000000  r5 : bf5d6200  r4 : 00000000
r3 : 01c20800  r2 : 01c20800  r1 : 00000000  r0 : bf5d5048
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 30c5387d  Table: 49ece7c0  DAC: b106c794
Process insmod (pid: 1253, stack limit = 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: e5953000 e3050048 e1a01004 e34b0f5d (e7932104)
---[ end trace 928c64a33a054308 ]---
Segmentation fault
3. ioremap() 的实现内幕

ioremap() 的实现内幕会涉及到比较多的内存管理的知识,这里我们抛开代码细节简单了解一下原理就好。

  • ioremap() 将 vmalloc 区的某段虚拟内存块映射到 io memory,其实现原理与vmalloc() 类似,都是通过在 vmalloc 区分配虚拟地址块,然后修改内核页表的方式将其映射到设备的 I/O 地址空间。

  • 与 vmalloc() 不同的是,ioremap 并不需要通过伙伴系统去分配物理页,因为ioremap 要映射的目标地址是 io memory,不是物理内存 (RAM)。


函数调用流程:

微信图片_20201030182049.png
点击查看大图

总结一下:

  • 相关检查;

  • 分配一个 vm_struct 结构体,内核在管理虚拟内存中的 vmalloc 区时,内核必须跟踪哪些子区域被使用、哪些是空闲的,对应的数据结构就是 vm_strcut。

  • 初始化 vm_struct;

  • 建立页表;



收藏 1 评论0 发布时间:2020-10-30 18:23

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版