嵌入式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的通用代码烧写方式的主要内容,如果未能解决你的问题,请参考以下文章

在Linux系统下通过TFTP或NFS烧写内核

嵌入式开发-迅为4412开发板学习笔记-TFTP服务器的搭建

Linux——iMX6ULL的启动过程详细解析(启动模式配置启动设备配置镜像烧写imx文件基本组成)

Linux——iMX6ULL的启动过程详细解析(启动模式配置启动设备配置镜像烧写imx文件基本组成)

Linux——iMX6ULL的启动过程详细解析(启动模式配置启动设备配置镜像烧写imx文件基本组成)

Linux学习笔记四:OTG方式为itop4412烧写安卓系统