天天看點

嵌入式學習——2.1 uboot基礎uboot基礎

uboot基礎

1.為什麼要有uboot?

  • 預備知識
    • 計算機系統運作時的主要核心部件包含3個東西:CPU+外部存儲器+内部存儲器
    • PC機啟動過程為:PC上電後先執行BIOS程式(實際上PC 的BIOS就是NorFlash),BIOS負責初始化DDR記憶體和硬碟,然後從硬碟上将OS鏡像讀取到DDR中,然後跳轉到DDR中去執行OS直到啟動(OS啟動後BIOS就無用了)
  • uboot主要作用是用來啟動作業系統核心,部署整個計算機系統,有操作Flash等闆子上硬體的驅動,提供一個指令行界面供人操作
  • uboot程式部署在能作為啟動裝置的Flash做上,OS部署在Flash上,記憶體在掉電時無作用,CPU在掉電時不工作
  • 嵌入式系統上電後先執行uboot、然後uboot負責初始化DDR,初始化Flash,然後将OS從Flash中讀取到DDR中,然後啟動OS(OS啟動後uboot就無用了)

2. uboot必須解決的問題

  • 自身可開機直接啟動
    • 必須根據具體的SoC的啟動方式來設計uboot
    • uboot必須進行和硬體相對應的代碼級别的更改和移植,才能保證響應啟動媒體的啟動,uboot中第一階段的start.S檔案中處理了這一塊
  • 能夠引導作業系統核心啟動并給核心傳參
    • 我們可以在uboot中事先給Linux核心準備一些啟動參數放在記憶體中特定位置然後傳給核心,核心啟動後會到這個特定位置去取uboot傳給他的參數,然後核心解析這些參數
  • 能提供系統部署功能
    • uboot必須能夠被人借助而完成整個系統在Flash上的燒錄下載下傳工作(裸機在刷機時就是利用uboot中的fastboot功能将各種鏡像燒錄到iNand中,然後從iNand啟動)
  • 能進行SoC級和闆級硬體管理
    • uboot中實作了一部分硬體的控制能力,因為uboot為了完成一些任務必須讓一些硬體工作。譬如uboot要在刷機時LCD上顯示進度條就必須驅動LCD。
    • SoC級就是SoC内部外設,闆級就是SoC外面開發闆上面的硬體
  • uboot存在生命周期
    • uboot本質上是一個裸機程式,一旦uboot開始SoC就會單純運作uboot,一旦uboot結束運作就無法再回到uboot
    • uboot入口為開機自動啟動;出口為啟動核心

3. uboot的工作方式

  • 從裸機程式鏡像uboot.bin說起
    • uboot的本質就是一個裸機程式,和裸機教程中的裸機程式xx.bin沒有本質差別,差別主要在于檔案大小,uboot在180k-400k之間
    • uboot本身為一個開源項目,由若幹個.c檔案和.h檔案組成,配置編譯之後會生成一個uboot.bin,這就是uboot這個裸機的鏡像檔案。然後鏡像檔案被合理的燒錄到啟動媒體中拿去給SoC啟動,即uboot在沒有運作時表現為uboot.bin
    • uboot運作時會被加載到記憶體中,然後逐次拿給CPU去運作
  • uboot的指令式shell界面
    • 有些程式需要人機互動,于是程式中就實作了一個shell
    • shell并不是作業系統,和作業系統一點關系都沒有,裸機也可以有shell
  • uboot使用的關鍵點:指令和環境變量
    • uboot啟動後大部分時間和工作都是在shell下完成的。uboot部署系統要在shell下輸入指令、要設定環境變量也需要在指令行下,要啟動核心也要在指令行下輸入指令
    • uboot中有幾十個指令,其中一些常用,還可以自己給uboot添加指令
    • uboot的環境變量和作業系統的環境變量工作原理和方式幾乎完全相同,uboot的驅動管理完全照抄了linux的驅動架構。系統或者程式在運作時可以通過讀取環境變量來指導程式的運作。環境變量就是運作時的配置屬性。

4. uboot常用指令

  • 指令特點
    • 有些指令有簡化的别名
      • printenv -> print
      • setenv -> set
    • 有些指令會帶參數
      • 每個指令都有事先規定好的格式,可以通過

        help

        指令檢視
    • 指令中的特殊符号(譬如單引号)
      • uboot有些指令帶的參數非常長,為了告訴uboot這個非常長且中間有好多空格的語句為一整個參數,使用單引号将這個語句引起來
    • 有些指令是一個指令族(譬如movi)
      • 指令族的意思就是好多個指令開頭都是用同一個指令關鍵字的,但是後面的參數不一樣,這些指令的功能和作用也不同
      • 同一個指令族中所有的指令都有極大的關聯,譬如movi開頭的指令族都和moviNand(EMMC、iNand)操作有關。
  • 常見指令
    • 列印環境變量:printenv/print
      • 指令不用帶參數,列印出系統中所有的環境變量。環境變量被存儲在Flash的一塊專門區域,一旦程式中儲存了該環境變量,下次開機時該環境變量的值将維持上一次更改儲存後的值
    • 設定(添加/更改)環境變量:setenv/set
      • set name value
    • 儲存環境變量的更改:saveenv/save
      • 不帶參數,直接執行。是對整體環境變量的儲存
    • 網絡測試指令:ping
      • 步驟
        • 用網線将電腦和開發闆連接配接
        • 設定電腦本地連接配接IPV4位址為192.168.1.10
        • 确認開發闆中uboot裡幾個與網絡相關的環境變量的值對不對。最重要的是ipaddr的位址必須與主機的IP位址在同一個網段内。(set ipaddr 192.168.1.xx)
    • tftp下載下傳指令:tftp
      • uboot主要目标是啟動核心,為了完成啟動核心必須要能夠部署核心,而核心鏡像需要從主機中下載下傳燒錄到開發闆的Flash中,下載下傳鏡像的主流方式為fastboot和tftp。fastboot的方式是通過usb線進行資料傳輸;tftp是通過網絡傳輸的
      • tftp方式下載下傳時uboot扮演的是tftp用戶端程式角色,主機中必須有一個tftp伺服器,然後将要下載下傳的鏡像檔案放在伺服器的下載下傳目錄中,然後在開發闆中使用uboot的tftp指令去下載下傳
      • 虛拟機和開發闆ping通的步驟
        • 虛拟機處選擇橋接方式
        • 在虛拟網絡編輯器中設定為橋接到有線網卡
        • 在虛拟機中設定IP靜态位址為192.168.1.102(修改/etc/network/interfaces檔案中的内容)
        • 重新開機網絡
          • sudo ifconfig eth0 down
                       
          • sudo ifconfig eth0 up
                       
        • 在虛拟機上搭建tftp的下載下傳目錄

          /tftpboot

          ,将要被下載下傳的鏡像複制到這個目錄下
        • 檢查開發闆uboot的環境變量,注意serverip必須設定與虛拟機Ubuntu的ip靜态位址相同
      • 在開發闆uboot中使用tftp指令下載下傳虛拟機中的鏡像
        • tftp 0x30000000 zImage-qt
                     
        • 意思為将伺服器上名為zImage-qt的檔案下載下傳到開發闆記憶體的0x30000000位址處
      • 鏡像下載下傳到開發闆DDR中後,uboot就可以用movi指令進行鏡像的燒寫了
    • SD卡/iNand操作指令:movi
      • 開發闆如果用SD卡/EMMC/iNand等作為Flash,則在uboot中操作Flash用指令movi
      • movi指令是一個指令集,在uboot中可通過

        help movi

        檢視
      • movi 的指令都是movi read和movi write一組的,movi read用來讀取iNand到DDR上,movi write用來将DDR中的内容寫入iNand中。
      • 上述指令為通用型描述方法:movi和read外面沒有任何标記說明每一次使用這個指令都是必選的;一對大括号{ }括起來的部分必選1個,大括号中的|表示多選一,中括号[]表示可選參數(可以有,也可以沒有)
        movi read u-boot 0x30000000
                   
        上面的語句為:把iNand中的u-boot分區讀出到DDR的0x30000000起始的位置處
    • NandFlash操作指令:nand
      • 操作方法完全類似于movi指令
    • 記憶體操作指令:mm、mw、md
      • DDR中是沒有分區的,但是記憶體使用時千萬不能越界,因為uboot是一個裸機程式,不像作業系統會由系統整體管理所有記憶體,則可能會出現程式的覆寫
      • md:memory display,顯示記憶體中的内容
      • mw:memory write,将内容寫到記憶體中
      • mm:memory modify,修改記憶體中的一塊,會批量逐個修改
    • 啟動核心指令:bootm、go
      • bootm啟動核心同時給核心傳參,而go指令啟動核心不傳參。
      • bootm是正宗的啟動核心的指令,go指令本來不是專為啟動核心設計的,go指令的實質是PC直接跳轉到一個記憶體位址去運作而已。go指令可以用來在uboot中執行任何的裸機程式

5. uboot環境變量

  • 如何了解環境變量
    • 環境變量有2份,一份在Flash中,一份在DDR中。uboot開機時一次性從Flash中讀取全部環境變量到DDR中作為環境變量的初始化值,然後使用過程中都是用DDR中這一份,使用者可以通過saveenv指令将DDR中的環境變量重新寫入Flash中去更新Flash中環境變量。下次開機時又會從Flash中再讀一次
    • 環境變量在uboot中是用字元串表示的,注意不要錯别字,出現儲存錯誤環境變量後

      set xxx

      消除并儲存
  • 自動運作指令設定:bootcmd
    • uboot在啟動後會開機自動倒數bootdelay秒,如果沒有打斷則會自動啟動核心
    • uboot中列印環境變量可以看到
      • bootcmd=movi read kernel 30008000

      • 上述語句意思為将iNand的kernel分區讀取到DDR記憶體的0x30008000位址處,然後使用bootm啟動指令從記憶體0x30008000處去啟東核心
  • uboot給kernel傳參:bootargs
    • Linux核心啟動時可以接收uboot給他傳遞的啟動參數,這些參數使uboot和核心約定好的形式、内容,為了核心在不重新編譯的情況下可以用不同的方式啟動
    • 在uboot的環境變量中設定bootargs,然後bootm指令啟動核心時會自動将bootargs傳給核心
    • bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

      • 意義解釋:控制台使用序列槽2,波特率為115200
      • root=… 根檔案系統在SD卡端口0裝置(iNand)第2分區,根檔案系統是可讀可寫的
      • init=… Linux的程序1(init程序)的路徑
      • rootfstype=… 根檔案系統的類型是ext3
    • 核心傳參非常重要。在核心移植的時候,新手經常忘記給核心傳參,或者給核心傳遞的參數不對,造成核心無法啟動
  • 建立、更改、删除一個環境變量的方法
    • 建立和更改
      • set var value

    • 删除
      • set var

  • 修改完環境變量後一定要儲存

6. uboot對Flash和DDR的管理

  • uboot階段的Flash分區
    • 所謂分區,就是對Flash進行分塊管理
    • PC機等産品,都是在作業系統下使用硬碟的,整個硬碟由作業系統統一管理,使用者不用自己太在意分區問題
    • 在uboot中沒有作業系統,對Flash的管理必須事先使用分區界定,在部署系統時按照分區界定方法來部署,uboot和kernel的軟體中也是按照這個分區界定來工作就不會出錯
    • 分區不是固定的,在一個移植中必須事先設計好定死。一般在設計系統移植時就會定好,定的标準是:uboot必須從Flash起始位址開始存放,uboot分區大小必須保證uboot肯定能放下,一般設計為512kb或者1MB;環境變量分區一般緊貼着uboot來存放,大小為32KB或多一點;kernel可以緊貼環境變量存放,大小一般為3MB或5MB或其他;rootfs緊貼着kernel;剩下的就是自動分區,一般kernel啟動後将自由分區挂載到rootfs下使用
    • 總結
      • 各分區彼此相連
      • 整個Flash充分利用,從開頭到結尾
      • uboot必須在Flash開頭,其他分區相對位置可變
      • 各分區的大小由系統移植工程師來定
      • 分區在系統移植前确定好,在uboot中和kernel中使用同一個分區表
  • uboot階段DDR的分區
    • 因為Flash是掉電存在的,而DDR是掉電消失,是以可以說DDR是每次系統運作時才開始部署使用的
    • 記憶體的分區主要是在Linux核心啟動之前,Linux核心啟動後核心的記憶體管理子產品會接管整個記憶體空間
    • 記憶體分區關鍵就在于記憶體中哪一塊用來幹什麼必須配置設定好,以避免各個不同功能使用了同一塊記憶體造成覆寫。