製作 ARM9 的 Bootstrap Root Filesystem

jollen 發表於 January 23, 2007 6:25 PM

更新「Building ARM9 Bootstrap Root Filesystem」教學文件,在此提供與各位朋友分享,並請多多指正,以讓文件內容更加完善。以下文件以 MS-Word 轉檔,請多見諒。


JollenRoot Filesystem置技術系列》

製作ARM9Bootstrap Root Filesystem

 作者/陳俊宏

http://www.jollen.org

更新日期:2007/1/23 

在「完整註明出處」的前提下(註明方式說明),您能立即擁有轉貼與引用的授權,且毋需知會作者。 

目的 

製作 bootstrap root filesystembase root filesystem)以提供一個最簡單、陽春且可開機的環境;製作完成的系統可開機到shell模式,並可使用 busybox 提供的指令。 

準備工作 

首先,您必須準備一台 host 開發環境,並安裝好 cross toolchain;接著,由於本文是做實機測試,因此,如果您沒有 ARM9 開發板,可以考慮使用 Qemu 來做模擬測試。 

以下的操作示範,只節錄重點指令片段,您可能必須根據自己的整體實作流程,來微調指令的順序,或是參數等。 

Step 1:建立工作目錄 

建立一個專用的工作目錄,命名為 arm9.so-busybox/ 

# mkdir arm9.so-busybox/

# cd arm9.so-busybox/ 

接著在 arm9.so-busybox/ 目錄下建立 4 個子目錄: 

# mkdir src/ install/ mnt/ pub/ build/ 

實際進行 root filesystem 實作時,我們應該養成將檔案分類擺放的好習慣。以本專案為例,build/ 目錄用來編譯程式,src/ 目錄用來存放原始程式碼,install/ 目錄則用來擺放我們最後的 root filesystem 

Step 2:建立目錄架構 

根據 FHS 的目錄架構標準,在 root filesystem 目錄下(install/)建立目錄階層架構: 

# cd install/

# mkdir bin/ dev/ etc/ mnt/ proc/ sbin/ usr/ 

另外還有二個必要的目錄:/var /tmp,由於這二個目錄都需要具備寫入權限,所以在這裡我們是以 ramdisk 的做法來 mount 這二個目錄。 

Step 3:建立裝置檔 

root filesystem dev/ 目錄下建立必要的裝置檔: 

crw------- 1 root root 5 1 1 1 1970 console

crw------- 1 root root 29 0 1 1 1970 fb0

crw------- 1 root root 1 3 1 1 1970 null

brw------- 1 root root 1 0 1 1 1970 ram0

crw------- 1 root root 5 0 1 1 1970 tty

crw------- 1 root root 4 0 1 1 1970 tty0 

此階段使用 mknod 指令來完成。請先切換到 root filesystem dev/ 目錄下,接著執行以下指令: 

# mknod console c 5 1

# mknod fb0 c 29 0

# mknod null c 1 3

# mknod ram0 1 0

# mknod tty c 5 0

# mknod tty0 c 4 0 

對於需要產生大量 device file 的場合來說,可以改用 genext2fs ‘-D’ 參數來製作。詳見 Jollen’s Blog[使用 genext2fs '-D'device file table)來建立 root filesystem] 

Step 4:加入Busybox 

編譯並安裝 Busybox(動態程式庫方式)。將取得的Busybox原始碼解壓縮至 project 目錄裡的 src/ 子目錄下,以下是幾個注意事項: 

  • 本教學文件使用 Busybox 1.3.1

  • Busybox 1.3.0 開始,使 Linux Kernel Makefile(因為開始支援 CONFIG_DESKTOP)。Cross compile 時,需要修改 Makefile 如下: 

ARCH ?= arm

CROSS_COMPILE ?= /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-

CROSS_COMPILE 的設定是 cross toolchain 的「PREFIX」,視您的 toolchain 而定。您可由 http://www.jollen.org/kit/ 下載本文所使用的 GCC 3.4.1 ARM9 toolchain,以使用與本文完全相同的修改。 

Busybox 整合了常用的指令與工具,我們可以設 Busybox,以勾選我們需要的功能選項。進入 Busybox 的設定選單: 

# make menuconfig

 請注意,init shell 是必選的項目,請檢查是否有勾選這二個功能。同時,也別忘了設定 Busybox 的安裝路徑,將安裝路徑指到我們 root filesystem 目錄下。 

接著直接進行編譯(cross compile): 

# make

 編譯完成後,將 Busybox 安裝至我們的 root filesystem 目錄(即 Step 2 install/ 目錄): 

# make install

 此時,您應該可以在 root filesystem 目錄下看到 Busybox 所安裝的檔案。 

Step 5:加入動態程式庫

 編譯完成的 Busybox 已經是給 ARM9 執行的格式了,但我們的編譯設定是將Busybox 編譯成 shared library 架構,因此 Busybox 執行時需要以下的檔案: 

˙ libc.so.6C library標準程式庫。

˙ ld-linux.so.2Native dynamic loader

 請由 toolchain 將以上二個檔案複製至 root filesystem lib/ 目錄下: 

# cd ../../install (切換至root filesystem根目錄)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/ld-linux.so.2 lib/ (複制native dynamic loader。以上命令請勿斷行)

# cp /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/libc.so.6 lib/ (複製C library。以上命令請勿斷行)

 Busybox 會因版本與功能選項設定的差異,而需要更多的程式庫。請使用 cross toolchain objdump 指令來檢查 Busybox 的程式庫相依問題(無法使用 ldd 指令),並將所需的程式庫由 toolchain 複製到 root filesystem lib/ 目錄下。 

Step 6:加入系統檔案 

加入2個重要的系統檔案於 etc/ 目錄下: 

˙ fstabmount table

˙ inittab:系統初始表(init table)。 

etc/fstab內容如下: 

/dev/ram0 / ext2 defaults 1 1

none /proc proc defaults 0 0

/dev/ram1 /tmp ramfs defaults 0 0

/dev/ram2 /var ramfs defaults 0 0  

fstab 第一行設定,目的在將 /dev/ram0 重新附掛成 ‘/’root),此動作用意在於重新指定 ‘/’ 的檔案系統為 ext2。最後二行的目的是為了以 ramfs mount 要的二個目錄:/var /tmp;如此一來,就算開機沒做 remount root(詳見後文說明),也能對 /tmp /var 目錄做寫入的動作 

etc/inittab內容如下: 

:0:sysinit:/etc/rc.d/rc.init

:0:respawn:/bin/sh 

根據這個 inittab 設定,當系統開機後便會進入 run level 0,在 run level 0 模式下,init process會執行2個動作:(1) 執行 /etc/rc.d/rc.init,此即「init script」;(2) 執行 /bin/sh,即進入 shell 模式。 

在此我們並沒有參照 LSB 的標準來設定 run level,而且也沒有使用 getty 來讓使用者登入(多使用者模式)。 

Step 7:編寫 Initial Script 

根據 inittab 的設定,我們 root filesystem init script 位於 /etc/rc.d/rc.init。以下提供一個供 Embedded Linux 使用的 init script 範本: 

#!/bin/sh 

# automount (/etc/fstab)

mount -a 

# remount root

mount -o remount rw / 

#

mkdir /var/lock

mkdir /var/lock/subsys

mkdir /var/run

 

# start other applications (Running application automatically during

# booting up.

# eg. /bin/thttpd –p 80 –d /var/www

 當我們執行「mount –a」後,mount 便會去讀取前一步驟所設定的 fstab,並根據此表格的內容來做 mount 的動作。另外,這裡有一個 remount 的動作: 

# mount -o remount rw / 

此動作的目的是將 root’/’)重新 mount 成可讀寫,此動作是選擇性的,若省略不做,請務必保持 /var /tmp 目錄是能寫入的(建議以 ramdisk 方式實作為佳)。 

root filesystem 未包含 inittab 設定檔,則 Busybox 會使用以下的內建設定: 

::sysinit:/etc/init.d/rcS

::askfirst:/bin/sh

::ctrlaltdel:/sbin/reboot

::shutdown:/sbin/swapoff -a

::shutdown:/bin/umount -a -r

::restart:/sbin/init 

不過,還是建議編寫自己的 inittab 定檔。 

Step 8:製作 Root Filesystem 映像檔(Image File 

截至目前為止,我們的檔案系統已經擁有基本的系統指令與工具。接下來,我們即可將建置完成的 root filesystem 製作成 ext2 格式的映像檔。 

以下提供二種 ext2fs image file 的製作方式:(1) 土方法;(2) 使用 genext2fs 工具。 

先說明傳統的土方法。首先,先利用dd指令做出一個空白的檔案,大小為 4Mbytes): 

# dd if=/dev/zero of=ext2fs bs=1k count=4096 

我們將檔案命名為 ext2fs,接著再將 ext2fs 製作成 ext2 格式的檔案系統: 

# mkfs.ext2 ext2fs

mke2fs 1.3209-Nov-2002

ext2fs is not a block special device.

Proceed anyway?yny 

選擇y後出現以下畫面: 

Filesystem label=

OS type Linux

Block size=1024log=0

Fragment size=1024log=0

128 inodes 1024 blocks

51 blocks4.98%reserved for the super user

First data block=1

1 block group

8192 blocks per group 8192 fragments per group

128 inodes per group

 

Writing inode tables done

Writing superblocks and filesystem accounting information done

 

This filesystem will be automatically checked every 26 mounts or

180 days whichever comes first. Use tune2fs -c or -i to override. 

到這裡我們已經做好一個檔案格式為 ext2 的空白映像檔,再來只要將先前做好的 root filesystem 全部複製到 ext2fs 映像檔「裡面」即可。 

先將 ext2fs 附掛至任一空目錄,例如 mnt/ 

# mkdir mnt/

# mount -t ext2 -o loop ext2fs mnt/ (指定檔案系統為 ext2 

複製檔案系統時,我們不使用 cp 指令,而是利用 tar 來完成: 

# cd install/

# tar cz * > ../install.tar.gz (將檔案系統做成tarball,同時也備份 root filesystem。)

# cd ..

# cd mnt/

# tar zxvf ../install.tar.gz (再將tarball解至映像檔) 

接著將映像檔 umount 並壓縮即可: 

# cd ..

# umount mnt/

# gzip -9c ext2fs > pub/ext2fs.gz 

最後得到的 ext2fs.gz 即是完成品。請注意,若不使用 tar 來說,也應該使用 cpio 來複製檔案,避免使用 cp 指令。 

使 genext2fs 

genext2fs 是一個 ext2 filesystem image file 的製作工具,可以讓我們很方便地將 root filesystem 製作成 image 檔。請由 genext2fs 的官方網站下載原始碼套件: 

http://genext2fs.sourceforge.net/ 

編譯後可以取得 genext2fs 檔案,以下是將 install/ 目錄製作成 ext2fs image 檔的指令: 

# genext2fs -b 8192 -i 1024 -d install/ ext2fs 

執行後,會得到檔名為 ext2fs image 檔,大小為 8 MB(透過 ‘-b’ 參數指定 image file 大小);接著同樣再用 gzip ext2fs 檔壓縮即可。 

Step 9:在 Target 端做測試 

本步驟以 Jollen-Kit! 為例,Jollen-Kit! 是由 www.jollen.org 所推出的 ARM9 training board,詳細介紹請參考 [http://www.jollen.org/kit/]。請注意,本階段的操作,視 target device 的不同而不同,因此以下示範只適用於 Jollen-Kit! 或是其他的 SMDK2410 平臺。 

步驟 8 所得到的 ext2.gz 必須再包裝成 U-Boot 的格式,才能透過 U-Boot 載入到 RAM,以成為 kernel initial ramdiskinitrd): 

# mkimage -A arm -O linux -T ramdisk -C none -a 0x30800000 -e 0x30800000 -n ramdisk -d ext2fs.gz urootfs.img 

執行後可得到 urootfs.img 檔案,在測試階段為了方便起見,我們可以直接將 urootfs.img 載到 RAM 做測試;U-Boot 指令如下: 

jollen.org # tftpboot 32000000 urootfs.img; tftpboot 30F00000 uimage.img; bootm 30F00000 32000000 

urootfs.img 是我們製作的 root filesystemuimage.img 則是給 Jollen-Kit! 使用的 Linux kernelpre-built)。 

延伸閱讀

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw