tftp是一种网络传输协议,称为简单文件传输系统。在嵌入式开发中经常使用tftp将bootloader、内核等从服务器下载到内存,然后从内存启动系统,或是将位于内存中的数据写入到flash等。在gboot中,我们将实现tftp协议,并且通过tftp协议,将内存下载到内存中,以便于最后从内存中启动整个系统。
[uboot] # tftp 0x50008000 zImage #将zImage下载到内存的0x50008000处 |
实现tftp的前提是对tftp协议有着充分的认识,了解其封装格式及协议交互过程,对此我们通过wireshark抓包来完成。通过在宿主机上搭建tftp服务器,比如使用tftpd32,然后在开发板上使用uboot执行一次tftp下载,可以抓取到一次tftp的完整交互过程。
通过抓包可以知道,tftp协议使用UDP实现,这可以说是大大减轻了网络包的封装难度,毕竟UDP封装比TCP简单多了。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下载内核的流程图,按照流程图实现代码就可以了,但有一点要特别注意,即内核的下载地址。S3C6410的内核下载地址固定是0x50008000,所以下载地址不能变,而之前在编写gboot的链接脚本时,将gboot的链接地址也设置成了0x50008000,这样将导致gboot运行时,其代码也位于0x50008000处。因为下载过程中会不断往这个地址保存数据,这将导致gboot本身的代码也会被覆盖掉,这也是为什么之前下载会频繁死机的原因。顺便说一下,国嵌的视频由于使用TQ2440作演示,而TQ2440的bootloader运行于1MB的Norflash中,不会因为下载内核而覆盖掉,所以并没有出现问题。