在上一章节中描述的两个问题,这里做下简单回顾。
情况一
在运行到接收以太帧的时候,出现了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文件),便可查找到出现异常的原因。
根据上面所述,发现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
。
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完全和文件一模一样,那么就只会有数据不一样才会出现这个错误问题。
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)
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)
解决办法如下:
+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)
接口来实现对交互数据的跟踪。遇到问题的时候不妨多想一下可能导致该问题的相关因素,在利用合适的调试方法,便可以解决绝大部分问题;当然还是解决不了的,只有找大神帮忙咯。