kernel移植——从三星官方内核开始移植
以下內(nèi)容源于朱有鵬嵌入式課程的學(xué)習(xí),如有侵權(quán),請(qǐng)告知?jiǎng)h除。
一、內(nèi)核移植初體驗(yàn)
1、三星官方移植版內(nèi)核獲取
- 源碼包來于三星的SMDKV210開發(fā)板附帶的光盤資料,下載地址。
2、構(gòu)建移植環(huán)境
(1)Windows下建立SI工程;
- 建立前刪除不必要的目錄和文件:刪除arch目錄下非arm架構(gòu)的目錄文件;刪除arch/arm目錄下不是三星的mach-xxx、plat-xxx目錄。
(2)ubuntu下解壓;
3、配置、編譯
(1)檢查Makefile中ARCH =arm 和CROSS_COMPILE =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-;
(2)make xx_defconfig;(如何確定xx_defconfig?這里是smdkv210_android_defconfig)
- 在arch/arm/configs下有很多,找到三星相關(guān)的。這里找一個(gè)最像的:smdkv210_android_defconfig
(3)make menuconfig;(目前還不知道修整哪些)
(4)make -j4(直接make,則會(huì)直接單線程編譯;如果make -j4,則會(huì)4線程編譯)。
4、將得到的zImage下載運(yùn)行看結(jié)果
- 編譯完成后得到的內(nèi)核鏡像不在源碼樹的根目錄下,而是在arch/arm/boot這個(gè)目錄下。
- 通過tftp將鏡像下載到開發(fā)板,操作過程見博客http://blog.csdn.net/oqqhutu12345678/article/details/70537721。
(1)結(jié)果
- 只見到uboot打印出的下圖信息,看不到linux自解壓代碼打印出的“Uncompressing Linux... done, booting the kernel.”
(2)分析
- 說明zImage根本沒有被解壓成功,內(nèi)核代碼根本就沒有被運(yùn)行。因此問題出在解壓相關(guān)的部分。
- 問題出在解壓后代碼放置的內(nèi)存地址處(該地址可以由內(nèi)核配置的,如果不對(duì),則內(nèi)核不能運(yùn)行)。
- 內(nèi)核配置的解壓地址應(yīng)該等于連接地址,否則自解壓之后內(nèi)核無法運(yùn)行。
- 現(xiàn)在問題變成:第一,內(nèi)核的連接地址等于多少?第二,內(nèi)核中配置的解壓地址是多少?
- 這里面還有個(gè)問題:內(nèi)核的連接地址是一個(gè)虛擬地址,而自解壓代碼解壓內(nèi)核時(shí)需要物理地址(此時(shí)mmu還沒開,因?yàn)閮?nèi)核還沒有開始運(yùn)行),因此連接地址對(duì)應(yīng)的物理地址等于自解壓地址。
- 連接地址和對(duì)應(yīng)的物理地址在head.S中可以查到,經(jīng)分析(在.config中查宏TEXT_OFFSET,合成)得知分別是0xC0008000和0x30008000,那么自解壓代碼配置的解壓地址應(yīng)該是30008000。
(3)修改操作
- 自解壓代碼對(duì)應(yīng)的自解壓地址在mach-s5pv210/Makefile.boot文件中。
- 在此文件后面追加入下面內(nèi)容。
- (實(shí)踐中這樣好像并沒有預(yù)期的結(jié)果。因此我直接賦值不使用宏。問題是,為什么這里的宏不起作用?因?yàn)槲覍戝e(cuò)了,尷尬……)
(4)同步、編譯,重新下載運(yùn)行查看結(jié)果
- 因?yàn)橹恍薷牧艘恍╂溄訁?shù),不需要重新配置編譯,所以make很快就完成了。
- 自解壓代碼解壓打印信息已經(jīng)出來;
- 內(nèi)核還是沒有運(yùn)行;
(5)問題分析
- 因?yàn)槲覀冊(cè)趗boot中配置的空間為3000 0000開始(而不是2000 0000開始),因此這里定義的物理地址不對(duì),從20000000改到30000000即可。
(6)重新編譯,運(yùn)行結(jié)果:內(nèi)核打印出很多運(yùn)行信息。
二、內(nèi)核中機(jī)器碼的確定(知識(shí)課程)
內(nèi)核支持什么架構(gòu)、支持哪款cpu是怎么確定的。內(nèi)核中保存了一份機(jī)器碼,會(huì)和uboot傳過來的機(jī)器碼比對(duì)(在head.s中比對(duì)uboot傳過來的機(jī)器碼),比對(duì)成功說明uboot和內(nèi)核是匹配的。 之前講過uboot如何給內(nèi)核傳機(jī)器碼。本課講內(nèi)核的機(jī)器碼是怎么確定的,怎么來的。
每個(gè)mach-xx文件夾代表一種cpu,其內(nèi)可能包含cpu相同的不同開發(fā)板。比如mach-s5pv210文件夾內(nèi)有mach-smdkc110.c和mach-smdkv210.c等開發(fā)板。
1、MACHINE_START宏
- 這個(gè)宏用來定義一個(gè)機(jī)器碼的數(shù)據(jù)結(jié)構(gòu)。
- 即用來定義一個(gè)結(jié)構(gòu)體類型為machine_desc類型的結(jié)構(gòu)體變量,名為__mach_desc_SMDKV210。
- 這個(gè)結(jié)構(gòu)體變量會(huì)被定義到一個(gè)特定段.arch.info.init,因此這個(gè)結(jié)構(gòu)體變量將來會(huì)被鏈接器鏈接到這個(gè).arch.info.init段中。
比如?MACHINE_START(SMDKV210, "SMDKV210")解釋為下面內(nèi)容。
static const struct machine_desc __mach_desc_SMDKV210 \__used \__attribute__((__section__(".arch.info.init"))) = { \.nr = MACH_TYPE_SMDKV210,//機(jī)器碼2456 \.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, };(1)mach-xxx.c文件定義了一個(gè)機(jī)器碼的開發(fā)板的machine_desc結(jié)構(gòu)體變量(一個(gè)機(jī)器碼對(duì)應(yīng)一個(gè)開發(fā)板,一個(gè)cpu可以對(duì)應(yīng)多個(gè)開發(fā)板)
- 這個(gè)結(jié)構(gòu)體變量放到.arch.info.init段中后,表示當(dāng)前內(nèi)核可以支持這個(gè)機(jī)器碼的開發(fā)板。
(2)我們所移植的目標(biāo)開發(fā)板使用S5PV210的CPU,開發(fā)板名字叫X210。
- 但在三星官方版本的內(nèi)核中找不到mach-x210.c;
- 但不想從零開始移植,因此在三星移植的mach-s5pv210目錄下找一個(gè)mach-xx.c,使得該開發(fā)板和X210開發(fā)板最為接近(同一款cpu),然后以此為基礎(chǔ)來移植。
(3)經(jīng)過查看,發(fā)現(xiàn)mach-s5pc110.c和mach-s5pv210.c與X210開發(fā)板最為接近。
- 尋找原則:開發(fā)板和三星官方的哪個(gè)開發(fā)板最為相似。
- X210開發(fā)板抄的是三星的SMDKV210,因此要找SMDKV210對(duì)應(yīng)的文件(按理應(yīng)該是mach-smdkv210.c,但實(shí)際是嗎?)
(4)結(jié)合mach-s5pv210目錄下的Makefile來分析,得知.config中定義CONFIG_MACH_SMDKV210后,實(shí)際綁定的是mach-smdkc110.c這個(gè)文件。
- 因此實(shí)際上mach-smdkv210.c這個(gè)文件根本沒用到。
2、硬件驅(qū)動(dòng)的加載和初始化函數(shù)執(zhí)行
.init_machine = smdkc110_machine_init,
- 定義了一個(gè)機(jī)器硬件初始化函數(shù);
- 這個(gè)函數(shù)非常重要,這個(gè)函數(shù)中綁定了開發(fā)板linux內(nèi)核啟動(dòng)過程中會(huì)初始化的各種硬件的信息。
- 在此函數(shù)中添加的才會(huì)被初始化,否則不會(huì)被初始化。
- 這篇視頻末尾的知識(shí),可以強(qiáng)化內(nèi)核分析的學(xué)習(xí)。
三、解決內(nèi)核啟動(dòng)中的錯(cuò)誤
1、認(rèn)識(shí)內(nèi)核啟動(dòng)OOPS(內(nèi)核死亡信息)
(1)內(nèi)核啟動(dòng)后會(huì)有打印信息,打印信息中隱藏了問題所在。
- 認(rèn)真的去分析這個(gè)打印信息,從中找到對(duì)的或者錯(cuò)誤的一些信息片段,才能幫助我們找到問題,從而解決問題。
(2)內(nèi)核啟動(dòng)中的錯(cuò)誤信息的特征
- 由PC和LR的值可以看出,程序執(zhí)行到dev_driver_string或者max8698_pmic_probe(這兩個(gè)是函數(shù)或者匯編中的標(biāo)號(hào))符號(hào)部分的時(shí)候出錯(cuò)了。
- 我們從這兩個(gè)符號(hào)出發(fā)去尋找、思考可能出錯(cuò)的地方然后試圖去解決。
2、錯(cuò)誤追溯及問題解決
(1)max8698_pmic_probe
- max8698這個(gè)電源管理IC的驅(qū)動(dòng)安裝函數(shù)部分出錯(cuò);
- 開發(fā)板系統(tǒng)中配置了支持這個(gè)電源管理IC,于是啟動(dòng)時(shí)去加載它的驅(qū)動(dòng),結(jié)果驅(qū)動(dòng)在加載執(zhí)行的過程中出錯(cuò)。
(2)這個(gè)驅(qū)動(dòng)加載時(shí)為什么會(huì)出錯(cuò)?
- 結(jié)合X210開發(fā)板的硬件實(shí)際情況來分析:X210開發(fā)板上根本就沒有max8698這個(gè)電源管理IC,既然硬件都沒有,執(zhí)行驅(qū)動(dòng)肯定會(huì)出錯(cuò)。
- 移植三星版本的uboot時(shí),在uboot的lowlevel_init.S中也有調(diào)用電源管理IC初始化函數(shù)(PMIC_init),結(jié)果會(huì)報(bào)錯(cuò),屏蔽掉該函數(shù)的調(diào)用,uboot就可以成功運(yùn)行下去。
(5)為什么uboot和內(nèi)核中,都默認(rèn)調(diào)用這個(gè)電源管理IC的初始化代碼?
- 因?yàn)槿堑腟MDKV210開發(fā)板中用了max8698這個(gè)電源管理IC,因此三星的uboot和kernel中都默認(rèn)支持這個(gè)。
- 但是X210中是沒用的,因此uboot和內(nèi)核中都需要去掉該代碼模塊。
(6)怎么解決?
- 在uboot中,是直接改源代碼,即屏蔽掉那個(gè)初始化函數(shù)解決的;
- 在kernel中,不能直接修改源代碼。
- 因?yàn)閘inux kernel是高度模塊化高度可配置的,內(nèi)核中每一個(gè)模塊都被配置項(xiàng)條件編譯;
- 因此要去掉對(duì)某個(gè)模塊的支持,需要重新配置,配置時(shí)去掉選項(xiàng)即可,不用改源代碼。
- 因此關(guān)鍵就是要找它對(duì)應(yīng)的配置項(xiàng)。
(7)操作
- 先make menuconfig;
- 然后/搜索"MAX8698"這幾個(gè)關(guān)鍵字,然后看到這個(gè)配置項(xiàng)的路徑,然后到路徑下去按N鍵去掉這個(gè)模塊的支持,保存,重新編譯即可。
(8)重新編譯、下載運(yùn)行
- 此問題被解決了;
- 內(nèi)核再次啟動(dòng)后直接運(yùn)行到掛載rootfs才出錯(cuò)。
3、分析及總結(jié)
分析:根本原因在于CONFIG_MFD_MAX8698這個(gè)配置宏。這個(gè)配置宏決定了很多東西
- 第一:這個(gè)配置宏決定了drivers目錄下的max8698對(duì)應(yīng)的驅(qū)動(dòng)程序源代碼是否被編譯;
- 第二:這個(gè)配置宏決定了kernel啟動(dòng)過程中是否會(huì)調(diào)用一些max8698的相關(guān)的代碼;
總結(jié):kernel是高度模塊化和可配置化的,所以在內(nèi)核中做任何事情(添加一個(gè)模塊、更改一個(gè)模塊、去掉一個(gè)模塊)都必須按照內(nèi)核設(shè)定的方案和流程。
四、iNand的問題和安排
1、錯(cuò)誤分析
(1)內(nèi)核錯(cuò)誤信息:Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。
- 內(nèi)核試圖掛載根文件系統(tǒng)時(shí)失敗,失敗的原因是unknown-block(不能識(shí)別的塊設(shè)備)
(2)backstrace分析,可以得知錯(cuò)誤信息的來源,再結(jié)合之前的內(nèi)核啟動(dòng)流程分析,就更加確定出錯(cuò)的地方。
(3)為什么unknown-block(0,0)?
- 在kernel啟動(dòng)時(shí),uboot給內(nèi)核傳遞一個(gè)cmdline。
- 其中root=xx指定rootfs在哪個(gè)設(shè)備上,內(nèi)核會(huì)到相應(yīng)的地方去掛載rootfs。
- 譬如root=/dev/mmcblk0p2,這里的/dev/mmcblk0p2就是rootfs所在的設(shè)備的地址。
- 它表示mmc設(shè)備0的第2個(gè)分區(qū)(設(shè)備0,即在SD0通道上的設(shè)備,即iNand)。
- 這里的問題就是沒找到mmc設(shè)備0的第2分區(qū)。
(4)為什么沒找到mmc設(shè)備0的第2分區(qū)?
- 一定是因?yàn)閗ernel啟動(dòng)過程中加載mmc驅(qū)動(dòng)的時(shí)候有問題,驅(qū)動(dòng)沒有發(fā)現(xiàn)mmc設(shè)備0。
- 問題定位在MMC相關(guān)的驅(qū)動(dòng)方面。
(5)對(duì)比九鼎版本的內(nèi)核啟動(dòng)信息
下圖是九鼎版本的內(nèi)核啟動(dòng)信息部分截圖
- 可發(fā)現(xiàn)待移植的內(nèi)核啟動(dòng)時(shí),沒有找到MMC設(shè)備(內(nèi)置的iNand和外置的SD卡都沒找到);
- 沒找到肯定是驅(qū)動(dòng)的問題,因此要移植MMC驅(qū)動(dòng)。
2、知識(shí)補(bǔ)充
(1)SD/iNand都是由一個(gè)一個(gè)的扇區(qū)組成的
- BL1從SD卡的1扇區(qū)開始往后存放;
- SD卡的0扇區(qū)是不用的,SD卡的0扇區(qū)用來放置MBR(主引導(dǎo)記錄)。
(2)MBR用來描述塊設(shè)備的分區(qū)信息
- 事先定義了一個(gè)通用的數(shù)據(jù)結(jié)構(gòu)來描述塊設(shè)備的分區(qū);
- 只要將分區(qū)信息寫入MBR中,即可完成對(duì)該設(shè)備的分區(qū);
- MBR默認(rèn)存放在塊設(shè)備的第0個(gè)扇區(qū)。
(3)內(nèi)核如何知道iNand分了4個(gè)分區(qū)?哪里對(duì)inand進(jìn)行了分區(qū)?
- uboot中有一個(gè)命令fdisk,fdisk -c 0時(shí),對(duì)iNand進(jìn)行分區(qū);
- fdisk命令對(duì)iNand的分區(qū)已經(jīng)寫死,內(nèi)核通過讀取MBR,就可以知道分區(qū)信息了;
- 由iNand本身通過MBR給內(nèi)核傳遞分區(qū)信息,因此uboot給內(nèi)核傳參時(shí),不用傳遞分區(qū)表信息。
(4)如果開發(fā)板使用的是nandFlash,分區(qū)表一般是在內(nèi)核中用代碼構(gòu)建的。
- 因此nand版本的內(nèi)核移植時(shí),一般都需要移植、更改nand分區(qū)表。
3、解決安排
- 暫時(shí)解決不了這個(gè)問題……
五、網(wǎng)卡驅(qū)動(dòng)的移植和添加實(shí)驗(yàn)
1、移植標(biāo)準(zhǔn)
(1)網(wǎng)卡驅(qū)動(dòng)移植ok時(shí),啟動(dòng)信息為
[ 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)當(dāng)前內(nèi)核中網(wǎng)卡驅(qū)動(dòng)尚未移植,因此內(nèi)核啟動(dòng)時(shí)有錯(cuò)誤的打印信息: [ 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)移植的目標(biāo)
- 讓此版本的內(nèi)核可以打印出正確情況下的啟動(dòng)信息。
2、make menuconfig中添加DM9000支持
- 搜索/DM9000,找到所在路徑。
- menuconfig中選擇Y。
3、mach-smdkc110.c中邏輯分析
(1)mach-smdkc110.c中的smdkc110_machine_init是整個(gè)開發(fā)板的所有硬件的初始化函數(shù)
- 在這里被加載的硬件,在將來啟動(dòng)時(shí)就會(huì)被初始化;
- 在這里沒被加載的硬件,在將來啟動(dòng)時(shí)就不管。
- smdkc110_dm9000_set(),是和DM9000相關(guān)的SROM bank的寄存器設(shè)置;
- 其相當(dāng)于uboot中dm9000移植時(shí)的dm9000_pre_init函數(shù),只是讀寫寄存器的函數(shù)名稱不同了。
- smdkc110_dm9000_set()函數(shù)的更改,直接拷貝九鼎的移植好的。
4、修改相應(yīng)的配置參數(shù)
(1)DM9000相關(guān)的數(shù)據(jù)配置在arch/arm/plat-s5p/devs.c中;
(2)在arch/arm/mach-s5pv210/include/mach/map.h中定義了DM9000的IO基地址,和DM9000接在哪個(gè)bank有關(guān)。
- 根據(jù)實(shí)際情況,將其改為0x8800 0300。
(3)+2改成+4
?截圖是x210的配置,可見里面是+4,因此下面要改成+4
(4)IRQ_EINT9改成10即可
5、同步、編譯、下載,查看啟動(dòng)信息
六、內(nèi)核啟動(dòng)第一階段(匯編階段)的調(diào)試方法
1、調(diào)試的原因
(1)內(nèi)核啟動(dòng)在head.S中首先進(jìn)行三個(gè)校驗(yàn)(CPU id的校驗(yàn)、機(jī)器碼的校驗(yàn)、tag的校驗(yàn)),然后創(chuàng)建頁表,然后做了一些不太會(huì)出錯(cuò)的事情,然后b start_kernel。
- 基本上能運(yùn)行到start_kernel,內(nèi)核移植就不太會(huì)出問題了。
(2)有時(shí)候移植的內(nèi)核啟動(dòng)后的現(xiàn)象是:根本沒有啟動(dòng)信息出來(下面所述主要針對(duì)這個(gè)問題)。
- 有可能是內(nèi)核啟動(dòng)運(yùn)行了,但是運(yùn)行出錯(cuò)了沒啟動(dòng)起來所以沒有打印信息;
- 有可能是內(nèi)核根本沒運(yùn)行;
- 希望能有一種調(diào)試手段來確定問題所在。
2、調(diào)試方法和原理
(1)調(diào)試方法:在內(nèi)核啟動(dòng)的第一階段,添加匯編操作led點(diǎn)亮/熄滅的方法來標(biāo)明代碼運(yùn)行的軌跡。
(2)將led點(diǎn)亮和熄滅的代碼,復(fù)制粘貼到head.S中合適位置,然后內(nèi)核啟動(dòng)后根據(jù)led的表現(xiàn)來標(biāo)明代碼有無運(yùn)行。
3、動(dòng)手測試
(1)在head.S中合適的地方(比如函數(shù)集中域處)添加(定義)led這個(gè)函數(shù),然后在head.S的內(nèi)核起始運(yùn)行階段添加調(diào)用led函數(shù)。然后重新編譯內(nèi)核,運(yùn)行內(nèi)核看這段代碼有無被運(yùn)行。
(3)分析思路
- 如果被運(yùn)行,證明在調(diào)用led的步驟之前的部分都是沒問題的,如果有錯(cuò),錯(cuò)誤肯定在后邊;
- 如果沒有被運(yùn)行則證明錯(cuò)誤在之前,那么就要去之前的部分debug。
4、典型led函數(shù)
//移植內(nèi)核的led調(diào)試方法 led:// 第一步:把0x11111111寫入0xE0200240(GPJ0CON)位置ldr r3, =0x11111111 // 從后面的=可以看出用的是ldr偽指令,因?yàn)樾枰幾g器來判斷這個(gè)數(shù)ldr r4, =0xE0200240 // 是合法立即數(shù)還是非法立即數(shù)。一般寫代碼都用ldr偽指令str r3, [r4] // 寄存器間接尋址。功能是把r0中的數(shù)寫入到r1中的數(shù)為地址的內(nèi)存中去// 第二步:把0xff寫入0xE0200244(GPJ0DAT)位置ldr r3, =0xff //開發(fā)板啟動(dòng)時(shí),led半亮,這里賦值為ff,則三顆全滅了,一顆半亮。ldr r4, =0xE0200244str r3, [r4] // 把ff寫入到GPJ0DAT寄存器中,引腳即輸出高電平,LED熄滅mov pc,lr //函數(shù)一定要返回 //r0,r1,r2在head.s中,是用來給內(nèi)核傳參的,類似于全局變量。 //因此不能用這些寄存器,我們可以使用那些沒有被使用的寄存器。 //因此可以r0改為r3,r1改為r4總結(jié)
以上是生活随笔為你收集整理的kernel移植——从三星官方内核开始移植的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 仓库管理数据库系统设计
- 下一篇: Apizza在线接口调试文档工具如何方便