tftp的作用

tftp是一种网络传输协议,称为简单文件传输系统。在嵌入式开发中经常使用tftp将bootloader、内核等从服务器下载到内存,然后从内存启动系统,或是将位于内存中的数据写入到flash等。在gboot中,我们将实现tftp协议,并且通过tftp协议,将内存下载到内存中,以便于最后从内存中启动整个系统。

[uboot] # tftp 0x50008000 zImage   #将zImage下载到内存的0x50008000处

tftp协议分析

实现tftp的前提是对tftp协议有着充分的认识,了解其封装格式及协议交互过程,对此我们通过wireshark抓包来完成。通过在宿主机上搭建tftp服务器,比如使用tftpd32,然后在开发板上使用uboot执行一次tftp下载,可以抓取到一次tftp的完整交互过程。

通过抓包可以知道,tftp协议使用UDP实现,这可以说是大大减轻了网络包的封装难度,毕竟UDP封装比TCP简单多了。tftp的封装如下所示:
Alt text

其交互过程如下:

  1. 客户端首先发送一个tftp请求,tftp请求的操作码是1,然后在tftp请求中指明要下载的文件名和下载模式。文件名和下载模式都用字符串来表示,下载模式一般是octet,表示用于传输文件。
  2. 服务器收到请求之后开始发送数据块,操作码是3,每个数据块包含2字节块编号,块编号从1开始。除了最后一块,其他块的长度都是512字节。
  3. 客户端每收到一个块,都要对该块进行响应,表示成功收到这个块。响应的操作码是4,还需要附加对哪个块进行响应。
  4. 以上过程重复进行,直到服务器发送完最后一个块,过程结束。

tftp实现

实现tftp协议的重点是完成tftp数据包的组装与解析。tftp协议使用UDP实现,这意味着需要自己组装以太网头,IP头,UDP头,然后才是用户数据,各个头的字段组成可以参考《TCP/IP详解 卷1:协议》,以下使用相关结构体来描述对应封装:

typedef struct eth_hdr
{
    u8 d_mac[6];
    u8 s_mac[6];
    u16 type;
}ETH_HDR;


typedef struct arp_hdr
{
    ETH_HDR ethhdr;
    u16 hwtype;
    u16 protocol;
    u8 hwlen;
    u8 protolen;
    u16 opcode;
    u8 smac[6];
    u8 sipaddr[4];
    u8 dmac[6];
    u8 dipaddr[4];
}ARP_HDR;

typedef struct ip_hdr
{
    ETH_HDR ethhdr;
    u8 vhl;
    u8 tos;
    u16 len;
    u16 ipid;
    u16 ipoffset;
    u8 ttl;
    u8 proto;
    u16 ipchksum;
    u8 srcipaddr[4];
    u8 destipaddr[4];
}IP_HDR;

typedef struct udp_hdr
{
    IP_HDR iphdr;
    u16 sport;
    u16 dport;
    u16 len;
    u16 udpchksum;
}UDP_HDR;

有了这些结构体之后,按照协议要求填充相关字段就可以了。

使用tftp下载内核

下面是使用tftp下载内核的流程图,按照流程图实现代码就可以了,但有一点要特别注意,即内核的下载地址。S3C6410的内核下载地址固定是0x50008000,所以下载地址不能变,而之前在编写gboot的链接脚本时,将gboot的链接地址也设置成了0x50008000,这样将导致gboot运行时,其代码也位于0x50008000处。因为下载过程中会不断往这个地址保存数据,这将导致gboot本身的代码也会被覆盖掉,这也是为什么之前下载会频繁死机的原因。顺便说一下,国嵌的视频由于使用TQ2440作演示,而TQ2440的bootloader运行于1MB的Norflash中,不会因为下载内核而覆盖掉,所以并没有出现问题。

Alt text

  • 无标签