一种基于嵌入式Linux系统的一键安全升级方法
作者: 李龙杰 李竞择 李泽银
摘要:在基于linux的嵌入式系统中,无论是在bootloader下还是系统下,对bootloader、内核以及文件系统等镜像的升级方式均是将目标板通过网络连接至主机,然后手动执行镜像下载、擦除分区、镜像写入分区的过程。该文介绍了一种将上述升级过程进行整合且裁剪系统级相关升级命令的方法,达到一键升级、安全升级的目的,该升级方法已广泛应用于多台出厂设备中,运行状况稳定、安全、可靠。
关键词:linux嵌入式系统;一键升级;安全升级
中图分类号:TP393 文献标识码:A
文章编号:1009-3044(2023)26-0084-03
开放科学(资源服务)标识码(OSID)
传统的嵌入式linux系统升级过程中,首次升级通常使用串口或者专用烧写设备将bootloader烧写至目标板的flash中作为基础程序,然后通过该基础程序对内核以及文件系统升级,该过程均通过网络下载至内存中,然后烧写至flash完成。在首次flash成功烧写映像后,后续对所有映像的升级均可在bootloader或linux系统下使用网络下载命令结合flash擦写命令共同完成。
上述无论是在bootloader还是linux系统下,升级任何程序都会经历网络下载、擦除flash分区、写映像到flash分区的过程。这种升级方式首先由于是手动单步完成,需要升级人员对操作命令以及对系统分区(擦写固定地址及大小的)熟悉,这增加了升级难度,特别是有需求升级多映像多分区时,极易出错。其次网络下载映像后,没有任何校验检查映像是否损坏就直接写入分区,若映像下载过程中损坏或者出错,则会直接导致升级后设备不可用的风险,特别是升级bootloader出错时,将导致设备无法启动,风险极大。最后,在bootloader和linux系统下保留了这些升级必要的flash擦写命令,将给系统带来极大的安全隐患,如系统维护人员误操作擦除或写入了flash某区块,误操作系统所在分区将导致系统无法启动,误操作文件系统所在分区将导致文件丢失,造成系统运行异常,更有甚者将遭到商务竞争对手进入设备后对设备的破坏或恶意升级。本文将结合某工程中使用的uboot作为bootloader以及linux2.6内核作为参考,介绍了一种将手动升级过程进行整合且裁剪系统级相关升级命令的方法,成功解决了手动单步执行繁杂易出错、升级映像可靠性不明确、安全性较低的问题。
1 嵌入式linux系统简介
嵌入式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所示。
2 方案设计与实现
2.1 总体方案设计
当前目标板提供了128MB的NAND FLASH,笔者将系统划分为6个分区,分区名以及功能如表1。
该方案将系统设计为双分区系统,mtd0存储的是uboot镜像,mtd1存储uboot运行过程中使用的环境变量,其中,mtd2和mtd3两个分区互为工作区和备份区,增强设备可靠性,一般升级过程中仅升级备份区,升级完成后,备份区切换为工作区,工作区切换为备份区。mtd5分区存储设备硬件形态关键数据,软件通过建立硬件抽象层识别和操作这些硬件,对上层软件屏蔽了硬件形态差异,实现多形态硬件兼容。
针对上述分区设计情况,笔者首先实现了一个自编写命令行打包工具,该工具将所有需要升级的映像打包为一个映像,该映像奠定了一键安全升级的基础。映像包含一个头,记录了文件所包含的固件类型、大小、位置、校验和、厂商信息、版本信息等内容,如下所示:
typedef struct
{
INT8 ascVendorName[MAX_NAME_LEN]; /*vendor name*/
UINT8 aucMd5Sum[MD5SUM_LEN]; /*MD5 checksum*/
UINT32 ulFwMask; /*bit indicate uboot、kernel、rootfs,bit0 for uboot、bit1 for kernel、bit2 for rootfs*/
UINT32 ulFwLength; /* total length of the firmware */
UINT32 ulKernelOffset; /* kernel data offset */
UINT32 ulKernelLen; /* kernel data length */
UINT32 ulRootfsOffset; /* rootfs data offset */
UINT32 ulRootfsLen; /* rootfs data length */
UINT32 ulUbootOffset; /* bootloader data offset */
UINT32 ulUbootLen; /* bootloader data length */
UINT32 ulUbootFlag; /* boot write erase env flag*/
UINT8 aucReserved[256]; /* for reserved*/
}__attribute__ ((packed))FW_HEADER;
在uboot和linux系统下分别实现一个升级命令,该升级命令实现了映像的下载、校验、识别、flash擦除、flash写入、分区切换功能,可实现映像的一键下载及升级。同时,笔者裁剪掉了uboot和linux系统下的flash相关操作命令以及ftp、tftp下载命令,所有的升级映像下载均在自编写升级命令中用代码实现。
2.2 映像打包
自编写程序打包工具是一个在linux虚拟机上运行的命令行工具,名称为”fw_pack”,使用方法如图2所示。
该工具根据传入的命令参数将升级固件uboot、uImage、rootfs等打包为一个大的映像文件,该文件包含如前面描述的一个文件头。打包并发布固件时,仅支持五类文件,如表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) :
2.3 升级过程
在uboot和linux系统下分别实现一个升级命令,名称为upgrade,使用用法如图3所示。
执行升级命令后,升级程序根据传入参数从对应的ftp服务器上下载指定文件名的映像,下载成功后将对映像的厂商以及校验和进行合法性检查,通过后写入映像包含的内容到参数指定分区。升级成功后根据传入参数决定是否重启设备,若需重启,则立即重启设备。升级过程如图4所示。
由上述过程可以看出,无论需要升级系统哪些内容,只有一个升级文件,且升级过程全由程序代码逻辑控制,无任何人为干扰因素,提高了升级过程的自动化水平。并且在升级时对升级映像进行厂商合法性校验以及对整个影响进行校验和检验,当且仅当两层校验通过时才开始升级操作,提高了升级过程的安全性,防止设备开发或维护人员的误操作或来自竞争对手的恶意升级。实现升级程序后,将uboot下和系统下关于flash的所有操作命令以及程序下载命令全部删除,即该升级工具是实现升级功能的唯一通道。
2.4 uboot引导
如表1所示,系统采用双分区系统,且两个分区互为工作区和备份区,uboot默认情况下将引导启动工作区系统,如果工作区系统损坏,则引导启动备份区系统。那么uboot如何检测到工作区系统损坏?通常情况下,uboot在将flash中的linux内核和根文件系统加载到内存后,跳转执行内核前,将计算一次内核校验和,校验通过则启动内核,校验不通过则放弃启动[4]。这就为双分区系统设计提供了可能,但这样的设计显然有问题,当存储根文件系统的flash区域存在坏块或根文件系统遭遇破坏时,系统启动时无法成功挂载根文件系统,导致设备无法启动。在使用本方案设置的系统中,将增加对根文件系统的校验,内核的校验和可由mkimage生成,但根文件系统的校验则没有对应工具可生成。因此,通过自编写根文件生成工具在根文件系统文件前加上固定帧头,帧头中就存储了根文件系统的校验和,当且仅当内核和根文件系统均校验通过后,启动内核,uboot引导启动过程如图5所示。
3 测试验证
笔者根据使用场景以及本升级方法重点解决的问题设计了以下几种测试案例,案例中均使用fw_all.bin同时升级双分区的方式进行:
1) 升级映像不做任何修改,观察是否成功升级。
2) 更改升级映像厂商名为0x12345678,观察是否成功升级
3) 更改升级映像校验和为0xFFFFFFFF,观察是否成功升级。
测试结果见表4。
4 结束语
本文介绍了一种在uboot以及linux系统下,将所有系统升级内容、升级过程进行整合,并结合裁剪系统级相关升级命令的方式,达到一键升级、安全升级的目的[5]。该方法增加了升级过程的自动化程度及安全性,可大大提高设备维护人员对设备的升级效率,更能防止竞争对手对设备的恶意升级。同时本系统设计为双分区系统,增加了设备的使用寿命以及远程升级的安全性。该方法具有普遍适用性,可供其他嵌入式开发者参考使用。
参考文献:
[1] 郑克龙,蒋明,陈小朋.基于ARM-Linux嵌入式系统Bootloader的自动升级设计[J].科学技术与工程,2007,7(21):5567-5569,5579.
[2] 谢辉.基于Linux系统的内核编译与升级研究[J].信息记录材料,2016,17(6):21-23.
[3] 陈碧珍.谈linux系统内核升级[J].福建电脑,2005,21(11):54-55.
[4] 高文玉.利用NAND Flash实现嵌入式系统的远程更新[J].单片机与嵌入式系统应用,2011,11(12):76-77.
[5] 岳洋,张杰明.Linux内核技术特性及升级[J].计算机系统应用,2000,9(12):27-28,36.
【通联编辑:代影】