Linux操作寄存器前为什么要ioremap
1. 原因
這里只考慮有 MMU 的芯片,Linux 為了實(shí)現(xiàn)進(jìn)程虛擬地址空間,在啟用 MMU 后,在內(nèi)核中操作的都是虛擬地址,內(nèi)核訪問不到物理地址。
如果在驅(qū)動(dòng)里直接訪問物理地址,等于訪問了一個(gè)非法地址,會(huì)導(dǎo)致內(nèi)核崩潰,下面會(huì)有一個(gè)相關(guān)的小實(shí)驗(yàn)。
通過 ioremap 將物理地址映射為虛擬地址后,內(nèi)核就能通過 ioremap() 返回的虛擬地址,以 虛擬地址->mmu頁表映射-> 物理地址 的形式正確地訪問到物理地址了。
ARM Linux 引入設(shè)備樹特性后,一些支持設(shè)備樹的設(shè)備驅(qū)動(dòng)不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的內(nèi)部仍然會(huì)調(diào)用 ioremap(),例如:
2. ioremap() 實(shí)驗(yàn)
實(shí)驗(yàn)環(huán)境:
Linux-4.14 + Allwinner/H3。
實(shí)驗(yàn)代碼:
#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_IOREMAPgpio_regs?=?(volatile?unsigned?long?*)ioremap(H3_GPIO_BASE,?1024); #elsegpio_regs?=?(volatile?unsigned?long?*)H3_GPIO_BASE; #endiffor?(i=0;?i<3;?i++)printk(KERN_INFO?"reg[%d]?=?%lx\n",?i,?gpio_regs[i]);return?0; } module_init(ioremap_mod_init);static?void?__exit?ioremap_mod_exit(void) {printk(KERN_INFO?"ioremap_mod?exit\n?");#ifdef?USE_IOREMAPiounmap(gpio_regs); #endif? }module_exit(ioremap_mod_exit);MODULE_AUTHOR("es-hacker"); MODULE_LICENSE("GPL?v2");實(shí)驗(yàn)結(jié)果:
使用了 ioremap()
$?insmod?ioremap ioremap_mod?init reg[0]?=?71227722 reg[1]?=?33322177 reg[2]?=?773373未使用 ioremap():
$?insmod?ioremap_mod.koUnable?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?fault3. ioremap() 的實(shí)現(xiàn)內(nèi)幕
ioremap() 的實(shí)現(xiàn)內(nèi)幕會(huì)涉及到比較多的內(nèi)存管理的知識(shí),這里我們拋開代碼細(xì)節(jié)簡單了解一下原理就好。
ioremap() 將 vmalloc 區(qū)的某段虛擬內(nèi)存塊映射到 io memory,其實(shí)現(xiàn)原理與vmalloc() 類似,都是通過在 vmalloc 區(qū)分配虛擬地址塊,然后修改內(nèi)核頁表的方式將其映射到設(shè)備的 I/O 地址空間。
與 vmalloc() 不同的是,ioremap 并不需要通過伙伴系統(tǒng)去分配物理頁,因?yàn)閕oremap 要映射的目標(biāo)地址是 io memory,不是物理內(nèi)存 (RAM)。
函數(shù)調(diào)用流程:
點(diǎn)擊查看大圖總結(jié)一下:
相關(guān)檢查;
分配一個(gè) vm_struct 結(jié)構(gòu)體,內(nèi)核在管理虛擬內(nèi)存中的 vmalloc 區(qū)時(shí),內(nèi)核必須跟蹤哪些子區(qū)域被使用、哪些是空閑的,對應(yīng)的數(shù)據(jù)結(jié)構(gòu)就是 vm_strcut。
初始化 vm_struct;
建立頁表;
4. 相關(guān)參考
深入理解 Linux 內(nèi)核 / 8.3.2
深入 Linux 內(nèi)核架構(gòu) / 3.5.7
深入理解Linux設(shè)備驅(qū)動(dòng)程序內(nèi)核機(jī)制 / 3.5.3
https://blog.csdn.net/njuitjf/article/details/40745227
#推薦閱讀:
? ??專輯|Linux文章匯總
? ??專輯|程序人生
? ??專輯|C語言
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號(hào)?
總結(jié)
以上是生活随笔為你收集整理的Linux操作寄存器前为什么要ioremap的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL插入数据时让ID从指定值自增
- 下一篇: 【HarmonyOS】【Json解析】Z