1. 原因
- 這里只考慮有 MMU 的芯片,Linux 為了實現進程虛擬地址空間,在啟用 MMU 后,在內核中操作的都是虛擬地址,內核訪問不到物理地址。
- 如果在驅動里直接訪問物理地址,等于訪問了一個非法地址,會導致內核崩潰,下面會有一個相關的小實驗。
- 通過 ioremap 將物理地址映射為虛擬地址后,內核就能通過 ioremap() 返回的虛擬地址,以 虛擬地址->mmu頁表映射-> 物理地址 的形式正確地訪問到物理地址了。
- ARM Linux 引入設備樹特性后,一些支持設備樹的設備驅動不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的內部仍然會調用 ioremap(),例如:
clk-rk3288.c (driversclkrockchip)
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 initn");
#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] = %lxn", i, gpio_regs[i]);
return 0;
}
module_init(ioremap_mod_init);
static void __exit ioremap_mod_exit(void)
{
printk(KERN_INFO "ioremap_mod exitn ");
#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)
[
3. ioremap() 的實現內幕
ioremap() 的實現內幕會涉及到比較多的內存管理的知識,這里我們拋開代碼細節簡單了解一下原理就好。
- ioremap() 將 vmalloc 區的某段虛擬內存塊映射到 io memory,其實現原理與vmalloc() 類似,都是通過在 vmalloc 區分配虛擬地址塊,然后修改內核頁表的方式將其映射到設備的 I/O 地址空間。
- 與 vmalloc() 不同的是,ioremap 并不需要通過伙伴系統去分配物理頁,因為ioremap 要映射的目標地址是 io memory,不是物理內存 (RAM)。
函數調用流程:
總結一下:
- 相關檢查;
- 分配一個 vm_struct 結構體,內核在管理虛擬內存中的 vmalloc 區時,內核必須跟蹤哪些子區域被使用、哪些是空閑的,對應的數據結構就是 vm_strcut。
- 初始化 vm_struct;
- 建立頁表;
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
芯片
+關注
關注
453文章
50417瀏覽量
421852 -
寄存器
+關注
關注
31文章
5318瀏覽量
120015 -
Linux
+關注
關注
87文章
11230瀏覽量
208931
發布評論請先 登錄
相關推薦
linux系統中內存分配基本原理分析
訪問外設的寄存器(一般包括數據寄存器、控制寄存器與狀態寄存器),需要在驅動初始化中將外設所處的物理地址映射為虛擬地址,linux為應對該問題
發表于 03-28 09:16
?731次閱讀
iTOP-4412開發板ioremap控制GPIO寄存器
GPIO的讀和寫以及其它任意功能。 需要的基礎知識虛擬地址和物理地址內存管理單元概念linux 驅動模塊的加載 主要內容GPIO 的寄存器文檔詳細介紹和說明函數 ioremap的用法
發表于 05-16 15:13
寄存器變量
C語言中使用關鍵字register來聲明局部變量為寄存器變量。寄存器變量的值會被存放在CPU的寄存器中,每當需要使用它們時,CPU就可以直接使用,而無須再通過控制器從內存中獲取。由于
發表于 06-03 10:13
?2340次閱讀
STM32的寄存器操作
STM32的寄存器操作在使用STM32單片機編程時一般都用ST給的庫函數編程,庫函數編程的底層就是對單片機寄存器的操作,庫函數就是一系列寄存器
發表于 01-13 15:43
?19次下載
Linux驅動操作寄存器
,第四個參數是映射的大小。 驅動中操作: #define?OFFSET??0x60?//某個寄存器的偏移地址 static?int?my_probe(struct?platform_device
如何在shell中操作寄存器
shell 中操作寄存器可以使用 devmem 命令. devmem 命令其實就是上述應用層操作寄存器生成的可執行文件,只不過busybox已經幫我們實現了。 devmem 命令格式:
Linux應用層操作寄存器
--- > [*] /dev/mem virtual device support Linux應用層操作寄存器 除了直接使用devmem,我們也可以在Linux應用層自己實現一個de
評論