背景
之前做過一次uboot的升級,當(dāng)時留下了一些記錄,本文摘錄其中比較有意思的兩個問題。
啟動失敗問題
問題簡述
uboot代碼中用到了一個庫,考慮到庫本身跟uboot版本沒什么關(guān)系,就直接把舊的庫文件拷貝過來使用。結(jié)果編譯鏈接是沒問題,啟動卻會卡住。
消失的打印
為了明確卡住的位置,就去修改了庫的源碼,添加一些打印(此時還是在舊版本uboot下編譯的),結(jié)果發(fā)現(xiàn)卡住的位置或隨著添加打印的變化而變化,且有些打印語句,添加后未打印出來。 我決定先從這些神秘消失的打印入手。 分析下uboot中的printf實現(xiàn),最底層就是寫寄存器,是一個同步的函數(shù),也沒什么可疑的地方。 為了確認(rèn)打印不出來的時候,到底有沒有調(diào)用到printf,我決定給printf增加一個計數(shù)器,在gd結(jié)構(gòu)體中,增加一個printf_count字段,初始化為0,每次打印時執(zhí)行printf_count++并打印出值。 設(shè)計這個試驗,本意是確認(rèn)未打印出來時是否確實也調(diào)用到了printf,但卻有了別的發(fā)現(xiàn),實驗結(jié)果中printf_count值會異常變化,不是按打印順序遞增,而是會突變成很大的異常值。 printf_count是gd結(jié)構(gòu)體的成員,那就是gd的問題了。進(jìn)一步將uboot全局結(jié)構(gòu)體gd的地址打印出來。確認(rèn)了原因是gd結(jié)構(gòu)體的指針變化了。 這也可以解釋部分打印消失的現(xiàn)象,原因是我們在gd中有另一個字段,用于控制打印等級。當(dāng)gd被改動了,printf就可能解析出錯,誤以為打印等級為0而提前返回。
gd的實現(xiàn)
那么好端端的,gd為什么會被改了呢?這就要先看看gd到底是怎么實現(xiàn)的了。 uboot中維護了一個全局的結(jié)構(gòu)體gd。在代碼中加入
DECLARE_GLOBAL_DATA_PTR; 即可使用gd指針訪問這個全局結(jié)構(gòu)體,許多地方都會借助gd來保存?zhèn)鬟f信息。 進(jìn)一步看看這個宏的定義舊版本uboot: #defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8") 新版本uboot: #defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r9") 居然不一樣,一個是將gd的值放到r8寄存器,一個是放在r9寄存器。 那么就可以猜測到,庫是在舊版本uboot中編譯出來的,可能使用了r9,那么放到新版本uboot中去,就會破壞r9寄存器中保存的gd值,導(dǎo)致一系列依賴gd的代碼不能正常工作。
驗證改動
為了求證,將庫反匯編出來,發(fā)現(xiàn)確實避開了r8寄存器,但使用了r9寄存器。 說明uboot在指定gd寄存器的同時,還有某種方法讓其他代碼不使用這個寄存器。 那是不是把舊uboot中的這個r8改成r9,重新編譯庫就可以了呢?試一下,還是不行。 那么禁止其他代碼使用r8寄存器肯定就是通過別的方式實現(xiàn)的了。簡單粗暴地在舊版本uboot下搜索r8,去掉.c .h等類型后,很容易發(fā)現(xiàn)了
./arch/arm/cpu/armv7/config.mkPLATFORM_RELFLAGS+=-fno-common-ffixed-r8-msoft-floa 將-ffixed-r8修改為-ffixed-r9,重新編譯出庫,這回就可以正常工作了,打印正常,啟動正常。反匯編出來也可以看到,新編譯出來的庫用了r8沒有用r9。 當(dāng)然更好的改法,是直接在新版本的uboot中編譯,這是最可靠的。
追本溯源
話說回來,為什么兩個版本的uboot,會使用不同的寄存器呢?難道有什么坑? 這就得去翻一下git記錄了。
commitfe1378a961e508b31b1f29a2bb08ba1dac063155 Author:JeroenHofstee
啟動慢問題
問題簡述
填了幾個坑之后,新的uboot可以啟動到內(nèi)核了,但發(fā)現(xiàn)啟動速度非常慢,內(nèi)核啟動速度慢了接近10倍!明明是同一個內(nèi)核,為什么差異這么大。
排查寄存器
初步排查了下設(shè)備樹配置,以及uboot跳轉(zhuǎn)內(nèi)核前的一些關(guān)鍵寄存器,確實在兩個版本的uboot中有所不同,但具體去看這些不同,發(fā)現(xiàn)都不會影響速度,將一些驅(qū)動對齊之后寄存器差異基本就消失了。
差異的分界
那再細(xì)看,kernel的速度有差異,uboot呢?在哪個時間點之后,速度開始產(chǎn)生差異? 嘗試在兩個版本的uboot中插入一些操作,對比時間戳,發(fā)現(xiàn)兩個uboot在某個節(jié)點之后的速度確實有區(qū)別。 進(jìn)一步排查,原來是在打開cache操作之后,舊uboot的速度就會比新uboot快。嘗試將舊uboot的cache關(guān)掉,則二者基本一致。嘗試將舊uboot操作cache的代碼,移植到新uboot,未發(fā)生改變。 此時可確認(rèn)新uboot的開cache有問題。但覺得這個跟kernel啟動慢沒關(guān)系。因為uboot進(jìn)入kernel之前都會關(guān)cache,由kernel自己去重新打開。 也就是不管是用哪份uboot,也不管uboot中是否開了cache,對kernel階段都應(yīng)該沒有影響才對。 于是記錄下來uboot的這個問題,待后續(xù)修復(fù)。先繼續(xù)找kernel啟動慢的原因。(注:現(xiàn)在看來當(dāng)時的做法是有問題的,這里的異常這么明顯,應(yīng)該設(shè)法追蹤下去找出原因才對)
鎖定uboot
uboot的嫌疑非常大,但還不能完全確認(rèn),因為uboot之前還有一級spl。是否會是spl的問題呢? 嘗試改用新spl+舊uboot,啟動速度正常。而新spl+新uboot的啟動速度則很慢,其他因素都不變,說明問題確實出在uboot階段。
多做or少做
當(dāng)時到這一步就卡住了,直接比較兩份uboot的代碼不太現(xiàn)實,差異太大了。 后來我就給自己提了個問題,到底新uboot是多做了某件事情,還是少做了某件事情? 換個說法,目前已知
spl-->舊uboot-->kernel(速度快) spl-->新uboot-->kernel(速度快) 但到底是以下的情況A還是情況B呢?A:spl(速度慢)-->舊uboot(做了某個會提升速度的操作)-->kernel(速度快) spl(速度慢)-->新uboot(少做了某個會提升速度的操作)-->kernel(速度慢) B:spl(速度快)-->舊uboot(沒做特殊操作)-->kernel(速度快) spl(速度快)-->新uboot(多做了某個會限制速度的操作)-->kernel(速度慢) 為了驗證,我決定讓spl直接啟動內(nèi)核,看看內(nèi)核到底是快是慢。 支持過程碰到了一些小問題 1.spl沒有能力加載這么大的kernel 解決:此時不需要kernel能完全啟動,只需要能加載啟動一段,足以體現(xiàn)出啟動速度是否正常即可,于是裁剪出一個非常小kernel來輔助實驗。 2.kernel需要dtb 解決:內(nèi)核有一個CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE選項。選上重新編譯。編譯后再用dd將kernel和dtb拼接到一起,作為新的kernel。這樣,spl就只需要加載一個文件并跳轉(zhuǎn)過去即可。 試驗結(jié)果,spl啟動的kernel和使用新uboot啟動的kernel速度一致,均比舊uboot啟動的kernel慢。 說明,舊uboot中做了某個關(guān)鍵操作,而新uboot沒做。
找出關(guān)鍵操作
那接下來的任務(wù)就是,找出舊uboot中的這個關(guān)鍵操作了。 怎么找呢?有了上一步的成果,我們可以使用以下方法來排查
spl加載kernel和舊uboot
spl跳轉(zhuǎn)到舊uboot,此時kernel其實已經(jīng)在dram中準(zhǔn)備好了,隨時可以啟動
在舊uboot的啟動流程各個階段,嘗試直接跳轉(zhuǎn)到kernel,觀察啟動速度
如果在舊uboot的A點跳轉(zhuǎn)kernel啟動慢,B點跳轉(zhuǎn)啟動快,則說明關(guān)鍵操作位于AB點之間。
方法有了,很快就鎖定到start.S,進(jìn)一步在start.S中揪出了這段代碼
#ifdefined(CONFIG_ARM_A7) @setSMPbit mrcp15,0,r0,c1,c0,1 orrr0,r0,#(1<<6) ????mcr????????p15,?0,?r0,?c1,?c0,?1 #endif 新uboot的start.S中沒有這段代碼,嘗試在新uboot的start.S中添加此操作,速度立馬恢復(fù)正常了。 再全局搜索下,原來這個新版本uboot中,套路是在board_init中進(jìn)行此項設(shè)置的,而這個平臺從舊版本移植過來,就沒有設(shè)置 SMP bit, 補上即可。
SMP bit是什么
SMP 是指對稱多處理器,看起來這個 bit 會影響多核的 cache一致性,此處沒有再深入研究。 但可以知道,對于單處理器的情況,也需要設(shè)置這個bit才能正常使用cache。 貼下arm的圖和描述:
[6]SMP SignalsiftheCortex-A9processoristakingpartincoherencyornot. Inuniprocessorconfigurations,ifthisbitisset,thenInnerCacheableSharedistreatedasCacheable.Theresetvalueiszero. 搜下kernel的代碼,發(fā)現(xiàn)也是有地方調(diào)用了的。不過這個芯片是單核的,根本就沒配置CONFIG_SMP。#ifdefCONFIG_SMP ALT_SMP(mrcp15,0,r0,c1,c0,1) ALT_UP(movr0,#(1<6))??@?fake?it?for?UP ?tst?r0,?#(1?<6)???@?SMP/nAMP?mode?enabled? ?orreq?r0,?r0,?#(1?<6)??@?Enable?SMP/nAMP?mode ?orreq?r0,?r0,?r10???@?Enable?CPU-specific?SMP?bits ?mcreq?p15,?0,?r0,?c1,?c0,?1 #endif
總結(jié)
整理出來一方面是記錄這兩個bug,另一方面也是想記錄下當(dāng)時的一些操作。 畢竟同樣的bug可能以后都不會碰到了,但解bug的方法和思路卻是可以積累復(fù)用的。
-
編譯
+關(guān)注
關(guān)注
0文章
679瀏覽量
33969 -
Uboot
+關(guān)注
關(guān)注
4文章
129瀏覽量
29079
原文標(biāo)題:坑!uboot升級過程遇到的兩個bug
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
評論