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

Linux操作寄存器前需要ioremap

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

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

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

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


    + }8 X: w- F- V
clk-rk3288.c (drivers\clk\rockchip)( {* y% G! b" Q/ H
static void rk3288_clk_init(struct device_node *np) {
# i+ E6 C$ A$ ?1 U: a1 }% y; T    rk3288_cru_base = of_iomap(np, 0);
. d9 y; W, P- A) F    [...]
/ R+ A; ?0 ?9 p( R  x& O+ k}
! M5 R' J: i+ _/ C
2. ioremap() 实验

实验环境:

  • Linux-4.14 + Allwinner/H3。% a3 \0 m& @5 n# g/ f$ v1 ~9 z

实验代码:

#include <linux/init.h>
1 c  a& H4 s( P$ w: m#include <linux/module.h>
/ @' J6 d  q% l9 @#include <linux/sched.h>. n. @* M1 E' w$ T4 l$ F0 ^- R  c4 @
#include <asm/io.h>
8 [; C( Z  C) u7 Q9 N8 Q
& O5 j' @( I, K& r! `$ f2 t3 I8 d#define USE_IOREMAP% T: e( M$ y+ h  P3 Y) E/ P
7 m. ]) c* a1 w7 A
#define H3_GPIO_BASE (0x01C20800)
, @6 Z0 [) H" u; Y0 U: N: K0 x: d% R! N
$ f1 q: {& _0 v& f. Gstatic volatile unsigned long *gpio_regs = NULL;
2 k: ~$ y8 [. b0 ^: d6 k
4 A1 K' B' N0 x: w4 H+ Wstatic int __init ioremap_mod_init(void)
+ L7 z. d8 ~6 `' }{! q1 U' t  u) s) B

) X* n8 e8 Z9 s+ `7 y3 T    int i = 0;
# v/ a4 ^- c8 v& J    printk(KERN_INFO "ioremap_mod init\n");) J3 G6 `! j- p  X
1 ]. f" \0 t8 M- U) O8 N
#ifdef USE_IOREMAP
$ A7 D% B! W; q3 U    gpio_regs = (volatile unsigned long *)ioremap(H3_GPIO_BASE, 1024);! E% B2 X9 M9 q; ]5 q8 M
#else
* Z: }, @  q: }) [+ I  w4 P    gpio_regs = (volatile unsigned long *)H3_GPIO_BASE;4 p5 N5 V" `$ x( j2 W7 T2 s9 }& N( e
#endif
2 c3 B% y; ]5 E6 e( l& v0 A# X
- k" X8 Q# v$ i; g% l; l    for (i=0; i<3; i++)7 H, L  r/ v- {5 O7 N% t+ x
        printk(KERN_INFO "reg[%d] = %lx\n", i, gpio_regs);8 X7 s/ ]7 z( X3 m" f, P

( {: I% m* o+ I- j    return 0;
' J4 S% @' `- A: d1 I}, y/ n" `, D4 E! D/ p0 t4 l
module_init(ioremap_mod_init);( ?7 p# O( }- J3 f3 @; m2 l
( ^9 K% ~5 a7 `2 p  [
static void __exit ioremap_mod_exit(void)
! y6 A: h, W2 Z) y$ [{' ~# l, q, `8 p6 t4 W' ]9 Z
    printk(KERN_INFO "ioremap_mod exit\n ");: X( n( r. [: f" _# G( M
0 p3 c) O. {, k! D
#ifdef USE_IOREMAP
5 t! u! t2 R+ ]  P) c4 ]  E    iounmap(gpio_regs);
) n1 b, c* v5 C# `3 o, ^#endif
- B: H3 c; z* V! j: q' ?}" e) B; p. K. A4 q$ y; h

/ H" [1 M8 B- a' s, ?module_exit(ioremap_mod_exit);
& G, V  a9 q; x2 t5 u( Q
+ D6 f2 c2 \0 j% U) z; O: ?MODULE_AUTHOR("es-hacker");/ q+ q3 Q9 D. M: n% L0 q
MODULE_LICENSE("GPL v2");
5 x+ K9 p4 o3 d- `( A8 [

实验结果:

使用了 ioremap()

$ insmod ioremap* \* G2 J. w9 k* R
ioremap_mod init: _6 P1 n1 T- z3 U7 b
reg[0] = 712277223 z, n2 O4 t* W% U
reg[1] = 333221770 x7 g! v: `; Y& [- B
reg[2] = 7733739 q9 M5 j$ b5 [, h6 G

未使用 ioremap():

$ insmod ioremap_mod.ko
. c, S% r8 g% O2 h4 o. V5 h8 l9 V# J' l  J' d/ M" n
Unable to handle kernel paging request at virtual address 01c20800
% |( v/ w' ?* g" B2 Gpgd = c9ece7c0
1 M' M  }6 }1 R1 Q( B( ~" X; u[01c20800] *pgd=6ddd7003, *pmd=00000000
4 Z1 P' ?( P8 ]- D& wInternal error: Oops: 206 [#1] SMP ARM+ f" C" ^, w' m( M
CPU: 1 PID: 1253 Comm: insmod Tainted: G           O    4.14.111 #116
/ y+ V9 [( C! B2 D# rHardware name: sun8i3 O& ]; p; N) D" ]' F+ @
task: ef15d140 task.stack: edc500006 k. Z$ e0 ^7 P) ^0 L, H8 g
PC is at ioremap_mod_init+0x3c/0x1000 [ioremap_mod]1 J& L5 m. _% |' O* b. x" X
LR is at ioremap_mod_init+0x14/0x1000 [ioremap_mod]4 s, o# E5 ?( H- z* R% q
pc : [<bf5d903c>]    lr : [<bf5d9014>]    psr: 600e0013
% M" |" f1 t9 b( n) Zsp : edc51df8  ip : 00000007  fp : 118fa95c
7 [2 y) k$ l' j6 G9 {r10: 00000001  r9 : ee7056c0  r8 : bf5d6048
9 Q5 x6 N& Z5 h' ~9 {( cr7 : bf5d6000  r6 : 00000000  r5 : bf5d6200  r4 : 00000000
! T5 [; {5 G$ N; er3 : 01c20800  r2 : 01c20800  r1 : 00000000  r0 : bf5d5048* J+ ?" I/ O) R/ G2 H; h
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
- K( F4 C/ y4 C( V* JControl: 30c5387d  Table: 49ece7c0  DAC: b106c794
+ [% L/ X/ S( K4 y. W9 `Process insmod (pid: 1253, stack limit = 0xedc50210)
- s( X+ ]( P* H3 b0 ?' l5 {" A* a8 @4 v4 q6 }1 I+ ~1 t
[<bf5d903c>] (ioremap_mod_init [ioremap_mod]) from [<c0201a70>] (do_one_initcall+0x40/0x16c)9 g6 v- F5 `* Y4 Q( D2 O: X
[<c0201a70>] (do_one_initcall) from [<c02b20c8>] (do_init_module+0x60/0x1f0)2 }; A/ @" e0 A8 q
[<c02b20c8>] (do_init_module) from [<c02b1214>] (load_module+0x1b48/0x2250)0 Q+ }3 X7 _8 _8 Z5 ]
[<c02b1214>] (load_module) from [<c02b1ad8>] (SyS_finit_module+0x8c/0x9c)7 d0 k; h. a) B: o5 J. W
[<c02b1ad8>] (SyS_finit_module) from [<c0221f80>] (ret_fast_syscall+0x0/0x4c), q5 \7 m, D$ v+ L5 y+ m" c
Code: e5953000 e3050048 e1a01004 e34b0f5d (e7932104) ' h1 T, u2 X- E& @0 @  E
---[ end trace 928c64a33a054308 ]---" N+ Z) H# A5 y  E9 f5 d
Segmentation fault
  ^) ~; ~* Z+ Q
3. ioremap() 的实现内幕

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

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

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


    & y, d( h+ C! V( ?

函数调用流程:

微信图片_20210225122929.png
点击查看大图
. D2 }6 H( g2 `! q8 j

总结一下:

  • 相关检查;

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

  • 初始化 vm_struct;

  • 建立页表;


    ) S' w& B0 z* v  g# [
4. 相关参考
  • 深入理解 Linux 内核 / 8.3.2

  • 深入 Linux 内核架构 / 3.5.7

  • 深入理解Linux设备驱动程序内核机制 / 3.5.3

  • https://blog.csdn.net/njuitjf/article/details/40745227

    ) V1 Z& y5 C+ p2 I) B

# Y  p& n! D8 S& m- i
赞 收藏 评论0 发布时间:2021-2-25 12:30

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版