,,
(蘇州大學 電子信息學院,蘇州 215006)
用SD卡定制嵌入式Linux系統的最小系統
茅勝榮,肖家文,喬東海
(蘇州大學 電子信息學院,蘇州 215006)
以經典ARM應用處理器S3C6410為例,通過移植u-boot-2013.04-rc2和linux-3.18.57,制作EXT4格式的根文件系統,來定制基于SD卡的嵌入式Linux最小系統,并通過Appweb服務器的移植進一步完善了嵌入式系統的軟件功能。實驗結果表明,SD卡即插即用的特性大大方便了系統的開發、維護與升級。
S3C6410;嵌入式Linux;U-Boot;EXT4文件系統;Appweb
在一個完整的嵌入式Linux系統中,Flash扮演著數據與代碼存儲器的角色,其中NAND類型的Flash具有容量大、成本低的優點,所以很多處理器內部集成了專門的控制器來驅動NAND Flash。但是其工藝決定了它內部容易產生壞塊,讀寫Flash同時需要做額外的ECC校驗,這增加了驅動移植的難度。而SD卡利用特殊的封裝工藝將NAND Flash顆粒與控制器集成在一起,對外提供高速的SDIO接口進行讀寫控制,底層驅動程序徹底擺脫了復雜的壞塊管理工作。本文在SD卡上搭建U-Boot來引導Linux內核,然后掛載位于SD卡分區上的根文件系統,在此基礎上移植嵌入式web服務器Appweb。

圖1 配置單板信息
然后將內核從存儲介質讀取到內存中運行。本文以u-boot-2013.04-rc2為例,分析其移植的一般規律。
1.1 基本配置
U-Boot支持絕大多數CPU體系結構,首先在boards.cfg文件中配置單板計算機的基本信息。如圖1所示,單板名字為smdk6410,CPU架構為arm,體系結構為arm1176,制造商為samsung,SoC系列為s3c64xx,U-Boot編譯系統通過讀取這些信息來篩選需要編譯的體系結構相關的源代碼。考慮到S3C6410和S3C6400屬于一個系列,功能基本相同,因此可以選擇U-Boot中的smdk6400作為模板,復制一份相同的源碼并重命名,這樣可以迅速搭建起一個框架。其次,位于include/configs目錄下的smdk6410.h文件中包含了所有板級的配置信息,需要根據實際單板的情況作修改,比如DRAM的大小、命令行提示符等。U-Boot編譯系統將單板目錄下的u-boot.lds作為默認的鏈接腳本文件,此外在config.mk文件中指定鏈接地址的基地址CONFIG_SYS_TEXT_BASE=0x57e0 0000。代碼搬移后,u-boot.bin的代碼段將從該地址開始,其余段也會根據鏈接腳本文件確定好在內存中的分布位置。

如圖2所示,U-Boot將分多個階段依次完成最底層的初始化工作,最終創建好Linux內核運行所需要的環境。首先,u-boot.lds文件中的ENTRY(_start)語句指定了u-boot.bin代碼段的第一條指令位于start.S文件中的_start標號處,程序直接跳轉到reset異常處理代碼中運行。在low_level_init函數中,僅需對必需的外設做初始化,比如關閉看門狗、配置系統時鐘、初始化調試串口和內存控制器等。

圖2 UBoot工作流程
圖3所示為SD卡的分區信息,單板上電后,固化在S3C6410內部ROM中的程序會自動把位于SD卡BL1處的代碼讀取到SRAM中,BL1的主要工作是從SD卡中拷貝完整的u-boot.bin至DRAM中。實際上SRAM的大小只有8 KB,所以必須通過修改u-boot.lds文件來設置代碼搬移的程序在u-boot.bin的前8 KB中。代碼搬移的工作主要包括SD驅動器初始化、SD卡設備初始化,以及底層I/O的讀寫驅動。廠商已經把這些功能固化在了ROM中,并且把針對SD卡的拷貝函數的指針存放在0x0C00 4008地址處,函數原型為:
int CopyMMCtoMem(int channel, uint32_t StartBlkAddress, uint16_t blockSize, uint32_t *memoryPtr, int with_init);
其中channel是SD控制器的通道號,StartBlkAddress是待拷貝數據在SD卡上的起始扇區號,blockSize是需要拷貝的扇區數,memoryPtr是拷貝到內存的目標地址,with_init表示是否需要初始化。

圖3 SD卡分區信息
Linux內核一般使用低端的內存地址,為了盡可能給內核騰出多的空間,U-Boot會再一次將當前執行的代碼搬移到DRAM內存的頂部,這一過程即為重定位,通過匯編函數relocate_code來實現。圖4為重定位前后SRAM和DRAM內存的劃分情況,重定位前系統堆棧位于SRAM中,破壞了BL1末尾的代碼,具有一定的風險。距離DRAM內存底部0x100處存放著Linux內核的啟動參數,U-Boot在最后階段通過do_bootm_linux函數將該地址告知內核,以使內核正確掛載根文件系統。
S3C6410的啟動機制表明U-Boot只能燒寫在SD卡尾部的特定區域,如圖3所示,在偏移SDHC卡末尾521 KB處開始燒寫u-boot.bin的前8 KB,這一地址是強制要求的。除此之外的布局可以自定義,但要保證鏡像燒寫的位置與U-Boot搬移代碼時搜索的位置一致。顯然,燒寫之前必須要獲取SD卡總共的扇區數,Linux下使用fdisk-l命令可以查看當前SD卡所有的信息,然后通過簡單的字符串處理便能提取出SD卡總共的扇區數,具體腳本如下:
TOTAL_BLKCNT=`sudo fdisk-l $FLASH_MEDIA|head-n 1|awk ‘{print $7}’`
其中FLASH_MEDIA變量代表SD卡的設備文件名,通常為/dev/sdx,x可以是a,b,c,d等。
為了方便燒寫,需要把BL1和BL2兩個部分拼在一起制作成U-Boot刷機包,步驟如下:
① 創建空白鏡像:dd if=/dev/zero of=$LOADER bs=1K count=$LOADER_SIZE。
② 把鏡像文件設置為回環設備:sudo losetup /dev/loop0 $LOADER。
③ 拼接BL1與BL2:sudo dd if=u-boot.bin of=/dev/loop0 bs=1k seek=0和sudo dd if=u-boot.bin of=/dev/loop0 bs=1k seek=528 count=8。
④ 卸載回環設備:sudo losetup -d /dev/loop0。

圖4 SRAM與DRAM的內存劃分
腳本中的LOADER和 LOADER_SIZE代表U-Boot刷機包的名字和大小。最后將刷機包燒寫進SD卡設備:
sudo dd if=$LOADER of=$FLASH_MEDIA bs=1k count=$LOADER_SIZE seek=$SEEK_OFFSET
其中SEEK_OFFSET的值可以通過表達式`expr $TOTAL_SIZE - $LOADER_SIZE`來求得。圖5所示為最終燒寫在SD卡中的U-Boot啟動界面,因為SD卡中還未燒寫環境變量,因此U-Boot警告bad CRC,將使用U-Boot代碼中默認的環境變量。

圖5 UBoot啟動界面
U-Boot啟動后,在沒有檢測到用戶終端輸入的情況下會自動執行環境變量bootcmd中的指令,其具體內容定義在全局配置文件smdk6410.h中:
#define CONFIG_BOOTCOMMAND "fatload mmc 0:2 50008000 uImage; bootm 50008000"。
U-Boot通過fatload命令將SD卡第二個分區上的uImage文件讀取到內存0x5000 8000處,然后程序跳轉到該地址運行Linux內核。本文以linux-3.18.57為例,分析內核鏡像uImage制作的一般規律。
2.1 基本配置
內核的移植非常復雜,不可能一步到位,可以先移植出一個基本能用的內核,再逐步向其中添加新的功能,直至內核最終能夠驅動開發板上所有設備。Linux內核支持的使用S3C6410芯片的單板非常多,這里選擇友善之臂的MINI6410作為模板。將板級初始化文件mach-mini6410.c重命名為mach-suda6410.c,并將文件中所有的mini(MINI)修改為suda(SUDA)。將mach-suda6410.c編譯進內核中,需要由Kconfig和Makefile配合完成,即在Kconfig中加入config MACH_SUDA6410條目,并且在Makefile中添加編譯選項:obj-$(CONFIG_MACH_SUDA6410) += mach-suda6410.o。這樣就能通過make menuconfig選中SUDA6410來把文件編譯到內核中。圖6為U-Boot和內核對機器碼的定義,一個U-Boot只能引導一種單板,兩者之間使用機器碼來匹配。U-Boot在do_bootm_linux函數中會將機器碼作為參數傳遞給內核,內核會嘗試引導該機器碼對應的單板,一旦不匹配,將停止加載。
嵌入式Linux通常需要一個完整的網絡環境,Linux內核不僅具有成熟穩定的網絡協議棧,還支持各種主流的網卡驅動。圖7為網卡配置界面,通常只需要在內核配置中添加對網絡子系統的支持,并選擇實際使用的網卡對應的驅動,就可以使單板具備入網功能。
2.2 制作uImage
make menuconfig結束后,會生成.config文件,保存著有關內核的所有配置選項,make命令將根據它來指導內核編譯系統的工作,并最終生成zImage鏡像文件。U-Boot的bootm命令無法直接加載zImage,需要使用mkimage工具給zImage額外增加64字節的頭部信息,具體使用方法為:
mkimage -A arm -O linux -T kernel -C none -a 50008000 -e 50008040 -n "$KERNEL_NAME"-d zImage uImage

圖6 機器碼

圖7 網卡配置選項
其中-A指定CPU的體系結構,-O指定操作系統類型,-T指定鏡像類型,-C指定鏡像的壓縮方式,-a指定uImage在內存中的加載地址,-e指定鏡像運行的入口點地址,-n指定鏡像名字,-d指定zImage的路徑。
2.3 燒寫uImage到SD卡
uImage的燒寫不同于U-Boot,它是一個分區+格式化的過程,由圖3可知,uImage保存在SD卡的第二個分區上。U-Boot的fatload命令只能識別vfat格式的分區,因此需要將該分區格式化成vfat,具體燒寫步驟如下:
① SD卡分區:sudo fdisk $FLASH_MEDIA和sudo partprobe $FLASH_MEDIA。
② 格式化內核分區:sudo mkfs.vfat $FLASH_MEDIA$KERNEL_PART。
③ 掛載內核分區:sudo mount -t vfat $FLASH_MEDIA$KERNEL_PART /mnt。
④ 燒寫uImage:sudo cp $KERNEL_OUTPUT/uImage /mnt。
⑤ 卸載內核分區:sudo umount /mnt。
內核啟動的最后階段會根據設置的啟動參數掛載對應的根文件系統,最后運行根文件系統中的init用戶進程。
3.1 內核啟動參數
內核的啟動參數保存在U-Boot的bootargs環境變量中,也可以在smdk6410.h文件中進行設置:
#define CONFIG_BOOTARGS "root=/dev/mmcblk0p1 rootfstype=ext4 rootwait=5 console=ttySAC0,115200 init=/linuxrc"
其中的rootfstype參數指定根文件系統的類型,root參數指定根文件系統所掛載的物理設備,console參數指定用戶終端設備,init參數指定內核啟動的第一個用戶進程在根文件系統中的路徑。
3.2 構建最小根文件系統
要構建一個可用的根文件系統,需要按照FHS(Filesystem Hierarchy Standard)的標準布局文件目錄,并且創建必要的二進制文件和庫文件。Buildroot是一個簡單高效,用于定制嵌入式Linux文件系統的工具,底層封裝了很多busybox軟件的配置工作。如圖8所示,通過make menuconfig對Buildroot進行配置,Target options菜單中是與體系結構相關的配置選項,System configuration菜單中是用戶系統相關的配置選項,Toolchain菜單中是交叉編譯工具的配置選項。此外,還可以在Target packages中選擇常用的第三方應用程序包。盡管Buildroot能夠生成一個完整的根文件系統,但仍然需要微調,例如,Linux系統的啟動需要/dev/null文件,可以通過sudo mknod/dev/null c 1 3來創建這個設備文件。再如為了提高mdev生成設備文件的速度,推薦以ramfs方式掛載/dev目錄,即mount-t ramfs mdev/dev。

圖8 Buidroot配置
3.3 燒寫根文件系統到SD卡
根文件系統存放在SD卡的第一個分區。Linux內核最常使用的文件系統是EXT4,屬于日志型文件系統,它使用獨立的日志文件跟蹤磁盤內容的變化,比傳統文件系統安全。燒寫EXT4格式的文件系統到SD的主要步驟如下:
① SD卡分區:sudo fdisk $FLASH_MEDIA和sudo partprobe $FLASH_MEDIA。
② 格式化文件系統分區:sudo mkfs.ext4 $FLASH_MEDIA$ROOTFS_PART。
③ 掛載文件系統分區:sudo mount $FLASH_MEDIA$ROOTFS_PART /mnt。
④ 燒寫文件系統:sudo tar-vxf $ROOTFS_OUTPUT/rootfs.tar-C/mnt。
⑤ 卸載文件系統分區:sudo umount/mnt。
圖9展示了Linux內核與文件系統的運行結果,通過命令uname -a可以查看到當前內核的版本和名字。

圖9 Linux內核與文件系統的運行結果
在某些應用場合下,Buildroot無法提供所有需要的第三方軟件包,這就需要從源代碼編譯安裝到文件系統中。本文以開源的嵌入式web服務器Appweb為例,分析移植第三方庫的一般規律。Appweb針對嵌入式設備進行高度優化,能夠提供高能效、高吞吐率的動態網頁應用。Appweb的編譯安裝需要使用MakeMe工具,這是一種擴展了Makefile的項目管理與編譯自動化技術。Appweb移植的具體步驟如下:
① 配置源碼:./configure--show--nolocal--release--platform linux-arm-release。
② 編譯源碼:me--overwrite。
③ 安裝與發布:me--deploy deploy2ARM。
最后把deploy2ARM中的所有內容拷貝到文件系統相應的目錄中,便完成了Appweb的移植工作。在單板的用戶終端輸入命令appweb --config appweb.conf啟動

圖10 Web服務器運行結果
服務器,其中appweb.conf是針對該服務器的配置文件,可以指定服務器監聽的端口號,網頁內容的根目錄等。圖10所示是通過瀏覽器訪問該服務器返回的頁面,該網頁僅僅測試了Web服務器的最小功能。

[1] 楊鑄,李奎.構建嵌入式Linux核心軟件系統實戰[M].北京:北京航空航天大學出版社,2013.
[2] 朱兆琪,李強.嵌入式Linux開發實用教程[M].北京:人民郵電出版社,2014.
[3] 范展源,劉韜.深度實踐嵌入式Linux系統移植[M].北京:機械工業出版社,2015.
[4] 周立功.嵌入式Linux開發教程[M].北京:北京航空航天大學出版社,2016.
[5] Embedthis Software.APPWEB DOCS [EB/OL].[2017-06].https://embedthis.com/appweb/doc/.
茅勝榮、肖家文(在校研究生),研究方向為嵌入式系統設計、信號處理;喬東海(教授),研究方向為信號處理、MEMS器件設計。

[5] WANG Nan,MENG Qingfeng,ZHENG Bin.Data compression and coding algorithm used in wireless transmission of vibration signal[J].Journal of Vibration,Measurement &Diagnosis,2013,33(2):236-241.
[6] Kutyniok G.Compressed Sensing:Theory and Applications[J].Corr,2012,52(4):1289-1306.
[7] 楊真真.壓縮感知重構技術及其在圖像融合中的應用研究[D].南京:南京郵電大學,2014.
[8] 方亮.基于壓縮感知的無線傳感器網絡數據壓縮算法研究[D].長沙:湖南大學,2011.
[9] 王小雪.基于無線傳感器網絡的無源被動式目標定位研究[D].杭州:浙江工業大學,2013.
[10] 陳劍美.壓縮感知算法的改進及其在無線傳感網絡中的應用[D].秦皇島:燕山大學,2016.
邵云峰,主要研究方向為無線傳感器網絡。
EmbeddedLinuxMinimalSystemCustomizedwithSD
MaoShengrong,XiaoJiawen,QiaoDonghai
(Department of Electronic Information,Soochow University,Suzhou 215006,China)
In the paper,taking the classic ARM processor S3C6410 for example,The customize embedded Linux minimal system based on the SD card is introduced by porting u-boot-2013.04-rc2 and linux-3.18.57,and the root file system formatted in EXT4 is built.The embedded web server called Appweb makes the system more powerful in software.The experiment results show that the plug-and-play feature of SD card greatly facilitates the development,maintenance and upgrading.
S3C6410;embedded Linux;U-Boot;EXT4 file system;Appweb
TP368.2
A
2017-06-30)
(責任編輯:薛士然 收稿日期:2017-06-15)