Showing posts with label ARM. Show all posts
Showing posts with label ARM. Show all posts

Thursday, November 29, 2007

KB9202B - 製作 JFFS2 影像,並寫入 NAND Flash

試了2天,終於成功了...

我們要做的工作是把先前可用於 NFS 遠端開機的根檔案系統寫入到目標板上的 NAND Flash 中,要做這件事之前,首要工作是要建立可以寫入 NAND Flash 的一個 JFFS2 影像檔。JFFS2 是用於 Flash 的檔案系統,就我的理解,Linux 是透過 MTD 的驅動程式,將 NAND Flash 模擬成一個分割區(partition, 在我的板子上是 /dev/mtd0)。如果將製作成 JFFS2 形式的影像檔寫入到該分割區,就可以建立在該分割區上的一個 JFFS2 檔案系統。最後藉由正確的開機參數,指定 Linux 核心由該分割區(/dev/mtdblock0)開機。以下流程是說明如何建立 JFFS2 的影像檔,以及如何寫入到 NAND Flash 中。

假設製作好且確定可用於 NFS 掛載的根檔案系統,放置於主機的 /usr/local/arm/target_fs

一、建立 MTD 工具 - MTD 工具是建立 JFFS2 影像檔及寫入 Flash 的必備工具,因些必須先下載編譯。

1. 下載 MTD 工具 的程式碼(KB9202B 的開發手冊上的說明)
$ cvs -d :pserver:anoncvs@cvs.infradead.org:/home/cvs login
(密碼 : anoncvs)
$ cvs -d :pserver:anoncvs@cvs.infradead.org:/home/cvs co mtd
$ cvs -d :pserver:anoncvs@cvs.infradead.org:/home/cvs logout
2. 編譯主機上可以用的 mkfs.jffs2
$ cd mtd/utils
$ make mkfs.jffs2
$ cp mkfs.jffs2 /usr/local/bin/
 Ubuntu 也可以直接安裝 mtd-tools 這個套件,附有 mkfs.jffs2 這支程式。不過僅限於主機端使用,用於 ARM 板上的程式(flash_eraseall, nandwrite)仍要透過步驟 3 產生。

3. 透過跨平台編譯,建立可在 ARM 上面使用的 mtd 工具
$ make clean
$ make CROSS=arm-linux-
 這個跨平台編譯需要在 toolchain 中安裝 zlib 這個函式庫,如果之前建立 buildroot 時沒有選 zlib ,那就要到 buildroot 重新設定及編譯一下。

二、建立 JFFS2 影像檔,及寫入 NANS Flash

1. 在主機端,建立根檔案系統的 JFFS2 影像檔,這部份在 KB9202B 的文件中,其指令如下︰
$ mkfs.jffs2 --pad=0x4000 --eraseblock=0x4000 -l --root=/usr/local/arm/target_fs -o full_fs.bin
 不過我在後面要寫入 NAND 時出現錯誤,原因似乎是因為 NAND 內有 bad blocks ,而這樣的參數導致寫入失敗。(詳細原因我不懂@@)後來嘗試了另一組參數可以成功寫入,指令如下︰
$ mkfs.jffs2 -n -p --root=/usr/local/arm/target_fs -o full_fs.bin
2. 寫入 full_fs.bin 到 NAND 上

 a. 使用 nfs 掛載根檔案系統的方式,開啟 KB9202B 上的系統。
 b. 在主機端,將 full_fs.bin 複製到 /usr/local/arm/target_fs/root 內。
 c. 將跨平台編譯好的程式 flash_eraseall, nandwrite 複製到 /usr/local/arm/target_fs/root 內
 d. 進入 ARM 的 Linux 系統中,使用以下指令︰
[KB9202B] $ cd /root
[KB9202B] $ ./flash_eraseall /dev/mtd0
[KB9202B] $ ./nandwrite -f /dev/mtd0 full_fs.bin
[KB9202B] $ reboot
3. 設定 u-boot 開機參數 bootargs 如下︰
setenv bootargs console=ttyS0,115200n8 root=/dev/mtdblock0 rootfstype=jffs2 mem=64M
saveenv
reset

Monday, November 19, 2007

KB9202B - 使用遠端 Linux 核心及根檔案系統

最近猛 K "建構嵌入式 Linux 系統" 這本書,慢慢對整個嵌入式 Linux 的架構有了更深一層的了解。這本書是我大約在2~3年前就買的,當時就想學習嵌入式系統,但是一直苦無機會。這本書出版的時候,Linux 的核心還是 2.4 版,但是書中的資訊一直到現在仍是相當的有用。

我在前一文中有提到,在 KB9202B 的相關文件中只教怎麼編 Kernel ,沒教怎麼安裝。而事實上,在嵌入式系統的開發階段,為了節省檔案傳輸的麻煩,是可以不需要將核心甚至是根檔案系統安裝到目標板上,而是將目標板設定成 - "直接使用遠端主機上的 Linux 核心及根檔案系統"。要做這樣子的設定,主要是要修改2個地方,一個是目標板上 U-Boot 的設定,一個是主機上,要讓目標板遠端掛載的根檔案系統。

note: 下面的操作前,必須先編譯好核心,並在主機端建立根檔案系統。同時並假設核心的放置目錄為 /tftpboot,檔名為 uImage,根檔案系統的目錄為 /usr/local/arm/target_fs 。目標板的 ip 是 192.168.11.3 ,主機的 ip 是 192.168.11.2 ,gateway ip 是 192.168.11.1 。

1. 主機端的網路配置

a. tftp 伺服器的安裝及設定

我是使用 Ubuntu/Linux 為主機,因此直接使用 apt 安裝 tftp 及 tftpd 兩個套件。
sudo apt-get install tftp tftpd openbsd-inetd
接著開啟 /etc/inetd.conf 檔,並加入以下這行︰
tftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /tftpboot
其中 /tftpboot 就是放置編好的核心的地方,這個路徑是可以改的。

b. NFS 伺服器的安裝及設定

使用 apt 安裝 nfs-kernel-server 這個套件︰
sudo apt-get install nfs-kernel-server
開啟 /etc/exports 這個檔案,並加入以下這行︰
/usr/local/arm/target_fs 192.168.11.11/32(rw,no_root_squash)
其中 192.168.11.11 是我的目標板使用的 ip ,這行是指定,從 192.168.11.11 連線過來的 NFS 存取,具有讀寫的權限,同時 no_root_squash 這個引數讓目標板可以用其 root 權限來存取目錄。這個引數如果沒有設定,則在目標板掛載了之後,所有的寫入操作都無效。

2. 根檔案系統的修改
buildroot 預設建立在的根檔案系統,似乎無法直接使用 nfs 掛載,必須要在幾個地方做修改︰

a. 將 /usr/local/arm/target_fs/etc/init.d/目錄下的 S40network 改成 s40network ,即開機時不執行此 script 。

b. 修改 /usr/local/arm/target_fs/etc/network/interfaces ,加入如下關於 eth0 的描述,以開啟網路服務︰
auto eth0
iface eth0 inet static
address 192.168.11.3
netmask 255.255.255.0
gateway 192.168.11.1
c. 修改 /usr/local/arm/target_fs/etc/inittab ,將以下這行前面的 # 刪掉,這樣才可以在開機後,仍然可以經由 Serial port 來接收資訊︰
#ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
d. buildroot 並沒有正確建立 dev 下的節點,必須手動以 root 身份使用 mknod 建立。有哪些節點需要建立,可參考 buildroot 的 target/generic/device_table.txt 。建立單一節點的方式如下(必須使用super user)︰
mknod -m 0666 console c 5 1
我寫了一個 script ,在 dev 目錄下以 root 執行,可直接建立 device_table.txt 指定的所有節點,執行此檔案前必須先清空 dev 目錄。建立後可再使用 chown 修改擁有者。

e. 修改根檔案系統的 /etc/fstab 檔設定
修改 /usr/local/arm/target_fs/etc/fstab 這個檔案。將原本存在 fstab 中的下面這行前面加個 # 號。
#/dev/root / ext2 rw,noauto 0 1
這使的核心載入後,可以直接使用遠端的根檔案系統。

4. 修改 U-Boot 的設定

a. 在目標板開機倒數時,按下任意鍵進入 U-Boot 的設定程式。
b. 設定 target 及 host 的網路設定(依不同網路而異)︰
U-Boot> setenv ip 192.168.11.3
U-Boot> setenv serverip 192.168.11.2
U-Boot> setenv netmask 255.255.255.0
U-Boot> setenv gatewayip 192.168.11.1
c. 設定 bootp 要從 tftp 抓取的 kernel 影像檔名稱
U-Boot> setenv bootfile uImage
d. 指定核心下載的位置及根檔案系統位於 host 上的路徑
U-Boot> setenv kernel_addr 2000d000
U-Boot> setenv rootpath /usr/local/arm/target_fs
e. 設定開機命令稿
U-Boot> setenv bootpnfs bootp \$(kernel_addr)\; setenv bootargs console=ttyS0,115200n8 root=/dev/nfs rw nfsroot=\$(serverip):\$(rootpath) ip=\$(ipaddr):\$(serverip):\$(gatewayip):\$(netmask):::off panic=1\; bootm \$(kernel_addr)
f. 儲存及開機
U-Boot>saveenv
U-Boot>run bootpnfs

Tuesday, November 06, 2007

在 KB9202B (AT91RM9200 core) 上安裝新 Kernel - 使用 u-boot

KB9202BKwikbyte 推出的 ARM920T 學習板,核心是使用 Atmel 製造的 AT91RM9200 。最近花了很多時間在學這張板子,雖然有一些概念了,也懂的怎麼使用內建的 linux 系統,但是離入門還有點距離。

KB9202B 板子上提供有 GPIO 埠,但是如果要使用的話,似乎要自行把針腳焊接上去。不過目前的問題卡在,在程式中該怎麼存取系統的 GPIO ,尤期原廠沒有提供相關的資訊。根據 Linux 2.6.21 報馬仔 這篇的說明,2.6.21 的 linux kernel 提供有 GPIO api ,可省去自行撰寫驅動程式的過程,因此我就想嘗試將 kernel 升級。

KB9202B說明文件中,有說明如何建立 linux host 的開發環境,及如何編譯新板的 u-boot 、linux kernel。可是奇怪的是,linux kernel編好了之後,只說放到 tftpboot 的目錄下就沒了...囧
後來我進入 u-boot 模式,亂用 tftp 下載 kernel 後,系統每次在載入 kernel 時就會失敗,糗了@@。不過可說是塞翁失馬,焉知非福啦,因為這樣,逼的我去學 u-boot 了。

u-boot 是個類似 grub, lilo 等 linux x86 系統常用的開機程式,但是專用於各種嵌入式系統的應用。在 PC 系統中,linux kernel 通常是放在 /boot 或是系統根目錄(/) 下,但是在嵌入式系統的應用下,linux kernel 的放置位址通常與 root file system 是分開的,在 KB9202B 中,預設是放置在 NOR Flash 上,Flash Filesystems 的介紹可參考 Flash Filesystems for Embedded Linux Systems

KB9202Bu-boot 設定參數中,會告訴 u-boot 要到 0x10000000 的位址去載入 kernel image。如何看到 u-boot 的設定呢?
  1. 在開機時,當出現 "Hit any key to stop autoboot:" 時按下任意鍵,就可以進入 u-boot 的環境。
  2. 輸入 "printenv" 找到 bootcmd 這個變數。
KB9202B 的預設值是︰"bootcmd=bootm 0x10000000" ,因此 u-boot 會在開機時,到 0x10000000 的位址去載入 kernel image。而且只要修改 bootcmd 這個變數就可以自由改變 linux kernel 放置的位址。事實上,如果透過 flinfo 的指令,可以發現 KB9202B 的 Flash 位址就是介於0x10000000 ~ 0x10FE0000 之間,所以 kernel image 是存放在 Flash 內的。

那怎麼把編好的 kernel 放到 0x10000000 位址呢?我參考了u-boot 線上說明文件(這篇是說明如何安裝新的 u-boot),整理如下,

1.將編好的 kernel image - uImage 放到 host 的 tftpboot 的目錄下。

2.設定好 u-boot 的環境變數︰
#設定 ARM 板子的 IP
U-Boot> setenv ipaddr 192.168.xxx.xxx

#設定 host 的 IP
U-Boot> setenv serverip 192.168.xxx.xxx

#儲存變數到 EEPROM
U-Boot> saveenv
3. 下載及安裝 linux kernel image
#使用 tftp 下載 kernel 到 0x2000d000 (這個位置是暫存區),假設大小為 31c8d0
U-Boot> tftp 2000d000 uImage

#開啟 0x10000000 ~ 0x104fffff 之間的保護
U-Boot> protect off 10000000 104fffff

#將0x10000000 ~ 0x104fffff 的資料抹除
U-Boot> era 10000000 104fffff

#將暫存於 0x2000d000 的 kernel image 複製到 0x10000000 ,這個過程式滿久的#時間,請耐心等待
U-Boot> cp.b 2000d000 10000000 31c8d0
4.重開機
U-Boot> reset
接下來要再看看怎麼使用 GPIO api 了