Uboot 网卡移植遇到的问题与解决方法

前言

在上一章节中描述的两个问题,这里做下简单回顾。

情况一

在运行到接收以太帧的时候,出现了data abrot异常,如下:

data abort
pc : [<7fe9a2a0>]	   lr : [<7aede325>]
reloc pc : [<43e432a0>]	   lr : [<3ee87325>]
sp : 7ae54ce0  ip : 00000014	 fp : 00000fff
r10: 00000fff  r9 : 7ae54ed8	 r8 : 0000002e
r7 : 00000fff  r6 : 7aede303	 r5 : 0000001c  r4 : 7aede311
r3 : 00000000  r2 : 7aede311	 r1 : 00000014  r0 : 7aede311
Flags: nzCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

情况二

在解决完上面问题后,又出现了启动linux内核的时候,出现校验失败的问题,如下:

Verifying Checksum ... Bad Data CRC
 ERROR: can't get kernel image!

情况一分析

异常分析

首先我们需要根据uboot中打印的出错信息,分析出异常地址,根据ARM ABI调用规则,PC寄存器保存当前运行指针,通过objdump生成的u-boot(elf文件),便可查找到出现异常的原因。

  • PC指针

根据上面所述,发现reloc pc的值为重定位前的值,即elf文件对应的地址。上面的日志中为reloc pc : [<43e432a0>]

  • 反汇编

通过arm-linux-gnueabihf-objdump -D -S u-bot > dump.log,然后搜索改文件中的43e432a0地址对应的指令。

116932 unsigned compute_ip_checksum(const void *vptr, unsigned nbytes)
116933 {
116934 43e43240:   e92d4007    push    {r0, r1, r2, lr}
116935 43e43244:   e080e001    add lr, r0, r1
116936     int sum, oddbyte;
116937     const unsigned short *ptr = vptr;
116938 43e43248:   e1a02000    mov r2, r0
116939 
116940     sum = 0;
116941 43e4324c:   e3a03000    mov r3, #0
116942     while (nbytes > 1) {
116943 43e43250:   e04ec002    sub ip, lr, r2
116944 43e43254:   e35c0001    cmp ip, #1
116945 43e43258:   8a000010    bhi 43e432a0 <compute_ip_checksum+0x60>
116946 43e4325c:   e1a0c0a1    lsr ip, r1, #1
116947         sum += *ptr++;
116948         nbytes -= 2;
116949     }
...	// 此处省略若干行
116975 43e43294:   e6ff0070    uxth    r0, r0
116976 43e43298:   e28dd00c    add sp, sp, #12
116977 43e4329c:   e49df004    pop {pc}        ; (ldr pc, [sp], #4)
116978         sum += *ptr++;
// 此处为出异常处对应的指令地址
116979 43e432a0:   e0d2c0b2    ldrh    ip, [r2], #2
116980 43e432a4:   e083300c    add r3, r3, ip
116981 43e432a8:   eaffffe8    b   43e43250 <compute_ip_checksum+0x10>
116982 
116983 43e432ac <ip_checksum_ok>:
116984 
116985     return (~checksum) & 0xffff;
116986 }
116987 

原因分析

通过上述操作,我们发现导致异常的指令为43e432a0: e0d2c0b2 ldrh ip, [r2], #2

  • 发现该指令为LDRH读取R2所指向的字内容到IP,然后R2+=2
  • 发现R2的值为7aede311
  • 最终原因为LDRH读取了未字对齐的地址数据
  • 通过临时的pkt buffer,来临时拷贝接收到的USB帧数据,然后拷贝到预先分配的rxbuf中,这样就解决了因对齐导致的数据异常问题。

解决办法如下:

+int dm9601_eth_recv(struct udevice *udev, int flags, uchar **packetp)
+{
...
+    ALLOC_CACHE_ALIGN_BUFFER(unsigned char, pkt, PKTSIZE);
...
+
+    debug("---> packet_len = %d, len = %d\n", packet_len, len);
+    memcpy(pkt, ptr + 3, packet_len);   /* 3 bytes header */
+    memcpy(ptr, pkt, packet_len);       /* copy to dev->rxbuf
...
+}
+

情况二分析

仔细查阅数据crc校验出错的原因,查阅uImage的头,发现读取到内存的64字节uboot的header完全和文件一模一样,那么就只会有数据不一样才会出现这个错误问题。

  • 读取到的BUF数据和文件中是否一致性
  • 查看BUF分配方式为ALLOC_CACHE_ALIGN_BUFFER
   70 #define PAD_COUNT(s, pad) (((s) - 1) / (pad) + 1)
   71 #define PAD_SIZE(s, pad) (PAD_COUNT(s, pad) * pad)
   72 #define ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad)        \
   73     char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align)  \
   74               + (align - 1)];                   \
   75                                     \
   76     type *name = (type *)ALIGN((uintptr_t)__##name, align)
   77 #define ALLOC_ALIGN_BUFFER(type, name, size, align)     \
   78     ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
   79 #define ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad)     \
   80     ALLOC_ALIGN_BUFFER_PAD(type, name, size, ARCH_DMA_MINALIGN, pad)
   81 #define ALLOC_CACHE_ALIGN_BUFFER(type, name, size)          \
   82     ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
  • 可见该内存分配方式其实为一局部变量,76行返回宏的值为type * name,即__name结尾的数组首地址
  73     char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align)  \
  74               + (align - 1)];                   \
  75                                     \
  76     type *name = (type *)ALIGN((uintptr_t)__##name, align)
  • 最终给net栈处理的竟然是局部变量的地址?难怪会出问题!

解决办法如下:

+int dm9601_eth_recv(struct udevice *udev, int flags, uchar **packetp)
+{
...
+    ALLOC_CACHE_ALIGN_BUFFER(unsigned char, pkt, PKTSIZE);
...
+    memcpy(ptr, pkt, packet_len);       /* copy to dev->rxbuf */
+
+    /*
+     * MUST RETURN ALIGNED MEMORY, because checksum use LDRH !!!
+     * DO NOT return `ptr` allocated by ALLOC_CACHE_ALIGN_BUFFER,
+     * which expand to an array on stack.
+     * Here ptr --> dev->rxbuf (not array on stack).
+     */
+    *packetp = ptr;
+    return packet_len;
...
+}
+

注:ptr为分配的rxbuf地址,非栈上空间。

总结

在解决上述问题后,网卡移植便顺利的结束了,这里只简单的介绍了解决思路与办法,其实博主遇到的时候也差了不少资料,走了不少弯路,甚至还用Wireshark抓包对比,也增加了调试用的static void dump_msg(uint8_t *ptr, int len)接口来实现对交互数据的跟踪。遇到问题的时候不妨多想一下可能导致该问题的相关因素,在利用合适的调试方法,便可以解决绝大部分问题;当然还是解决不了的,只有找大神帮忙咯。

你可能感兴趣的:(Uboot,uboot,USB网卡)