1. 程式人生 > >U-boot各種配置 和U-boot原始碼最為詳細的講解

U-boot各種配置 和U-boot原始碼最為詳細的講解

今天開始移植三星原廠的U-boot,做一下筆記,以備日後所需 
移植的時候有一點感想,就是最好別註釋掉不對的原始碼,定義的地方千萬別動,儘量修改呼叫的地方,這樣可以極大的避免出錯;此外加上自己程式碼的時候應該後面用特殊的註釋標註一下,這樣通過編輯器搜尋就很容易定位自己改過的地方

0. 取得原始碼

  • 獲得原廠的U-boot原始碼後,需要在Linux環境下解壓,因為原始碼裡面存在著符號連結….在原始碼根目錄tools資料夾內有一堆符號連結,比如crc32.c等,我嘗試了make distclean,發現並沒有什麼卵用…一堆符號連結依然存在
  • 之後嘗試直接手動刪除,再編譯,沒有出任何問題,並且crc32.c等又重新出現了。上述現象證明這堆符號連結其實是一些編譯出來的中間檔案,並不屬於原始碼。之所以在原始碼中出現,很可能是因為三星工程師的make distclean沒寫好,導致原始碼內有符號連結殘留
  • 既然符號連結不屬於原始碼,那麼我們就能大大方方的把符號連結刪掉,然後將這純正的原始碼傳到windows中,並建立工程了

1. 先燒錄看現象

先燒到板子裡試試看,看看有什麼現象,再做分析

  • 解壓原始碼後,先看看Makefile中的交叉編譯工具鏈有沒有問題。根據 U-boot配置及編譯階段流程巨集觀分析可知,交叉編譯工具鏈配置在主Makefile的前端。檢視後發現裡面的工具鏈和我裝的一樣,沒有問題
  • 正式開始編譯之前需要配置,那麼Makefile中關於配置的偽目標是什麼呢?根據 U-boot配置及編譯階段流程巨集觀分析可知,偽目標在主Makefile的非常後面,而且名字應該是開發板的名字。但是問題來了,三星原廠的評估板叫smdkv210,但是Makefile後面的偽目標有5個名字差不多的,這樣的話,相應的.h檔案也有5個…..怎麼辦? 
    這裡寫圖片描述
     
    先不管了,選第一個吧,第一個名字看上去最正常,那以後標頭檔案就要選smdkv210single.h了….然後開始配置階段了,輸入make smdkv210single_config
  • 配置完之後直接make,發現並沒有報錯,編譯一切順利
  • 燒錄前,先看看燒錄指令碼有沒有錯,開啟sd_fusing目錄下的sd_fusing.sh,重點關注 
    這裡寫圖片描述這兩行,確定BL1和U-boot整體的燒錄扇區無誤,其中,U-boot的燒錄扇區由smdkv210single.h中的巨集MOVI_BL2_POS指定。然後保險起見,防止原廠編譯出來的sd_fdisk不能用,先在sd_fusing目錄make clean一下,再make,得到全新的sd_fdisk。最後執行 ./sd_fusing.sh /dev/sdb 燒錄一切順利
  • 插入SD卡,開機,發現 
    這裡寫圖片描述,此外,開發板供電鎖存成功了
  • 根據現象分析可知,我們並不能證明SD卡中U-boot徹底失敗,因為SD卡正常引導的時候也會輸出SD checksum Error,因為x210這塊板開機後inand通道的檢測優先順序是大於SD卡所在的通道的所以這句話指的是inand中校驗失敗。此外本應在lowlevel_init中最先列印的”OK“卻沒有正常列印,但是lowlevel_init中的供電鎖存卻成功了 
    所以得出結論,可能有兩種情況:1.程式在供電鎖存和列印”OK”之間的某處掛掉了;2.串列埠進行了錯誤的初始化(可能性不大,因為BL0中初始化過了)

2.解決串列埠的問題

由於串列埠列印對於除錯非常重要,所以重點先要把串列埠問題解決

  • 根據之前分析,先去lowlevel_init中檢查串列埠的初始化是否正確,結果發現串列埠的暫存器設定完全符合我們板子,所以能排除掉串列埠初始化錯誤的可能性了
  • 如果原廠的選用的串列埠不是我們板子上的串列埠,那麼就需要修改了,一般來說,選用哪一個串列埠還是使用巨集定義來決定的,通常沒有硬編碼,以210為例,只需要將smdkv210single.h中 #define CONFIG_SERIAL3 改為需要的串列埠即可
  • 然後檢查供電鎖存和列印”OK”之間的程式碼 
    這裡寫圖片描述 
    發現問題會在 PMIC、時鐘、記憶體初始化中。由於時鐘是SOC內部的,和板級無關,故不會有問題。同樣,lowlevel_init中的程式碼還沒重定位,用不到DDR,故也不會有問題。那麼問題可能出在設定PMIC中。另外此處也可以利用連續點燈定位bootloader的錯誤這個方法定位錯誤
  • 查資料可知, PMIC是一種電源管理晶片,而我們板子上沒用到;再看看PMIC_InitIp這個函式,發現裡面都是I2C的通訊程式碼….果然是在這裡掛掉了….由於我們沒有用PMIC,I2C的通訊便會失敗,一直處在某一個while(1)中
  • 燒錄,嘗試,發現整個U-boot居然啟動起來了,但是裡面的初始化有挺多的錯誤 
    這裡寫圖片描述 
    可知,DRAM、SD、網絡卡、fastboot之類的全部有問題,接下來開始根據U-boot初始化階段流程的順序來整改

3.檢查供電鎖存、時鐘、記憶體、串列埠配置

這幾部分的初始化都在lowlevel_init中

  • 關於供電鎖存,由現象可知並沒有問題
  • 關於時鐘,由U-boot列印資訊可知,沒有問題。不過如果要修改的話,直接改smdkv210single.h中的巨集即可,無需修改程式碼
  • 關於記憶體,由U-boot列印資訊可知,記憶體大小是有問題的,板子上只有512M(每片256,一片位於0x20000000,一片位於0x40000000),打印出來總共卻有1G。那麼記憶體本身的初始化(即時序、驅動強度等)有沒有問題呢?輸入md命令檢視記憶體資訊 
    這裡寫圖片描述 
    發現記憶體可以正常工作,則記憶體本身的初始化(即時序、驅動強度等)是正確的,錯誤是在記憶體的部署上面。那麼進入smdkv210single.h修改DDR相關的巨集 
    這裡寫圖片描述 
    將SDRAM_BANK_SIZE(單片SDRAM大小) 改為0x10000000 ,即256MB。改為再檢查檢查後面幾行,SDRAM1起始實體地址是正確的;單片SDRAM大小也是正確的;但是,SDRAM2的實體地址是有問題的,其值為MEMORY_BASE_ADDRESS + SDRAM_BANK_SIZE,即SDRAM1起始實體地址加上單片SDRAM的大小,這樣算下來就不對了。我們根據板子上的部署,將其改成0x40000000 
    這裡寫圖片描述 
    上電後,發現打印出來的DDR相關的設定都正確了~

  • 關於串列埠,由現象可知並沒有問題

4.檢查虛擬地址對映

預設的原廠U-boot的虛擬地址對映是不用改的,除非修改了記憶體從部署和配置

  • (可選步驟)接下來嘗試修改記憶體的部署和配置,把記憶體的部署改為0x30000000~0x4FFFFFFF,目的是為了熟悉並演示修改U-boot的引數的流程。首先,我們假設原廠的程式碼寫的都挺好的,不存在硬編碼,那麼只需要修改smdkv210single.h內的巨集即可。進入檢視和記憶體有關的巨集,首先根據條件編譯確定了我們使用的巨集為 
    這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述 
    分析可知,記憶體本身的初始化(即時序、驅動強度等)應該不用改,需要改的是DMC0控制器的起始位置,仔細查詢暫存器可知,只需修改DMC0_MEMCONFIG_0 值為 0x30F01323;此外,還需修改MEMORY_BASE_ADDRESS 值為 0x30000000,這個巨集被引用了多次,指導了很多程式碼的執行。
  • 然後來檢查和記憶體有關的虛擬地址對映表,進入lowlevel_init 
    這裡寫圖片描述 
    發現0xc0000000被對映到了0x20000000,而我們記憶體的起始地址為0x30000000,故把第一行改為.set __base,0x300即可
  • 此外,還需要檢查一下smdkv210single.h內有關虛擬地址的巨集,發現 
    這裡寫圖片描述 
    看來必須要修改這個虛擬地址轉換為實體地址的函式,定位後發現這裡寫圖片描述 
    這個函式寫的也是無語,居然硬編碼…..我們直接把0x20000000改成0x30000000即可….. 
    上電,發現修改成功

5.檢查MMC配置

  • 由U-boot列印資訊可知,mmc的配置是有問題的這裡寫圖片描述 
    但是由於mmc的初始化利用了linux的驅動,比較龐大,排查起來較為不便。所以,U-boot的列印資訊是定位問題的關鍵,我們可以利用編輯器的搜尋功能來搜尋列印資訊中的關鍵字。
  • 我們在原始碼中搜索 EXT_CSD,在此我用了slickedit的搜素功能,操作如下 
    這裡寫圖片描述 
    搜尋可得結果在mmc.c中這裡寫圖片描述 
    由此定位了錯誤之所在,但是還是不明白髮生了什麼……
  • 先觀察這幾句的上下文,發現整個函式是在讀取所謂的“拓展CSD暫存器”,從拓展CSD暫存器讀取到了mmc的版本號資訊,但是版本號大於5,就導致了程式錯誤並退出。問題的根節就在於我們用的mmc太新式,而這個U-boot並不認得太新潮的mmc……
  • 解決之道只有兩種,要麼換mmc,要麼強行改程式碼。換mmc不現實,至於怎麼改,因為太難,就只能上網查了。上網查了之後發現解決方法倒是很簡單,直接把判斷的數字5改為8,跳過這個錯誤即可。修改,上電,測試,發現確實解決了這個問題
  • 問題解決了,但還有一個問題,此處U-boot初始化的是接在通道0的inand還是接在通道1的SD卡呢?其實還是函式int mmc_initialize(bd_t *bis)初始化的時候 有一句程式碼:mmc = find_mmc_device(0); 所指定的,故此處初始化的不是SD而是inand,並且我們可以通過把0改成1來改為初始化SD卡

6.檢查環境變數配置

  • 首先,在U-boot命令列下嘗試環境變數的設定,設定後發現能斷電儲存,說明環境變數功能成功,但是我們還是要檢查一下,環境變數存放的扇區是否安全,會不會和其他內容發生衝突
  • 關於環境變數分割槽的檢查,以及預設環境變數的修改詳見U-boot對啟動介質的空間分配

7.檢查網絡卡配置

根據U-boot列印資訊可知,網絡卡初始化顯然是失敗的。由於網絡卡驅動是從Linux驅動拿過來的,所以無需修改,只需要確保U-boot中網絡卡初始化成功即可。

  • 關於這個網絡卡,其實是通過s5pv210的SROM介面來直接掛載在總線上的,所謂的SROM介面其實是一種對外部開放匯流排的方式,通過這個介面,網絡卡晶片內的暫存器可以直接通過匯流排訪問,對於程式設計師來說就彷彿在訪問SOC內部的SFR,網絡卡晶片與SOC直接的通訊過程就可以不用關心了。簡而言之,這個網絡卡晶片就像整合在SOC內部一樣。s5pv210的記憶體對映表有關SROM的部分: 
    這裡寫圖片描述

  • 接下來看看我們板子上網絡卡的具體接法: 
    這裡寫圖片描述 
    首先,要清楚一點,dm9000的地址介面和資料介面是複用的!CMD接在了ADDR2(一根地址匯流排),當CMD為1時data0-data15傳輸的是資料,而當CMD為0時傳輸的是地址,也就是說,如果要訪問dm9000的暫存器,data0-data15要先傳輸地址再傳輸資料。由介面data0-data15可知,SROM介面使用了16位寬度。由介面CSn1可知,網絡卡接了SROM的bank1的片選腳,所以網絡卡處在SROM的bank1。

  • 關於網絡卡的初始化,具體是在U-boot初始化第二階段的board_init函式中,裡面有呼叫網絡卡初始化函式(和SROM介面有關),我們板子上的網絡卡是dm9000,所以真正的初始化函式是dm9000_pre_init,我們需要檢查 
    1.初始化函式 
    2.smdkv210single.h中網絡卡相關的巨集定義 
    具體修改需要根據網絡卡晶片的接法(SROM介面)來定,以x210為例,板載的是dm9000,先來看看原廠程式碼的初始化流程 
    這裡寫圖片描述 
    上來先根據巨集來配置SROM_BW_REG,查手冊得知SROM_BW暫存器主要負責各個bank的資料匯流排的寬度設定,不同的位分別設定不同的bank,由於定義了巨集DM9000_16BIT_DATA,故此暫存器配置為16位模式,並且配置的是bank5,byteenable位設定成0(不使用UB/LB),waitenable位設成0(禁止等待),addrmode設為0(半字對齊)。我們修改為bank1,此外,原廠的很多設定都是有問題的,必須修改為byteenable位設定成1(使用UB/LB),waitenable位設成1(使能等待),addrmode設為1(位元組對齊),至於這些引數到底怎麼選擇,其實都是一些約定俗成的套路,特別是dm9000作為一款歷史悠久的老晶片,很多設定的方法都已經固化了,通過網路可以很輕鬆查到。綜上所述,我們把程式碼修改為:SROM_BW_REG &= ~(0xf << 4); SROM_BW_REG |= (0<<7) | (0<<6) | (0<<5) | (1<<4); 
    隨後原廠設定了SROM_BC5_REG,查手冊得知SROM_BC*暫存器負責第*個bank的控制,暫存器的內容本身倒是不用改,只需把SROM_BC5_REG改為SROM_BC1_REG即可。 
    最後原廠配置了MP01CON_REG這個暫存器,利用了臨時變數tmp先讀後改,這個暫存器其實就是配置了引腳MP01,原廠將其設為複用功能————SROM介面中的bank5片選引腳,而我們應將其改為bank1片選引腳,查詢手冊後,將其改為tmp &=~(0xf<<4); tmp |=(2<<4);

  • 接下來看看smdkv210single.h中相關巨集定義:這裡寫圖片描述 
    由s5pv210的記憶體對映表可知,巨集 CONFIG_DM9000_BASE將dm9000地址介面的基地址設定在了bank5(0xA8000000),我們應將其改為bank1,根據記憶體對映理論上是0x88000000但此處有坑,很玄學,居然有可能不工作,需要改成0x88000300才行,至於原因的話網上眾說紛紜,一種比較合理的說法是:DM9000有不同的版本,其中有的內部自帶0x300的偏移量…故只用bank的基地址是不行的,需要加上0x300。接下來看後面,DM9000_DATA(資料介面)的地址應改為CONFIG_DM9000_BASE+4,這是因為CMD接在了ADDR2(地址匯流排),當CMD為1時data0-data15傳輸的是資料,而當CMD為0時傳輸的是地址。修改後上電,測試,發現網絡卡成功執行,可以ping通主機

8.檢查核心引導部分

  • 當前U-boot還無法啟動核心,但是首先需要確定到底是U-boot的問題還是kernel的問題,如果手頭存在著其他確定正確的U-boot或者kernel,那麼就有可能排除掉一種情況。但是一般來說,手頭是沒有這個條件的,那麼就需要多方位的去診斷

  • 首先,在U-boot命令列設定正確的環境變數bootcmd和bootargs,具體詳見 U-boot引導核心流程分析 ,設定完發現無法啟動

  • 接著,試試看使用tftp匯入核心映象至記憶體,發現仍然無法啟動

  • 根據 U-boot引導核心流程分析 末尾的論述,我們來做基本的檢測,首先懷疑機器碼不對,此處要對比U-boot和kernel原始碼中的機器碼,U-boot的部分在smdkv210single.h中的#define 
    MACH_TYPE,對比kernel原始碼中的機器碼發現相同,說明機器碼沒問題。再來看看和傳參有關的巨集是否正確定義,發現好像#define CONFIG_SETUP_MEMORY_TAGS #define CONFIG_CMDLINE_TAG #define CONFIG_INITRD_TAG 這三個巨集沒有定義,定義完之後上電發現能夠正常執行

    至此,整個移植過程正式結束,總體來說,原廠的U-boot需要修改的地方不是很多