星期三, 6月 06, 2007

製作ARM9的Bootstrap Root Filesystem

作者/陳俊宏
http://www.jollen.org
更新日期:2007/1/23
在「完整註明出處」的前提下(註明方式說明),您能立即擁有轉貼與引用的授權,且毋需知會作者。
目的
製作 bootstrap root filesystem(base 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.6:C library標準程式庫。
˙ ld-linux.so.2:Native 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/ 目錄下:
˙ fstab:mount 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指令做出一個空白的檔案,大小為 4M(bytes):
# dd if=/dev/zero of=ext2fs bs=1k count=4096
我們將檔案命名為 ext2fs,接著再將 ext2fs 製作成 ext2 格式的檔案系統:
# mkfs.ext2 ext2fs
mke2fs 1.32(09-Nov-2002)
ext2fs is not a block special device.
Proceed anyway?(y,n)y
選擇y後出現以下畫面:
Filesystem label=
OS type: Linux
Block size=1024(log=0)
Fragment size=1024(log=0)
128 inodes, 1024 blocks
51 blocks(4.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 ramdisk(initrd):
# 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 filesystem,uimage.img 則是給 Jollen-Kit! 使用的 Linux kernel(pre-built)。

沒有留言: