《17.核心的移植1-從三星官方核心開始移植》
《17.核心的移植1-從三星官方核心開始移植》
第一部分、章節目錄
2.17.1.核心移植初體驗
2.17.2.初步移植以看到啟動資訊
2.17.3.核心中機器碼的確定
2.17.4.解決核心啟動中的錯誤
2.17.5.iNand的問題和安排
2.17.6.網絡卡驅動的移植和新增實驗
2.17.7.核心啟動第一階段的除錯方法
第二部分、章節介紹
2.17.1.核心移植初體驗
本節為核心移植的第一節,首先選定好待移植的原始碼,然後構建移植環境,並且進行基本的修改和配置編譯,再根據實驗現象進行後續步驟
2.17.2.初步移植以看到啟動資訊
本節課根據上節的實驗結果來分析問題並且嘗試解決問題,最終達到讓核心啟動資訊可以打印出來。
2.17.3.核心中機器碼的確定
本節講解核心中各mach-xx的機器碼是如何確定的,藉此分析找到我們的平臺對應的硬體初始化程式碼的位置。
2.17.4.解決核心啟動中的錯誤
本節課引入核心錯誤OOPS,然後分析如何從核心oops資訊中找到解決問題的線索並且解決啟動中的問題。
2.17.5.iNand的問題和安排
本節分析iNand的問題和不能掛載rootfs的原因,因為時間關係這個問題暫時沒能處理,要等到後面驅動部分開始時再去補上。
2.17.6.網絡卡驅動的移植和新增實驗
本節課講述網絡卡驅動的移植,主要目的是通過移植讓大家初步認識到linux中的驅動框架、驅動與資料相隔離的思想,核心驅動和uboot中驅動的相同點和不同點等。
2.17.7.核心啟動第一階段的除錯方法
本節補充講一下核心的一些除錯手法,主要目的是為大家提升下除錯技巧,提升學習動手能力。
第三部分、隨堂記錄
2.17.1.核心移植初體驗
2.17.1.1、三星官方移植版核心獲取
(1)從網盤下載原始碼包。
(2)這個檔案最初是來自於三星的SMDKV210開發板附帶的光碟資料
2.17.1.2、構建移植環境
(1)Windows下建立SI工程
(2)ubuntu下解壓
2.17.1.3、配置編譯下載嘗試
(1)檢查Makefile中ARCH和CROSS_COMPILE
(2)make xx_defconfig
(3)make menuconfig
(4)make -j4
預設情況下直接make則會直接單執行緒編譯。但是如果make -j4則會4執行緒編譯。
2.17.1.4、後續要做的事情:
(1)編譯得到的zImage去下載執行,看結果
(2)根據結果去分析問題原因,然後去嘗試解決這些問題。
2.17.2.初步移植以看到啟動資訊
2.17.2.1、分析問題
(1)根據執行結果,分析發現:linux核心的自解壓程式碼都沒有執行(因為沒有看到:Uncompressing Linux… done, booting the kernel.)
(2)說明zImage根本沒有被解壓成功,核心程式碼根本就沒有被執行,當然沒有輸出資訊了。所以問題出在解壓相關的部分。
(3)問題出在核心配置的解壓後代碼放置的記憶體地址處。
(4)核心配置的解壓地址應該等於連線地址,否則自解壓之後核心無法執行。現在問題變成:第一,核心的連線地址等於多少?第二,核心中配置的解壓地址是多少?
(5)這裡面還有個問題:核心的連線地址是一個虛擬地址,而自解壓程式碼解壓核心時需要實體地址,因此上面說的等於,其實是連線地址對應的實體地址等於自解壓地址。
(6)連線地址和他對應的實體地址在head.S中可以查到,分別是0xC0008000和0x30008000。那麼自解壓程式碼配置的解壓地址應該是30008000.
(7)自解壓程式碼對應的自解壓地址在mach/Makefile.boot檔案中。在其中修改,加入兩行:
override for SMDKV210
zreladdr-
(CONFIG_MACH_SMDKV210) := 0x30000100
(8)同步程式碼,並且編譯,得到的zImage複製到/tftpboot,然後重新下載執行檢視結果。
(9)結果就是:還是沒執行,但是有效果。自解壓程式碼解壓列印資訊已經出來了。但是核心還沒執行
2.17.2.2、問題分析
(1)定義的實體地址不對,從20000000改到30000000即可
2.17.3.核心中機器碼的確定
2.17.3.1、MACHINE_START巨集
(1)這個巨集用來定義一個機器碼的資料結構的。這個巨集的使用其實是用來定義一個結構體型別為machine_desc型別的結構體變數,名為__mach_desc_SMDKV210。這個結構體變數會被定義到一個特定段.arch.info.init,因此這個結構體變數將來會被連結器連結到這個.arch.info.init段中。
static const struct machine_desc __mach_desc_SMDKV210
__used
attribute((section(".arch.info.init"))) = {
.nr = MACH_TYPE_SMDKV210,
.name = “SMDKV210”,
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
.timer = &s5p_systimer,
};
(2)經過分析,發現一個mach-xxx.c檔案中定義了一個機器碼的開發板的machine_desc結構體變數,這個結構體變數放到.arch.info.init段中後,那麼就表示當前核心可以支援這個機器碼的開發板。
(3)落實到當前開發板和當前核心中來分析,當前我們移植的目標開發板使用S5PV210的CPU,開發板名字叫X210.我們在三星官方版本的核心中是找不到mach-x210.c的,所以我們又不想從零開始去移植,因此我們的思路是在三星移植的mach-s5pv210目錄下找一個mach-xx.c,這個開發板和我們的X210開發板最為接近,然後以此為基礎來移植。
(4)經過檢視,發現mach-s5pc110.c和mach-s5pv210.c和我們的X210開發板最為接近。我們一般確定的一個原則是:看我們的開發板和三星官方的哪個開發板最為相似。我們的X210開發板抄的是三星的SMDKV210,因此要找這個對應的那個檔案。
(5)結合mach-s5pv210目錄下的Makefile來分析,得知.config中定義了CONFIG_MACH_SMDKV210後,實際繫結的是mach-smdkc110.c這個檔案。所以實際上mach-smdkv210.c這個檔案根本沒用到。啟示就是不要光看名字。
2.17.3.2、硬體驅動的載入和初始化函式執行
(1).init_machine = smdkc110_machine_init,
(2)這個元素定義了一個機器硬體初始化函式,這個函式非常重要,這個函式中綁定了我們這個開發板linux核心啟動過程中會初始化的各種硬體的資訊。
2.17.4.解決核心啟動中的錯誤
2.17.4.1、認識核心啟動OOPS
(1)核心啟動後會有列印資訊,列印資訊中隱藏了問題所在。認真的去分析這個列印資訊,從中找到對的或者錯誤的一些資訊片段,才能幫助我們找到問題,從而解決問題。
(2)核心啟動中的錯誤資訊有一些特徵:
Unable to handle kernel NULL pointer dereference at virtual address 00000060
Internal error: Oops: 5 [#1] PREEMPT
PC is at dev_driver_string+0xc/0x44
LR is at max8698_pmic_probe+0x150/0x32c
(3)從以上錯誤資訊中的PC和LR的值可以看出,程式是執行到dev_driver_string或者max8698_pmic_probe(這兩個是函式或者彙編中的標號)符號部分的時候出錯了。我們就從這兩個符號出發去尋找、思考可能出錯的地方然後試圖去解決。
2.17.4.2、錯誤追溯及問題解決
(1)max8698_pmic_probe看名字是max8698這個電源管理IC的驅動安裝函式部分出錯了,應該是我們的開發板系統中配置了支援這個電源管理IC,於是乎啟動時去載入他的驅動,結果驅動在載入執行的過程中出錯了OOPS了。
(2)我們為什麼要配置支援這個驅動?這個驅動載入為什麼要出錯?
(3)結合我們X210開發板的硬體實際情況來分析:我們X210開發板上根本就沒有max8698這個電源管理IC,既然硬體都沒有驅動執行了肯定會出錯。
(4)回憶當時從三星版本的uboot移植的時候,在uboot的lowlevel_init.S中也有呼叫個電源管理IC初始化函式(PMIC_init),後來解決的辦法就是遮蔽掉了這個函式的呼叫,uboot就成功執行下去了。
(5)為什麼我們的uboot和核心中預設都呼叫了這個電源管理IC的初始化程式碼?原因就是三星的SMDKV210開發板中是用了max8698這個電源管理IC的,所以三星的uboot和kernel中都有預設支援這個。但是X210中是沒用的,因此都需要去掉。
(6)怎麼解決?在uboot中是直接改原始碼遮蔽掉那個初始化函式解決的;在核心中不能這麼幹?因為linux kernel是高度模組化高度可配置的,核心中每一個模組都是被配置項條件編譯了的,因此要去掉某個模組的支援,只需要重新配置去掉選項即可,不用改原始碼。所以我們的關鍵就是要找它對應的配置項。
(7)我們做法:make menuconfig,然後/搜尋"MAX8698"這幾個關鍵字,然後看到這個配置項的路徑,然後到路徑下去按N鍵去掉這個模組的支援,儲存,重新編譯即可。
(8)實踐證明問題被解決了,而且核心再次啟動後直接執行到掛載rootfs才出錯。
2.17.4.3、分析及總結
(1)分析:問題究竟是怎麼被解決的?涉及哪幾個方面
根本原因在於CONFIG_MFD_MAX8698這個配置巨集。這個配置巨集決定了很多東西
第一:這個配置巨集決定了drivers目錄下的max8698對應的驅動程式原始碼是否被編譯
第二:這個配置巨集決定了kernel啟動過程中是否會呼叫一些max8698的相關的程式碼
(2)總結:kernel是高度模組化和可配置化的,所以在核心中做任何事情(新增一個模組、更改一個模組、去掉一個模組)都必須按照核心設定的方案和流程來走。
2.17.5.iNand的問題和安排
2.17.5.1、錯誤分析
(1)得到的核心錯誤資訊:Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。從錯誤資訊字面意思來分析,就是核心試圖掛載根檔案系統時失敗,失敗的原因是unknown-block(不能識別的塊裝置)
(2)backstrace分析,可以得知錯誤資訊的來源,再結合之前的核心啟動流程分析,就更加確定了出錯的地方。
(3)下一個問題:分析這個錯誤出現的原因。unknown-block(0,0)。在kernel啟動時uboot會傳給核心一個cmdline,其中用root=xx來指定了rootfs在哪個裝置上,核心就會到相應的地方去掛載rootfs。譬如我們傳參中:root=/dev/mmcblk0p2,這裡的/dev/mmcblk0p2就是rootfs的裝置地址,這個裝置檔案編號的含義就是mmc裝置0的第2個分割槽(裝置0就是在SD0通道上的裝置,也就是iNand),這裡的問題就是沒找到mmc裝置0的第2分割槽。
(4)下一步問題:為什麼沒找到mmc裝置0的第2分割槽。一定是因為kernel啟動過程中載入mmc驅動的時候有問題,驅動沒有發現mmc裝置0.問題定位在MMC相關的驅動方面。
(5)對比九鼎版本的核心啟動資訊,即可發現我們的核心啟動並沒有找到MMC裝置(內建的iNand和外接的SD卡都沒找到),沒找到肯定是驅動的問題,這就要去移植MMC驅動了。
2.17.5.2、問題闡述
(1)SD/iNand本身都是由一個一個的扇區組成的,回憶裸機中講到的210的啟動時,BL1在SD卡的1扇區開始往後存放,SD卡的0扇區是不用的。SD卡的0扇區是用來放置MBR的。
(2)MBR就是用來描述塊裝置的分割槽資訊的,事先定義了一個通用的資料結構來描述塊裝置的分割槽,我們只要按照這個標準將分割槽資訊寫入MBR中即可對該裝置完成分割槽。MBR預設就是在塊裝置的第0個扇區上存放的。
(3)我們核心中讀到iNand分4個分割槽,我們哪裡分割槽的?uboot中有一個命令fdisk -c 0時就對iNand進行了分割槽。uboot的fdisk命令內部已經寫死了iNand的分割槽表,到核心中時核心直接讀取MBR就知道了分割槽。所以在uboot和核心之間iNand裝置的分割槽資訊是靠iNand自己傳遞的,所以uboot不用給核心傳參時傳遞分割槽表資訊。
(4)如果開發板用的是nandFlash的話,分割槽表一般是在核心中自己用程式碼構建的。所以nand版本的核心移植的時候一般都需要去移植更改nand分割槽表。
2.17.5.3、解決安排
(1)暫時解決不了這個問題。
2.17.5.4、後續課程安排
(1)一節課搞定網絡卡驅動的移植,一節課講述一些核心移植的小方法和技巧,然後課程結束
(2)整體移植的課程結束,進入根檔案系統部分。
2.17.6.網絡卡驅動的移植和新增實驗
2.17.6.1、移植標準
(1)網絡卡驅動移植ok時,啟動資訊為:
[ 1.452008] dm9000 Ethernet Driver, V1.31
[ 1.455870] eth0: dm9000c at e08f4300,e08f8304 IRQ 42 MAC: 00:09:c0:ff:ec:48 (platform data)
(2)當前核心中網絡卡驅動尚未移植,因此核心啟動時有錯誤的列印資訊:
[ 1.130308] dm9000 Ethernet Driver, V1.31
[ 1.133113] ERROR : resetting
[ 1.135700] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.140915] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.145941] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.150963] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.155992] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.161018] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.166041] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.171070] dm9000 dm9000.0: read wrong id 0x2b2a2928
[ 1.176092] dm9000 dm9000.0: wrong id: 0x2b2a2928
[ 1.180774] dm9000 dm9000.0: not found (-19).
(3)移植的目標就是讓我們的版本的核心可以打印出正確情況下的啟動資訊,那我們就相信核心啟動後網絡卡是可以工作的。
2.17.6.2、make menuconfig中新增DM9000支援
(1)menuconfig中選擇Y
(2)其實這一步本來就是Y,所以在我們這裡是不用管的。但是你自己遇到的一個核心可能預設不是Y,因此要設定。
2.17.6.3、mach-smdkc110.c中邏輯分析
(1)mach-smdkc110.c中的smdkc110_machine_init是整個開發板的所有硬體的初始化函式,在這裡載入了的硬體將來啟動時就會被初始化,在這裡沒有的將來啟動時就不管。
(2)smdkc110_devices和smdkc110_dm9000_set()這兩個地方是和DM9000有關的,要分別去做移植。
(3)smdkc110_dm9000_set這個函式就是DM9000相關的SROM bank的暫存器設定,相當於uboot中dm9000移植時的dm9000_pre_init函式。只是讀寫暫存器的函式名稱不同了。
2.17.6.4、修改相應的配置引數
(1)DM9000相關的資料配置在arch/arm/plat-s5p/devs.c中更改
(2)在arch/arm/mach-s5pv210/include/mach/map.h中定義了DM9000的IO基地址,和DM9000接在哪個bank有關。
(3)還有+2改成+4,IRQ_EINT9改成10即可。
2.17.6.5、程式碼實踐
(1)同步程式碼、編譯生成zImage
(2)下載啟動後看啟動資訊。
2.17.7.核心啟動第一階段的除錯方法
2.17.7.1、問題點描述
(1)核心啟動在head.S中首先進行了三個校驗(CPU id的校驗、機器碼的校驗、tag的校驗),然後建立頁表,然後做了一些不太會出錯的事情,然後b start_kernel。基本上能執行到start_kernel核心移植就不太會出問題了。
(2)有時候移植的核心啟動後的現象是:根本沒有啟動資訊出來。這時候有可能是核心啟動運行了但是執行出錯了沒啟動起來所以沒有列印資訊;也有可能是核心根本沒得以執行。都有可能但是沒法確定。我們希望能有一種除錯手段來確定問題所在。
2.17.7.2、除錯方法和原理
(1)除錯方法就是在核心啟動的第一階段新增彙編操作led點亮/熄滅的方法來標明程式碼執行的軌跡。
(2)我們找之前裸機中彙編操作led點亮/熄滅的程式碼過來,複製貼上到head.S中合適位置。然後核心啟動後根據led的表現來標明程式碼有無執行。
2.17.7.3、動手測試
(1)整理好led操作的程式碼段,在head.S中合適的地方新增led這個函式,然後在head.S的核心起始執行階段新增呼叫led函式,然後重新編譯核心,執行核心看這段程式碼有無被執行。
(2)如果被運行了,證明在這個呼叫led的步驟之前的部分都是沒問題的,那麼如果有錯肯定錯誤在後邊;如果沒有被執行則證明錯誤在之前,那麼就要去之前的部分debug。
2.17.7.4、實驗驗證