嵌入式Linux开发学习基于TFTP的通用代码烧写方式
Posted 与光同程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式Linux开发学习基于TFTP的通用代码烧写方式相关的知识,希望对你有一定的参考价值。
文章目录
概述
对于从事嵌入式行业的人来说,进行代码升级一定不会陌生。相对于单片机相对单一的烧写方式,嵌入式Linux开发中的烧写方式就丰富了许多。常见的比如串口烧写,USB烧写(fastboot),sd卡烧写。
可以看出烧写方式是非常自由的,但是也带来了一些问题,不同厂家往往会有自己独特的固件烧写方式,当你手里头有非常多的板子的时候,各种各样的烧写方式并不方便,特别是有的SOC厂家提供的烧写方式并不人性化,往往需要手动输入很多很长的命令(fastboot),或者说需要你等待很长时间(串口烧写),这个时候就需要一种更为通用的烧写方法。
实现思路
我们知道的是uboot和kernel通常都会帮我们实现tftp协议
在uboot中我们可以通过tftp 将代码拷到内存中
在文件系统中我们可以通过busybox的tftp命令拷贝到内存中,于是方法也就产生了。
但是我们也发现升级的时候可能需要在文件系统中执行,所以直接在挂载块设备的文件系统中烧写自己的代码并不现实,所以在烧写的时候一定要是initramfs root=/dev/ram0的方式挂载文件系统。
升级UBOOT
这个最简单 ,我们只需要在uboot中 通过uboot的net_loop
接口读取uboot到内存中的一个位置,然后使用uboot读写flash的接口写入即可。
升级KERNEL ROOTFS
这个就会相对复杂,因为在这个过程中会涉及到格式化flash的情况,同时因为kernel rootfs以及一些应用会导致uboot在升级中出错。所以可以考虑在文件系统挂载之后使用类似于dd的命令烧写flash。
QEMU 仿真
代码实现
烧写代码肯定需要具有flash和可以通网,所以我们在使用qemu的时候命令这样设置
/usr/local/qemu/bin/qemu-system-arm -M vexpress-a9 \\
-kernel $(TOP)/publish/u-boot -nographic -m 512M \\
-sd $(TOP)/publish/uboot.disk \\
-net nic -net tap,ifname=tap0,script=no,downscript=no $(linux_debug_para)
然后需要产生一块虚拟的disk盘 作为mmc 设备也就是上面的
uboot.disk
命令如下
tar -cvf digicap.tar zImage vexpress-v2p-ca9.dtb rootfs.cpio.gz
dd if=/dev/zero of=uboot.disk bs=1024K count=1024
sgdisk -n 0:0:+1M -c 0:uboot uboot.disk
sgdisk -n 0:0:+35M -c 0:digicap uboot.disk
sgdisk -n 0:0:+100M -c 0:rootfs uboot.disk
sgdisk -n 0:0:0 -c 0:update uboot.disk
sgdisk -p uboot.disk
freeloop="$(shell losetup -f)";\\
echo $$freeloop;\\
sudo losetup $$freeloop uboot.disk;\\
sudo partprobe $$freeloop;\\
ls $$freeloop*;\\
dd if=u-boot of=$$freeloopp1 bs=512 count=2048;\\
dd if=digicap.tar of=$$freeloopp2 bs=512 count=71680;\\
sudo losetup -d $$freeloop;\\
可以发现我们将digicap.tar zImage vexpress-v2p-ca9.dtb rootfs.cpio.gz
一起打了一个包,所以为了Uboot可以读取他们,我们在uboot中完成一个解tar包的程序。
然后我们需要修改Uboot的启动代码使得uboot可以正常解析tar包 并启动内核。
关键的函数如下
static int
do_hik_prep_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
int len ,ret;
if ( getenv("update_flag")!=NULL )
hik_debug("digicap update !!! \\n");
char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M update_flag=1";
setenv("bootargs",cmdline);
return 1;
#ifdef CONFIG_HIK_NET_BOOT_MODE
char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M ";
hik_debug("run in net mode \\n");
setenv("bootargs",cmdline);
len = hik_tftp_get_file(hik_board_info.digicap_name,hik_board_info.update_load_addr,NULL );
ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
if(ret < 0)
hik_debug("read zImage failed\\n");
return -1;
ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
if(ret < 0)
hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
return -1;
ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
if(ret < 0)
hik_debug("read rootfs.cpio.gz failed\\n");
return -1;
#elif CONFIG_HIK_MMC_BOOT_MODE
char cmdline[1024]= "root=/dev/mmcblk0p3 rw rootwait earlycon console=ttyAMA0 init=/linuxrc";
hik_debug("run in mmc mode \\n");
setenv("bootargs",cmdline);
len = hik_load_digicap();
ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
if(ret < 0)
hik_debug("read zImage failed\\n");
return -1;
ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
if(ret < 0)
hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
return -1;
#elif CONFIG_HIK_RAM_BOOT_MODE
char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M ";
hik_debug("run in ram mode \\n");
setenv("bootargs",cmdline);
len = hik_load_digicap();
hik_debug("load file success %d \\n",len);
ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
if(ret < 0)
hik_debug("read zImage failed\\n");
return -1;
hik_debug("read zImage success\\n");
ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
if(ret < 0)
hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
return -1;
hik_debug("read vexpress-v2p-ca9.dtb success\\n");
ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
if(ret < 0)
hik_debug("read rootfs.cpio.gz failed\\n");
return -1;
hik_debug("read rootfs.cpio.gz success\\n");
#else
hik_debug("run in flash mode \\n");
#endif
return 0;
U_BOOT_CMD(
hik_prep_boot, 1, 1, do_hik_prep_boot,
"set up en before boot",
"hik_prep_boot \\n"
);
我们只需要在bootcmd中添加hik_prep_boot命令就可以完成启动前的所有准备工作
我的bootcmd是这样的
#define CONFIG_BOOTCOMMAND "hik_prep_boot ; bootz 0x60003000 - 0x60500000 ; "
至于升级uboot 和digicap的命令是这样实现的
static int
do_updateb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
struct mmc *mmc;
int ret;
int blk_count;
if(argc == 1)
ret = hik_tftp_get_file(hik_board_info.uboot_name , hik_board_info.update_load_addr , NULL);
else
ret = hik_tftp_get_file(argv[1] , hik_board_info.update_load_addr , NULL);
if(ret < 0 )
hik_debug("hik_tftp_get_file failed \\n");
do_reset(NULL, 0, 0, NULL);
return -1;
else
mmc = hik_init_mmc_device(hik_board_info.flash_dev , false);
if (!mmc)
hik_debug("mmc init error\\n");
return -1;
if (mmc_getwp(mmc) == 1)
hik_debug("Error: card is write protected!\\n");
return -1;
blk_count=(ret/MMC_BLK_SIZE)+1;
ret = mmc->block_dev.block_write(hik_board_info.flash_dev , hik_board_info.uboot_start_blk , blk_count ,(const void *)hik_board_info.update_load_addr);
if( ret == blk_count)
hik_debug("uboot update success \\n");
else
hik_debug("uboot update failed %d\\\\%d\\n",ret,blk_count);
do_reset(NULL, 0, 0, NULL);
return 0;//never run to here
U_BOOT_CMD(
updateb, 2, 1, do_updateb,
"updateb uboot from tftp server",
"updateb filename\\n"
);
static int
do_updatek(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
char *s;
int ret,len;
if(argc == 1)
len = hik_tftp_get_file(hik_board_info.digicap_name , hik_board_info.update_load_addr , NULL);
else
len = hik_tftp_get_file(argv[1] , HIK_UPDATE_LOAD_ADDR , NULL);
if(len < 0 )
hik_debug("hik_tftp_get_file failed \\n");
do_reset(NULL, 0, 0, NULL);
return -1;
ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
if(ret < 0)
hik_debug("read zImage failed\\n");
return -2;
ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
if(ret < 0)
hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
return -2;
ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
if(ret < 0)
hik_debug("read rootfs.cpio.gz failed\\n");
return -2;
setenv("update_flag","1");
s = getenv("bootcmd");
autoboot_command(s);
return 0;//never run to here
U_BOOT_CMD(
updatek, 2, 1, do_updatek,
"updatek kernel and rootfs from tftp server",
"updatek filename\\n"
);
最后我们需要在启动脚本中添加如下代码
for file in /sbin/hik_scripts/include/*
do
if [ -f $file ];then
echo include $file
source $file
fi
done
update_flag=$(cmdline_exist update_flag)
echo update_flag="$update_flag"
if [ "$update_flag" == "0" ];then
mkdir /mnt/update
cd /mnt/update;\\
tftp -g 192.168.27.129 -r digicap.tar
dd if=digicap.tar of=mmcblk0p2 bs=512 count=71680
reboot
fi
升级效果
以上是关于嵌入式Linux开发学习基于TFTP的通用代码烧写方式的主要内容,如果未能解决你的问题,请参考以下文章
嵌入式开发-迅为4412开发板学习笔记-TFTP服务器的搭建
Linux——iMX6ULL的启动过程详细解析(启动模式配置启动设备配置镜像烧写imx文件基本组成)
Linux——iMX6ULL的启动过程详细解析(启动模式配置启动设备配置镜像烧写imx文件基本组成)