2014年8月31日 星期日

Embedded Linux on Cubieboard2 (II): Kernel

本篇Outline


  1. Kernel 開機所需檔案
  2. Building Kernel

Kernel 開機所需檔案

    前一篇最後講完了 U-boot 的事,但如果要讓 cb2 開機,還需要一些設定檔。第一個要講的是 script.bin。
 
    script.bin 基本上是 cb2 自己的硬體設定檔,供設定的參數非常的多種,它也是個 binary 檔,原來的文本不用自己寫,官方已經有寫好了,可從 linux sunxi 的 github 抓取。
git clone https://github.com/linux-sunxi/sunxi-boards.git
script.bin 原來的文本副檔名是 .fex,而 cb2 的 fex 檔位於 sunxi-boards/sys_config/a20/cubieboard2.fex。所有可設定的硬體參數都在這個官方文件裡,在這邊我要提起的一個設定是 MAC address 問題。cb2 本身有 EMAC 的功能,簡單來說就是動態獲取 MAC address,當然你也可以把它關掉使用靜態 address,但必須要注意的是 fex 檔裡絕對不能鬧雙包,也就是同時使用 EMAC 及靜態 address,鬧雙包的話他不會噴錯,但核心網路界面就會莫名其妙的不見了。如果你要使用靜態 MAC (預設是 EMAC ),就在 fex 檔裡加入下列文字。
[dynamic]
MAC="你的address"
接著找到檔案裡的 emac_para 區段(也就是找到"[emac_para]"),把第一行 "emac_used=1" 改成 "emac_used=0"。
接下來要把 fex 檔轉換成 binary 檔,需要 linux sunxi 的工具。
git clone https://github.com/linux-sunxi/sunxi-tools.git
切換到 sunxi-tools 目錄之後,建置 fex2bin。
make fex2bin
轉換 fex 檔。
fex2bin cubieboard2.fex ./script.bin

    接著要講boot.scr。boot.scr 是 U-boot 開機時會讀取的 binary 設定檔,它原來的文本( boot.cmd )是一個腳本,可以在裡面寫你要執行的 U-boot 指令。boot.scr 並非必要,沒有它 U-boot 還是有自己的預設值,雖然如此,我還是親手來寫一遍比較有感覺吧。我在 boot.cmd 裡第一條要寫的是設定 kernel argument。cb2 完整的 kernel arguments 在這個網站,但在這邊,我只要設定最重要的幾個。
setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait panic=10
" setenv bootargs" 是 U-boot 的指令,後面接的那些才是真正的 kernel arguments,上例是用於把 boot 分割區獨立畫出來的情況,所以 root 參數,也就是根檔案系統的位置,是在 p2,如果是沒分開來的情況就要寫 root=/dev/mmcblk0p1。接下來要寫的就是載入核心以及 script.bin,這裡採用的方法是先把他們載到一個記憶體位址再用 bootm 指令開機。
fatload mmc 0 0x43000000 script.bin
fatload mmc 0 0x48000000 uImage
fatload 顧名思義就是從 FAT 型態檔案系統裡載入所需檔案,因此,如果你沒有把 boot 分割區特別畫出來,核心等檔案放在 boot 資料夾且檔案系統是用 ext2 的話,就要這樣寫。
ext2load mmc 0 0x43000000 boot/script.bin
ext2load mmc 0 0x48000000 uImage boot/uImage
接著就是開機的指令了。
bootm 0x48000000
上面講的就是要寫進 boot.cmd 的指令。最後一步是把文本轉成 binary 檔,這裡需要 mkimage 工具,沒有意外的話是包含在 U-boot 原碼裡,跟 U-boot 主體一起編譯好了,位於 U-boot 目錄底下的 tools 資料夾。
mkimage -C none -A arm -T script -d ./boot.cmd boot.scr
boot.scr 也是放在 boot 分割區(或是 boot 資料夾)裡,U-boot 開機時自己會去找。如果你之後開機時出現 machine id 不被 kernel 接受的錯誤,就先記下接受的 machine id,接著用下方指令在 boot.cmd 中設定。(假設 kernel 接受的 id 是 0xf0)
setenv machid 0xf0

Building Kernel

    編譯 kernel 這個步驟相較於前面,顯的非常簡單。首先把官方改過得 kernel 抓下來。
git clone https://github.com/cubieboard2/linux-sunxi.git -b sunxi-3.4-cb2
要注意的是不要用 cb2 的 3.3 kernel,不僅問題很多(我連開機都開不起來),貌似也沒有繼續維護了。

接著設定環境。
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
套用 cb2 預設 config。
make cubieboard2_defconfig
祈禱不會出錯吧
make all modules
建置 uImage。要注意建置 uImage 需要 mkimage 工具喔
make uImage
基本的 kernel 建置就這麼簡單,下一篇會開始講 user space 的事。

2014年8月5日 星期二

Embedded Linux on Cubieboard2 (I): Toolchain 與 Bootloader

系列前言

    Cubieborad2 是深圳 Cubie Tech 公司 Cubieborad 系列ARM架構開發版的第二代。搭載了Allwinner A20(ARM Cortex-A7)@1GHz Dual Core處理器,1GB DDR3 記憶體,其他外部配備有 3.4GB NAND Flash,最高支援到 64GB 的 micro SD 插槽,HDMI,10/100 Ethernet,2 x USB 2.0 HOST,1 x mini USB OTG,SATA3 。
    Cubieboard 最吸引我的是它完全開源的軟硬體與廣大社群,除了頁面整理亂到不行+不定期死機的Wiki以外
    本系列文章是紀錄我個人在移植一個基本且盡量輕小的 Linux 系統到 Cubieboard2 (簡稱cb2)時的一點紀錄。
    作者本身才剛踏入嵌入式系統這個領域,文章裡如有哪邊需要修正請不用客氣的在留言中提出。

本篇Outline

  1. Cross compile toolchain
  2. 製作 Bootloader

Cross Compile Toolchain

    我 toolchain 用的是大名鼎鼎的 linaro toolchain。基本上把 binary 載下來就好了,但是由於不明原因,編譯 cb2 的 kernel 時請用 gcc 4.7 的,至於其他像是 bootloader 就可以用 gcc 4.8。以本篇為例,我 cross compiler 的 gcc 名字就是 arm-linux-gnueabihf-gcc,值得一提的是 gnueaibhf 的 "hf" 代表的是 hard float,簡單來說就是用硬體運算浮點數,以前比較舊的處理器運算浮點數的時候是用整數的指令來做運算,後來才有專門是作浮點數運算的硬體指令出現,速度自然是比較快的。

製作 Bootloader

    Cubieboard2 使用的是 U-boot 當作他的 bootloader,我沒那麼厲害可以從零開始移植 U-boot 到這塊板子,所以我就先下載官方移植好的 U-boot 自己做編譯和安裝。基本上像 bootloader 這麼底層的東西,文件和原始碼都在 Allwinner 的 wiki 和 github 裡。
首先,下載 U-boot 原始碼。
git clone https://github.com/linux-sunxi/u-boot-sunxi.git
sunxi 是 Allwinner 底下 ARM SoC 的代稱。U-boot 設定 cross compiler 的方式非常方便,跟眾多大型專案一樣,把環境變數 CROSS_COMPILE 設定成 toolchain 名字的 prefix 就可以了。
export CROSS_COMPILE=arm-linux-gnueabihf-
注意最後面有一個"-",因為在 Makefile 裡它是這樣展開(示意):
CC = $(CROSS_COMPILE)gcc
 接下來設定 U-boot:
make Cubieboard2_config
至於全部裝置的列表是在 boards.cfg 裡,所以如果是 cb1 的 U-boot 的話就是 make Cubieboard_config。 config 完之後就大力的輸入 make 吧。

接著,你會在 U-boot 的目錄下看到一個 u-boot-sunxi-with-spl.bin 的檔案,那就是我們要燒進去的 bootloader。事實上,上面的檔案是由兩個東西組成的:U-boot 和 SPL,SPL 就是 Second Program Loader,簡單來說就是負責載入第三階段的 U-boot,描述了許多載入時所需要的硬體資訊,如果要分開燒的話 SPL 在底下的 spl 資料夾的 u-boot-spl.bin。

    下圖是 cb2 SD 卡開機區段的 layout。


SD 卡的分區我是把 boot 與主系統分開來,這樣的優點是主系統可以使用 ext4 檔案系統,因為這個官方提供的 U-boot 在讀取kernel等開機所需檔案時,目前只能支援 fat32, ext2 等比較老的檔案系統(mainline 的 U-boot 已經支援 ext4 囉),所以像是另外一種沒有把 boot 分開來的切割方式,就只能使用有支援的檔案系統。切割工具的部份,我個人試過很多像是 fdisk, sfdisk 等,最後還是覺得:用 GParted 就可以了,又快又方便。

boot 分割區我是把它格式化成 fat32(下次再來試試 ext2 看有什麼差別)外加 LBA,在 fdisk 裡面 fat32(LBA) 的型態代碼是 0x0C,但奇怪的是我用最新版的 fdisk 怎麼這種型態不見了只剩下普通 fat32,至於在 GParted 裡,LBA 是一個旗標,跟 boot 旗標一樣,要按右鍵管理旗標的那個地方。要注意的是,依照上表,磁碟前 1MB 是要保留的,雖然現在的工具都會保留 1MB,在 fdisk 裡,通常就是從第 2048 sector 開始割(通常 1 sector 是 512 bytes,0.5 KB),最後記得 boot 分割區要加 boot 旗標。我的磁碟分割就如下圖。


(畫紅底線的就是磁碟旗標)

在燒錄前,我們先要依 layout 把前 1MB先清乾淨,依本人慘痛的經驗,如果把 partition table 也清掉的話將會是一場災難,雖然 partition table 不一定如 layout 說的有 8KB 那麼大,但我們還是乖乖的保留前 8KB 吧。
dd if=/dev/zero of=/dev/sdX bs=1024 count=1016 seek=8
請把上面的 sdX 改成你自己 SD 卡的裝置檔。接著,把 bootloader 燒進去。
dd if=/path/to/u-boot-sunxi-with-spl.bin of=/dev/sdx bs=1024 seek=8
Bootloader 的準備到這邊基本上就告一個段落了。