李龍杰,李競擇,李澤銀
(中國兵器裝備集團自動化研究所有限公司,四川綿陽 621000)
傳統的嵌入式linux系統升級過程中,首次升級通常使用串口或者專用燒寫設備將bootloader 燒寫至目標板的flash中作為基礎程序,然后通過該基礎程序對內核以及文件系統升級,該過程均通過網絡下載至內存中,然后燒寫至flash 完成。在首次flash 成功燒寫映像后,后續對所有映像的升級均可在bootloader 或linux 系統下使用網絡下載命令結合flash 擦寫命令共同完成。
上述無論是在bootloader 還是linux 系統下,升級任何程序都會經歷網絡下載、擦除flash 分區、寫映像到flash 分區的過程。這種升級方式首先由于是手動單步完成,需要升級人員對操作命令以及對系統分區(擦寫固定地址及大小的)熟悉,這增加了升級難度,特別是有需求升級多映像多分區時,極易出錯。其次網絡下載映像后,沒有任何校驗檢查映像是否損壞就直接寫入分區,若映像下載過程中損壞或者出錯,則會直接導致升級后設備不可用的風險,特別是升級bootloader 出錯時,將導致設備無法啟動,風險極大。最后,在bootloader 和linux 系統下保留了這些升級必要的flash 擦寫命令,將給系統帶來極大的安全隱患,如系統維護人員誤操作擦除或寫入了flash某區塊,誤操作系統所在分區將導致系統無法啟動,誤操作文件系統所在分區將導致文件丟失,造成系統運行異常,更有甚者將遭到商務競爭對手進入設備后對設備的破壞或惡意升級。本文將結合某工程中使用的uboot 作為bootloader 以及linux2.6 內核作為參考,介紹了一種將手動升級過程進行整合且裁剪系統級相關升級命令的方法,成功解決了手動單步執行繁雜易出錯、升級映像可靠性不明確、安全性較低的問題。
嵌入式linux 系統軟件部分主要包含bootloader、linux 內核、文件系統[1]。bootloader 是嵌入式板卡CPU上電后執行的第一段代碼,通常CPU會根據配置在啟動引腳上的電平組合方式決定從NAND FLASH、NOR FLASH 還是網絡等啟動。bootloader 的主要功能是完成基本硬件初始化(如內存、調試串口、網絡等外設)、鏡像的搬移(將bootloder、內核、文件系統拷貝至內存中),并跳轉到內核處執行。內核是操作系統核心,負責系統的進程管理、內存管理、文件系統、網絡功能、硬件驅動、安全機制等[2]。可根據自身需要定制編譯的內核運行更快具有更少更簡化的代碼[3]。文件系統定義了文件組成方式、存儲方式以及查找方式等。Linux常見的文件系統包括磁盤的文件系統,如ext2、ext3、ext4、XFS、JFS、NTFS 等,閃存文件系統,如JFFS2、YAFFS等,特殊用途的文件系統,如sysfs、tmpfs、squashfs 等。嵌入式linux系統啟動過程如圖1所示。

圖1 Linux啟動過程
當前目標板提供了128MB的NAND FLASH,筆者將系統劃分為6個分區,分區名以及功能如表1。

表1 系統分區情況
該方案將系統設計為雙分區系統,mtd0存儲的是uboot 鏡像,mtd1存儲uboot 運行過程中使用的環境變量,其中,mtd2 和mtd3 兩個分區互為工作區和備份區,增強設備可靠性,一般升級過程中僅升級備份區,升級完成后,備份區切換為工作區,工作區切換為備份區。mtd5分區存儲設備硬件形態關鍵數據,軟件通過建立硬件抽象層識別和操作這些硬件,對上層軟件屏蔽了硬件形態差異,實現多形態硬件兼容。
針對上述分區設計情況,筆者首先實現了一個自編寫命令行打包工具,該工具將所有需要升級的映像打包為一個映像,該映像奠定了一鍵安全升級的基礎。映像包含一個頭,記錄了文件所包含的固件類型、大小、位置、校驗和、廠商信息、版本信息等內容,如下所示:

在uboot 和linux 系統下分別實現一個升級命令,該升級命令實現了映像的下載、校驗、識別、flash 擦除、flash寫入、分區切換功能,可實現映像的一鍵下載及升級。同時,筆者裁剪掉了uboot 和linux 系統下的flash相關操作命令以及ftp、tftp下載命令,所有的升級映像下載均在自編寫升級命令中用代碼實現。
自編寫程序打包工具是一個在linux虛擬機上運行的命令行工具,名稱為”fw_pack”,使用方法如圖2所示。
該工具根據傳入的命令參數將升級固件uboot、uImage、rootfs 等打包為一個大的映像文件,該文件包含如前面描述的一個文件頭。打包并發布固件時,僅支持五類文件,如表2所示。

圖2 打包工具用法

表2 文件類型
根據需要升級的內容添加或裁減命令行參數,可打包成不同的映像。如需打包fw_all.bin,首先將uboot、uImage、rootfs 拷貝至打包工具所在目錄,然后執行命令“fw_pack-t 4-u uboot.bin-k uImage-r rootfs.bin”,命令執行后,將根據傳入的參數把所有文件信息填充到映像頭對應域,然后將合并成一個bin文件,合并后的文件計算MD5校驗和并填充在映像頭的ucMd5Sum域。最后在命令行工具所在目錄輸出對應的fw_all.bin。打包后,fw_all.bin文件映像結構如表3所示(這里假定所有文件大小均為1Mbytes):

表3 映像結構說明
在uboot 和linux 系統下分別實現一個升級命令,名稱為upgrade,使用用法如圖3所示。

圖3 升級命令
執行升級命令后,升級程序根據傳入參數從對應的ftp 服務器上下載指定文件名的映像,下載成功后將對映像的廠商以及校驗和進行合法性檢查,通過后寫入映像包含的內容到參數指定分區。升級成功后根據傳入參數決定是否重啟設備,若需重啟,則立即重啟設備。升級過程如圖4所示。
由上述過程可以看出,無論需要升級系統哪些內容,只有一個升級文件,且升級過程全由程序代碼邏輯控制,無任何人為干擾因素,提高了升級過程的自動化水平。并且在升級時對升級映像進行廠商合法性校驗以及對整個影響進行校驗和檢驗,當且僅當兩層校驗通過時才開始升級操作,提高了升級過程的安全性,防止設備開發或維護人員的誤操作或來自競爭對手的惡意升級。實現升級程序后,將uboot 下和系統下關于flash 的所有操作命令以及程序下載命令全部刪除,即該升級工具是實現升級功能的唯一通道。
如表1所示,系統采用雙分區系統,且兩個分區互為工作區和備份區,uboot 默認情況下將引導啟動工作區系統,如果工作區系統損壞,則引導啟動備份區系統。那么uboot 如何檢測到工作區系統損壞?通常情況下,uboot 在將flash中的linux 內核和根文件系統加載到內存后,跳轉執行內核前,將計算一次內核校驗和,校驗通過則啟動內核,校驗不通過則放棄啟動[4]。這就為雙分區系統設計提供了可能,但這樣的設計顯然有問題,當存儲根文件系統的flash區域存在壞塊或根文件系統遭遇破壞時,系統啟動時無法成功掛載根文件系統,導致設備無法啟動。在使用本方案設置的系統中,將增加對根文件系統的校驗,內核的校驗和可由mkimage生成,但根文件系統的校驗則沒有對應工具可生成。因此,通過自編寫根文件生成工具在根文件系統文件前加上固定幀頭,幀頭中就存儲了根文件系統的校驗和,當且僅當內核和根文件系統均校驗通過后,啟動內核,uboot 引導啟動過程如圖5所示。

圖4 升級過程

圖5 uboot引導過程
筆者根據使用場景以及本升級方法重點解決的問題設計了以下幾種測試案例,案例中均使用fw_all.bin同時升級雙分區的方式進行:
1)升級映像不做任何修改,觀察是否成功升級。
2)更改升級映像廠商名為0x12345678,觀察是否成功升級
3) 更改升級映像校驗和為0xFFFFFFFF,觀察是否成功升級。
測試結果見表4。

表4 測試結果
本文介紹了一種在uboot 以及linux 系統下,將所有系統升級內容、升級過程進行整合,并結合裁剪系統級相關升級命令的方式,達到一鍵升級、安全升級的目的[5]。該方法增加了升級過程的自動化程度及安全性,可大大提高設備維護人員對設備的升級效率,更能防止競爭對手對設備的惡意升級。同時本系統設計為雙分區系統,增加了設備的使用壽命以及遠程升級的安全性。該方法具有普遍適用性,可供其他嵌入式開發者參考使用。