打印
[应用相关]

u-boot网络固件还原功能

[复制链接]
1378|46
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、任务简介:在UBOOT中使用UDP协议与服务器实现通讯,并获取服务器的特定配置文件并执行其中的命令,从而应用于生产辅助、进行维护、升级等等。

使用特权

评论回复
沙发
xiaoqizi|  楼主 | 2019-7-9 10:46 | 只看该作者
二、流程:

使用特权

评论回复
板凳
xiaoqizi|  楼主 | 2019-7-9 10:46 | 只看该作者
三、注意几点:

1、INI文件格式(b开头的命令不是致命命令,执行不成功会跳到下一条命令),下面是一个例子:

::this is a inifile
s00=setenv serverip 192.168.1.68
s01=setenv ipaddr 192.168.1.10
s02=sa
s10=sf probe 0
s11=sf read 0x84800000 0xc0000 0x20000
s12=sf read 0x84900000 0xe0000 0x20000
s13=**
s20=sf probe 0
s21=sf read 0x82000000 0x180000 0x280000
s22=sf read 0x81000000 0x400000 0x800000
b23=bootm 0x82000000 0x81000000
s30=bzr 200 1
s31=bzr 55 2
t32=tftp 0x83000000 FWHI2104HF_20151104_DVR_R5104-AHD_2_2_7_2_413221.flash
s40=sf probe 0
s41=sf erase 0x80000 0xF80000
s42=sf write 0x83080000 0x80000 0xF80000
s50=bzr 55 2
s51=bzr 500 1
s52=bzr 500 3
s53=reset


使用特权

评论回复
地板
xiaoqizi|  楼主 | 2019-7-9 10:47 | 只看该作者
2、配置文件大小不要超过设定的字节数

3、为了不使每次开机慢,udp的处理尽量要快点,udp主要要处理两点:

1)网络不在(没插网线):重连4次,间隔大约1秒,每次分别响应蜂鸣器1、2、3、4次

2)服务器不在(或者传输过程中服务器不在了):重发5次之后重启net(蜂鸣器响一声,重启3次),历时大约10秒



4、由于命令集中可能包含tftp命令,而tftp命令的使用工程中可能发生很多意外,所以要对tftp命令进一步的完善处理,以下是对一些情况的处理方式:
1)网络不在(没插网线):重连4次,间隔大约1秒,每次分别响应蜂鸣器1、2、3、4次
2)服务器不在(或者传输过程中服务器不在了):1秒重发一次,10次之后重启net,重启3次。重启时蜂 鸣器响一声。
3)在传输过程中网络中断:1秒会重发一次,重发10次之后会重启网络,重启之后再重发10次,重启3次 。重启时蜂鸣器响一声。
4)其他情况:
找不到文件或发完ACK包服务器没回应:默认方式处理

使用特权

评论回复
5
xiaoqizi|  楼主 | 2019-7-9 10:47 | 只看该作者
四、实现步骤:

 1、添加一条查询服务器的命令和发送UDP包的命令

udp包中的数据是不同型号板子的信息,做法是在输出文件(u-boot.bin)中定义一个16字节的message段,用来存放板子信息,不能定义过大的空间以免覆盖其他重要信息。

修改/u-boot-2010.06/arch/arm/cpu/hi3520d/u-boot.lds文件:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
        .text        :
        {
                arch/arm/cpu/hi3520d/start.o        (.text)
                *(.text)
        }

        . = ALIGN(4);
        .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

        . = ALIGN(4);
        .data : { *(.data) }

        . = ALIGN(4);
        .got : { *(.got) }

        . = ALIGN(4);
        __setting_message_start = 0x90;                    //在偏移量为0x90(144字节)位置开始是message段
        .message :
        {  
                arch/arm/cpu/hi3520d/hi3520d_message.o        (.message)      //此段的入口是hi3520d_message.S

        }
        __setting_message_end = 0xA0;                     //在偏移量为0xA0(160字节)位置是message段的结束,一共16字节
       
       
        __u_boot_cmd_start = .;
        .u_boot_cmd : { *(.u_boot_cmd) }
        __u_boot_cmd_end = .;

        . = ALIGN(4);
        __bss_start = .;
        .bss : { *(.bss) }
        _end = .;

}

使用特权

评论回复
6
xiaoqizi|  楼主 | 2019-7-9 10:47 | 只看该作者
hi3520d_message.S:



.section .message,#alloc

.globl        _setting_message_start       
_setting_message_start:
.word __setting_message_start

.globl    _setting_message_end
_setting_message_end:
.word __setting_message_end

使用特权

评论回复
7
xiaoqizi|  楼主 | 2019-7-9 10:47 | 只看该作者
然后在需要访问的地方声明即可:


<span style="white-space:pre">        </span>extern ulong _setting_message_start;
        extern ulong _setting_message_end;
        printf("_setting_env_start=%08lX\n", _setting_env_start);
        printf("_setting_env_end=%08lX\n", _setting_env_end);
        ulong len = _setting_env_end - _setting_env_start;

使用特权

评论回复
8
xiaoqizi|  楼主 | 2019-7-9 10:48 | 只看该作者
验证是否已经定义到此段的方法是修改此段的内容然后再打印此段的内容看是否一致:

可以通过windwHEX软件或打开u-boot.bin二进制文件修改此段的数据,下面是后者的代码:


#include <stdio.h>
#include <errno.h>

int main(int argv, char *argc[])
{
        FILE *fp;
        char *str = "abcdefghijklmnop";
        fp = fopen("/nfs/opt/U-BOOT/u-boot/u-boot-2010.06/u-boot.bin","rb+");    //以可读写的方式打开一个二进制文件

        if( fp == NULL){
                printf("open failure!\n");
                fclose(fp);
        }
        printf("open succeed\n");
        fseek(fp , 144, SEEK_SET);                       //将光标移到第144字节处即0x90处

        if(fwrite(str, sizeof(char), 16, fp) != 16){     //写16字节,成功返回16
                printf("write failure\n");
                fclose(fp);
        }
        printf("write succeed!\n");
        fclose(fp);

        return 0;
}

使用特权

评论回复
9
xiaoqizi|  楼主 | 2019-7-9 10:48 | 只看该作者
uboot启动时cpu会把flash里面的uboot拿到ddr相对应的地址(TEXT_BASE)开始执行,这个TEXT_BASE在u-boot/u-boot-2010.06/board/hi3520d/config.mk中定义,不同型号可能不一样,hi3520d定义的TEXT_BASE=0x80800000。
所以在uboot跑的时候可以在对应的地址(TEXT_BASE+段的地址偏移量),就是说可以在0x80800090处获取message段的数据:

char *addr;
char *file_address;
file_address = (char *)simple_strtoul("0x80800090", NULL, 16);
addr = file_address;
memgetinfo(mes, MAX_DATA, &addr, &file_size);           //此函数是从特定地址读取特定大小数据
printf("info = %s\n", mes);

使用特权

评论回复
10
xiaoqizi|  楼主 | 2019-7-9 10:48 | 只看该作者
 2、完善tftp命令,主要是超时处理

 3、创建一些有关板子信息的环境变量

 4、区别FLASH的分区:

一般flash大小是16M:

Hi3521 DDR-SDRAM Momery Mapping
BLOCK 1 (DDRA 0x80000000~0x8FFFFFFF, 256MB Total, 32Bit Width)
0x8000 0000 - - | - - +0x0480 0000  (72MB) Linux OS
0x8480 0000 - - | - - +0x1B80 0000 (440MB) MMZ0
Hi3521 SPI Flash Mapping For JUAN (0x5800 0000~0x58FF FFFF,16MB Total)
Base   Address: 0x5800 0000
OffSet Address:
0x0000 0000 - - | - - +0x008 0000 (512KB)  U-boot Master
0x0008 0000 - - | - - +0x004 0000 (256KB)  U-boot Master Env
0x000C 0000 - - | - - +0x004 0000 (256KB)  U-boot **
0x0010 0000 - - | - - +0x008 0000 (512KB)  App Config
0x0018 0000 - - | - - +0x028 0000 (2.5MB)  Kernel Master
0x0040 0000 - - | - - +0x0C0 0000 (12MB)   Rootfs Master

0~0x80000是UBOOT(512k),0x80000~0xc0000是U-boot Environment(256k),0xc0000~0x100000是U-boot **(256k),0x100000~0x180000是App Config(512k),0x180000~0x400000是 Kernel Binary Image(2.5M),0x400000~0x1000000是Rootfs Binary Image(12M)。

使用特权

评论回复
11
xiaoqizi|  楼主 | 2019-7-9 10:48 | 只看该作者
但是hi3520dv300、hi3521a、hi3531a有所改变:


Hi3521A DDR-SDRAM Momery Mapping
BLOCK 1 (DDRA 0x80000000~0xFFFFFFFF, 2GB Total, 32Bit Width)
0x8000 0000 - - | - - +0x0480 0000  (72MB) Linux OS
0x8480 0000 - - | - - +0x1B80 0000 (440MB) MMZ0
0xC000 0000 - - | - - +0x2000 0000 (512MB) MMZ1

Hi3520Dv300 & Hi3521A & Hi3531A  SPI Flash Mapping For JUAN
OffSet Address:
0x0000 0000 - - | - - +0x005 0000 (320KB)  U-boot Binary Image(Last 64KB Size for S/N)
0x0005 0000 - - | - - +0x001 0000 (64KB)   U-boot Environment
0x0006 0000 - - | - - +0x002 0000 (128KB)  U-boot **
0x0008 0000 - - | - - +0x006 0000 (384KB)   App Config(192KB AppEnv /64KB User Management/128KB ODM)
0x000E 0000 - - | - - +0x028 0000 (2.5MB)        Kernel Binary Image
0x0036 0000 - - | - - +0x0CA 0000 (12MB+640KB)   Rootfs Binary Image(Last 4MB Size for Resource)

使用特权

评论回复
12
xiaoqizi|  楼主 | 2019-7-9 10:49 | 只看该作者
5、 在PC端写一个UDP服务器

/ udp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<stdio.h>
#include<string.h>
#include<Winsock2.h>

#pragma comment(lib, "ws2_32.lib")

typedef struct {
                char*       flag;
                char*       client_ip;
                int         client_port;
                char*                board;                // model       
                int                    flash;                //  flash size
                char*       cpu;
} Data_t;

使用特权

评论回复
13
xiaoqizi|  楼主 | 2019-7-9 10:49 | 只看该作者
char *find_char_or_comment(const char *s, char c)
{
        int was_whitespace = 0;

        while (*s && *s != c && !(was_whitespace && *s == ';')) {
                was_whitespace = isspace(*s);
                s++;
        }
        return (char *)s;
}

使用特权

评论回复
14
xiaoqizi|  楼主 | 2019-7-9 10:49 | 只看该作者
/* Strip whitespace chars off end of given string, in place. Return s. */
static char *rstrip(char *s)
{
        char *p = s + strlen(s);

        while (p > s && isspace(*--p))
                *p = '\0';
        return s;
}

使用特权

评论回复
15
xiaoqizi|  楼主 | 2019-7-9 10:50 | 只看该作者
/* Return pointer to first non-whitespace char in given string. */
static char *lskip(const char *s)
{
        while (*s && isspace(*s))
                s++;
        return (char *)s;
}

使用特权

评论回复
16
xiaoqizi|  楼主 | 2019-7-9 10:50 | 只看该作者
int main(int argc, char* argv[])
{
        Data_t data;
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2,2);
    if(WSAStartup(sockVersion, &wsaData) != 0)
    {
        return 0;
    }

    SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(serSocket == INVALID_SOCKET)
    {
        printf("socket error !");
        return 0;
    }

    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(75);
    serAddr.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("bind error !");
        closesocket(serSocket);
        return 0;
    }
   
    sockaddr_in remoteAddr;
    int nAddrLen = sizeof(remoteAddr);
        printf("Listening ....\n");
    while (true)
    {
        char recvData[500];
                char sendData[1000];
                char *buf[50];
                int allByte=0;
                int line=0;
                char *start;
                char *end;
                int i, count;

        int ret = recvfrom(serSocket, recvData,500, 0, (sockaddr *)&remoteAddr, &nAddrLen);
        if (ret > 0)
        {
            recvData[ret] = 0x00;
            printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
            //printf("%s\n",recvData);
                        start = lskip(rstrip(recvData));

                        for(i=0; i<50; i++){
                                end = find_char_or_comment(start, '/');
                                if ( *end == '/' && *(++end)) {
                                        *(--end) = '\0';
                                        buf[i] = rstrip(start);
                                        start = lskip(end + 1);
                                }
                                else{
                                        buf[i] = rstrip(start);
                                        break;
                                }
                        }
                        printf(" %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n",
                                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
                        end = find_char_or_comment(buf[0], '=');
                        buf[0] = lskip(end + 1);
                        end = find_char_or_comment(buf[1], '=');
                        buf[1] = lskip(end + 1);
                        if (!strcmp(buf[0], "ini")){
                                FILE *fp;
                                if(!strcmp(buf[1], "hi3520d")){
                                        fp = fopen("E:\\uboot_env_3520D_256m-hf.ini","rb");
                                }
                                else if(!strcmp(buf[1], "hi3521a")){
                                        fp = fopen("E:\\uboot_env_3521A_256m-hf.ini","rb");
                                }
                                if(!fp) {
                                        printf("open error!");
                                        return 1;
                                }
                                else
                                        printf("文件已经打开,等待传输...\n");
                               
                                while(!feof(fp))
                                {       
                                        memset(sendData, 0, 1000);
                                        int numreads=fread(sendData, 1, 1000, fp);
                                        int sByte = sendto(serSocket, sendData, numreads, 0, (sockaddr*)&remoteAddr, sizeof(remoteAddr));
                                        allByte+=sByte;
                                        line++;
                                        if(SOCKET_ERROR==sByte)
                                        {
                                                printf("sendto()Failed:%d\n",WSAGetLastError());
                                                closesocket(serSocket);
                                                WSACleanup();
                                                return -1;
                                        }               
                                }
                                printf("传输完毕,总共字节=%d,次数=%d\n",allByte,line);
                        }
                        else if(buf[0] == "xml"){                       
                        }
                        else if(buf[0] == "json"){                       
                        }
                       
        }
       // char * sendData = "一个来自服务端的UDP数据包\n";
        //sendto(serSocket, sendData, strlen(sendData), 0, (sockaddr *)&remoteAddr, nAddrLen);   
    }
    closesocket(serSocket);
    WSACleanup();
    return 0;
}

使用特权

评论回复
17
xiaoqizi|  楼主 | 2019-7-9 10:50 | 只看该作者
五、代码实现


1、在cmd_net.c 中添加udp命令和query命令:


int do_udp (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
        return netboot_UDP_TCP (UDP, cmdtp, argc, argv);
}

U_BOOT_CMD(
        udp,        6,        1,        do_udp,
        "send or receive message to/from server using UDP protocol",
        "[udp] [string] or [udp] <eth> <IP> [string]"
);

使用特权

评论回复
18
xiaoqizi|  楼主 | 2019-7-9 10:51 | 只看该作者
static int netboot_UDP_TCP (proto_t proto, cmd_tbl_t *cmdtp, int argc, char *argv[])
{
       
        char *s;
        int   size;

        switch (argc) {
        case 1:
                pkt_data = NULL;
                break;
        case 2:       
                pkt_data = argv[1];
                break;
        default:
                cmd_usage(cmdtp);
                show_boot_progress (-80);
                return 1;
        }
        show_boot_progress (80);
        if ((size = NetLoop(proto)) < 0) {
                show_boot_progress (-81);
                return 1;
        }
        show_boot_progress (81);
        /* NetLoop ok, update environment */
        netboot_update_env();
        /* done if no file was loaded (no errors though) */
        if (size == 0) {
                show_boot_progress (-82);
                return 0;
        }
       
        return 0;
}

使用特权

评论回复
19
xiaoqizi|  楼主 | 2019-7-9 10:51 | 只看该作者

#define MAX_DATA 1000

typedef struct {
                char                filetype[25];       
                char                  cpu_model[25];
                char                 flash_size[20];
                char                 flash_type[10];
                char             dram_size[25];
                char            serial_number[25];
                char                ouripaddr[25];
                char              ourport[10];
} Data_t;

使用特权

评论回复
20
xiaoqizi|  楼主 | 2019-7-9 10:51 | 只看该作者
int do_query (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
        struct mtd_info_ex *spiinfo;
        static unsigned int ddr_size;
        Data_t info;
        char buf[MAX_DATA];
        int i;
        char *s;
       
        switch (argc) {
        case 1:
                strcpy(info.filetype, "ini");
                break;
        case 2:
                strcpy(info.filetype, argv[1]);
                break;
        default:
                cmd_usage(cmdtp);       
                return 1;       
        }

        strcpy(info.cpu_model, CONFIG_PRODUCTNAME);
       
        spiinfo = get_spiflash_info();
        memset(info.flash_size, 0, 20);
        sprintf(info.flash_size, "%sB", ultohstr(spiinfo->chipsize));
       
        if(strcmp(info.flash_size, getenv("flashsize")) != 0){
                setenv("flashsize",info.flash_size);
        }
        switch(spiinfo->type){
                case MTD_NORFLASH:
                        strcpy(info.flash_type, "norflash");
                        break;
                case MTD_NANDFLASH:
                        strcpy(info.flash_type, "nandflash");
                        break;
                case MTD_DATAFLASH:
                        strcpy(info.flash_type, "dataflash");
                        break;
                default:
                        strcpy(info.flash_type, "norflash");
                        break;
        }
        if(strcmp(info.flash_type, getenv("flashtype")) != 0){
                setenv("flashtype",info.flash_type);
        }
       
        strcpy(info.dram_size, "0");
        strcpy(info.serial_number, "0");
        strcpy(info.ouripaddr, getenv("ipaddr")? getenv("ipaddr") : "192.168.1.114");
        strcpy(info.ourport, getenv("ourport")? getenv("ourport") : 1024);

        sprintf(buf,"udp filetype=%s/cpu_model=%s/flash_type=%s/flash_size=%s/dram_size=%s/serial_number=%s/ouripaddr=%s/ourport=%s\n",
                info.filetype, info.cpu_model,  info.flash_type, info.flash_size,
                info.dram_size, info.serial_number, info.ouripaddr, info.ourport);
       
        if(run_command(buf, 0) == -1){       
                if ( ctrlc() ) {
                        return 0;
                }
                printf("error, to upgrade the abnormal\n");
        }
        return 0;
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

4090

帖子

3

粉丝