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

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(),例如:

    1 p$ Y: Y( q5 A0 H8 l; A
clk-rk3288.c (drivers\clk\rockchip)2 Z% U- Z% [; g7 b2 t9 m
static void rk3288_clk_init(struct device_node *np) {* c; P) @6 z: c6 \* S
    rk3288_cru_base = of_iomap(np, 0);
* k! K' `) \( N" U1 z+ _" Z    [...]  D; \% {3 }/ f0 Z5 u' P/ t  _1 U
}6 h0 x& {6 k1 M
2. ioremap() 实验

实验环境:

  • Linux-4.14 + Allwinner/H3。
    & \& C+ y1 ~5 i* I

实验代码:

#include <linux/init.h>
1 I& T6 b5 q7 Z$ q3 \% g  H/ Y' k#include <linux/module.h>; R: R/ d, A+ V! I7 m' N
#include <linux/sched.h>
" {! L, L: V5 I7 i7 G. G#include <asm/io.h>4 y$ h4 L' x+ l: \6 f
4 [  ?5 R* [; o3 z: r* d& m
#define USE_IOREMAP0 T% C6 D/ @, p; i$ f2 R6 B

4 G- T6 H3 P9 D& V" d#define H3_GPIO_BASE (0x01C20800)
" W, M+ d; E3 S7 ^+ h! x( P. {1 ]  S. X% `# Q' }
static volatile unsigned long *gpio_regs = NULL;% r9 ?; d; i' s* D3 q- z
9 r- z+ P% J& H' o2 y" c9 O+ G
static int __init ioremap_mod_init(void)
6 W& d% X& N; [% h{
% e/ ~2 i; @" R' X* {7 U1 m! y/ o* ~- z: X
    int i = 0;. h& t+ O4 O9 s. t+ O' F
    printk(KERN_INFO "ioremap_mod init\n");
5 j7 c2 I( D& m6 [/ e1 h' h* s6 [+ R5 D3 W# o% }" s1 a7 i# z
#ifdef USE_IOREMAP. Y$ M: h/ z3 V9 j, Y+ ^; ]# V4 |
    gpio_regs = (volatile unsigned long *)ioremap(H3_GPIO_BASE, 1024);1 P: Y, |! g- O0 ~$ J
#else5 u: j9 M4 N9 U% c- l0 Y
    gpio_regs = (volatile unsigned long *)H3_GPIO_BASE;) b9 Q9 \1 y. q2 I7 v5 U
#endif  S- N; C& W5 z7 v( K
; x, q( s& _) F. n. E" ?' ~4 C
    for (i=0; i<3; i++)$ @4 a0 V+ l, `1 S: r" W6 V/ x9 f" e6 @
        printk(KERN_INFO "reg[%d] = %lx\n", i, gpio_regs);) ~: j  r# B, x8 H- Q

4 E2 I* r% s% s- Q6 G7 [* C    return 0;
7 L' s& Y* H+ G+ W: M9 V) d, Q9 x9 e}' x# v' K( r$ }2 y% A; E- r, L: X+ L, _
module_init(ioremap_mod_init);
, z( R6 V! T$ x, a0 ^/ C1 ]
" h7 K; i/ s& T! v% j# Pstatic void __exit ioremap_mod_exit(void)
- ]6 g/ b: N) z; M3 E{6 }3 W5 D% u0 S  ~7 L4 t6 P5 u! l4 w
    printk(KERN_INFO "ioremap_mod exit\n ");
( n8 P. Z5 T  w- @5 H
) V9 `2 N+ m* Y2 r: ?#ifdef USE_IOREMAP
5 J7 g: r5 E6 W1 }+ n    iounmap(gpio_regs);
7 @! Z/ d; J- n1 |& ~) s#endif
6 y# ^% `$ z! D- K. g& E) [$ C6 x}
6 V; j1 c$ B, _- o2 ^( K1 z8 s% G5 }
module_exit(ioremap_mod_exit);
! d  F  S7 [4 t) e0 G! p
/ |" h  c1 Q( q* l: _2 Q: r. hMODULE_AUTHOR("es-hacker");  a' i8 W  g8 p) c; _
MODULE_LICENSE("GPL v2");
* {3 D( C7 w4 I: o% k6 V- P5 ]

实验结果:

使用了 ioremap()

$ insmod ioremap
2 I0 y* w; C8 }3 B* Pioremap_mod init
& ~- n% x& L9 T! Qreg[0] = 712277222 S8 L  @% b, ]
reg[1] = 33322177
( Y* d8 [- m7 A& ~/ @3 w/ dreg[2] = 773373% w  Y6 j3 y  u  z, v& a8 l4 D

未使用 ioremap():

$ insmod ioremap_mod.ko: C6 A  D: g4 n, f, ]8 @
4 w9 p5 |4 x! ~/ g7 d3 I
Unable to handle kernel paging request at virtual address 01c20800
# [) y) o: B+ Q/ W6 V* Zpgd = c9ece7c04 \. r; |  i7 m6 ~/ K
[01c20800] *pgd=6ddd7003, *pmd=00000000# @- x/ K( L+ w
Internal error: Oops: 206 [#1] SMP ARM0 w/ s& H$ Q$ y
CPU: 1 PID: 1253 Comm: insmod Tainted: G           O    4.14.111 #116
* X. i# L1 x# e5 I( e+ X. F+ zHardware name: sun8i
* k" t, n  o0 O" dtask: ef15d140 task.stack: edc500003 H5 e3 ^3 j4 P; `: }
PC is at ioremap_mod_init+0x3c/0x1000 [ioremap_mod]
/ k# o5 {5 \( b# F8 RLR is at ioremap_mod_init+0x14/0x1000 [ioremap_mod]$ @. ?+ \9 V7 g. Q( {$ Z: I
pc : [<bf5d903c>]    lr : [<bf5d9014>]    psr: 600e0013
5 e" F2 T3 N2 Tsp : edc51df8  ip : 00000007  fp : 118fa95c" g5 x& A2 C, F4 L" J( l
r10: 00000001  r9 : ee7056c0  r8 : bf5d6048
& A7 m) ?2 U0 g( ~% d9 Nr7 : bf5d6000  r6 : 00000000  r5 : bf5d6200  r4 : 00000000
* a. ~8 g# z  P' \8 A: Z/ _3 er3 : 01c20800  r2 : 01c20800  r1 : 00000000  r0 : bf5d5048" m: D$ I4 e- d) B% s1 J9 F
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
  P& W; {6 t- W% mControl: 30c5387d  Table: 49ece7c0  DAC: b106c794
4 Z& q" T3 u0 x) EProcess insmod (pid: 1253, stack limit = 0xedc50210)$ B- W  b0 Z( m0 w2 i" k

: @& ^0 N$ F2 E+ F& Y[<bf5d903c>] (ioremap_mod_init [ioremap_mod]) from [<c0201a70>] (do_one_initcall+0x40/0x16c)& t/ `* V( J+ D+ {
[<c0201a70>] (do_one_initcall) from [<c02b20c8>] (do_init_module+0x60/0x1f0)
. w" R2 K0 J% L9 s7 d5 P+ B4 G[<c02b20c8>] (do_init_module) from [<c02b1214>] (load_module+0x1b48/0x2250)
  `& s& |4 v1 T, b& A' m* P. a[<c02b1214>] (load_module) from [<c02b1ad8>] (SyS_finit_module+0x8c/0x9c)
8 V/ Z: o3 V4 p, N6 E5 V; ][<c02b1ad8>] (SyS_finit_module) from [<c0221f80>] (ret_fast_syscall+0x0/0x4c)0 B4 `) @: L$ j8 L% g7 G& k6 F2 g- ~
Code: e5953000 e3050048 e1a01004 e34b0f5d (e7932104)
: l9 T2 E- {" P- H6 B! I---[ end trace 928c64a33a054308 ]---4 X; M8 Y+ E$ i6 e" w
Segmentation fault
' N  c! s6 d- [7 @. o# R* F8 e3 n
3. ioremap() 的实现内幕

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

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

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


    $ Y* r0 ?* ^0 }/ w. k/ c6 b0 v

函数调用流程:

微信图片_20201030182049.png
点击查看大图  U6 F9 }* t! d" m* m8 j4 _" A3 y

总结一下:

  • 相关检查;

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

  • 初始化 vm_struct;

  • 建立页表;


    6 g) B. c" o" H2 D

' H. d8 Z  Z. `9 A. `+ e6 h
赞 收藏 1 评论0 发布时间:2020-10-30 18:23

举报

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