引言:
几乎所有的研发到最后都会涉及到量产的问题,量产的时候就会遇到设备编号和网络MAC地址唯一性的问题.研发初期使用petalinux确实是非常的方便,虽然是很方便但是在某些情况下就显得不太灵活,比如说想自己修改一下u-boot,kernel什么的.我这里主要是希望每一台设备的MAC地址最好是独立的,不然N个设备在一个局域网里面路由器交换机就会变成一个傻子.起初的时候还是想过一些办法来解决MAC地址的问题,比如说使用PL那边的DNA,或者是外面挂一片eeprom来存储一个独立ID.查阅资料后听说PL的DNA有很大几率是重复的,所以首先就排除了这个方案,再说外挂一片eeprom,增加了成本不说还麻烦,得一片一篇的去编程.我是想找到一种类似于STM32那样,每一个芯片都有自己唯一的chip ID,但是ZYNQ确实没有.后来某交流群某群友提示说每一片SPI Flash都有一个唯一的ID,我就决定从这个下手...
==================================================================================================
正文:
首先的思路就是先从u-boot里面把flash里面的ID读出来,然后替换掉设备树里面的MAC地址的后三个字节,最后kernel启动的时候就可以加载新的MAC地址了...
然后接下来就是要修改u-boot的话,就不得不放弃掉petalinux这个东西了.这里我使用的是黑金提供的u-boot,一个是我不想自己去移植,还有一个就是传说要站在巨人的肩膀上才能那啥....(本篇文章里面所有的文件都会在最后贴上来)
闲话不多说,搞起来...
首先就是把u-boot解压出来,然后编译一下.这里我用的是黑金的AC7010核心板,本篇文章主要还是给提供一些思路,并不是希望大家都成为CV工程师.
make distclean
make zynq_ac7010_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
这里先编译一下的目的是为了我们能够更好的使用source insight阅读源码.然后这里我找到了一个脚本来生成si的文件列表(脚本我也会一并提供).
./PF_Prj_Gen.sh ../u-boot/u-boot-xlnx-xilinx-v2017.1/ u-boot
生成好的列表在当前目录下的u-boot文件夹里面.然后接下来创建si工程(这里提一下我是在虚拟机的ubuntu里面装了一个samba服务器,然后通过网络挂接到我的windows平台下的).
这里注意框里的路径需要手动添加一下
点击添加文件刚才生成的文件列表
点击同步一下工程
漫长的等待
在等待的时候,我们先把编译好的u-boot弄到SD卡里面运行一下.这里我还是使用的petalinux来打包..
先创建一个peatalinux工程:
petalinux-create -t project -n boot --template zynq
在后进入boot工程目录下创建 ./images/linux 文件夹
mkdir -p images/linux
然后把编译好的u-boot拷贝到images/linux/u-boot.elf(注意这里需要.elf的后缀),然后再随便拷贝一个fsbl和fpga的bit文件进去.然后使用petalinux打包生成boot.bin文件.
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/system_wrapper.bit --u-boot --force
接下来就把打包好的BOOT.bin文件拷贝到SD卡的fat分区里面,上电....
大概的意思就是没有识别到这个鬼东西....
然后复制一下 SF: unrecognized JEDEC id bytes 到si里面搜索...
在spi_flash.c里面搜索到有且只有这么一条打印.然后显然有一个比较应该就是在上面的for循环里面完成的,然后我们找到 spi_flash_ids 是在什么地方定义的.
然后发现貌似这里有个宏,只在zynq_ax***.h里面定义了,也就是说我用的ac7010里面没有.所以这里我把这个宏加上.重新编译打包之后拷贝进SD卡.
成功识别到了,接下来就是需要把flash的ID读出来...既然这里已经成功打印出了flash的信息,我们猜想一下对flash的操作应该的操作函数应该就可以在这句打印的附近,所以回到si搜索一下 SF: Detected
果不其然在spi_flash.c里面发现了一个非常可疑的函数 spi_flash_scan 并且在函数刚开始的地方发现了刚才找的读flash ID的函数:
那接下来的事情就简单了,我们只需要仿照 spi_flash_read_id 这个函数写一个读flash唯一id的函数就行了.打开flash的datasheet发现了这个....
实现函数:
/* patch by hlb start */
unsigned char hlb_qspi_flash_mac[8];
static int spi_flash_read_unique_id(struct spi_flash *flash, unsigned char* id)
{
int tmp;
unsigned char temp_id[12];
tmp = spi_flash_cmd(flash->spi, 0x4b, temp_id, 12);
if (tmp < 0) {
printf("SF: error %d reading Unique ID\n", tmp);
return -1;
}
memcpy(id, &temp_id[4], 8);
return 0;
}
/* patch by hlb end */
保存->编译->打包->下载->运行...
可以看到已经读出来了ID,并且打印出来了.接下来要做的就是替换一下设备树里面的MAC地址的后面三个字节就行了...于是问题就来了,应该在什么地方替换???应该在已经把设备树读取到内存里面之后的地方替换,那么新的问题又来了,设备树读取到内存里面之后的地方又再哪???
当时也是迷茫了很久,后来我也不知道是在做什么的时候灵光一闪......
接下来随便找个之前用petalinux生成好的设备树文件和黑金提供的uImage文件扔到SD卡里面上电...
发现这里非常可疑,打开si搜索 Loading Device Tree to
有且只有这么一句...猜想就是在这里之后的地方内存里面的设备树文件铁定是有效的.
跟着又发现了两条赋值语句和一个函数...
所以我决定在 set_working_fdt_addr 函数里面来实现替换功能...
接下来打磨一下这个函数:
/*
* The working_fdt points to our working flattened device tree.
*/
struct fdt_header *working_fdt;
/* patch by hlb start */
#define B2L(x) (((x & 0xff000000) >> 24) | ((x & 0xff0000) >> 8) | ((x & 0xff00) << 8) | ((x & 0xff) << 24))
unsigned int off_dt_struct; /* offset to structure */
unsigned int off_dt_strings; /* offset to strings */
unsigned int off_mem_rsvmap; /* offset to memory reserve map */
extern unsigned char hlb_qspi_flash_mac[];
/* patch by hlb end */
void set_working_fdt_addr(ulong addr)
{
void *buf;
/* patch by hlb start */
unsigned int temp;
unsigned int len;
unsigned int name_off;
unsigned char* str;
static int root_node = 0;
int mac_cnt = 0;
/* patch by hlb end */
buf = map_sysmem(addr, 0);
working_fdt = buf;
/* patch by hlb start*/
#if 1
printf("addr = 0x%08x\n", addr);
printf("buf = 0x%08x\n", buf);
#endif
off_dt_struct = B2L(working_fdt->off_dt_struct);
off_dt_strings = B2L(working_fdt->off_dt_strings);
off_mem_rsvmap = B2L(working_fdt->off_mem_rsvmap);
#if 1
printf("off_dt_struct = %d\n", off_dt_struct);
printf("off_dt_strings = %d\n", off_dt_strings);
printf("off_mem_rsvmap = %d\n", off_mem_rsvmap);
#endif
off_dt_struct += (unsigned int)working_fdt;
off_dt_strings += (unsigned int)working_fdt;
off_mem_rsvmap += (unsigned int)working_fdt;
do
{
temp = *((unsigned int*)off_dt_struct);
off_dt_struct += 4;
temp = B2L(temp);
switch (temp)
{
case 1:
if (root_node == 0) //处理根节点
{
root_node = 1;
off_dt_struct += 4; //根节点没有名字,跳过
}
else
{
/* node name */
str = (unsigned char*)off_dt_struct;
//printf("node name: %s\n", str);
int str_len;
str_len = strlen((const char*)str) + 1;
off_dt_struct += str_len;
if (str_len % 4)
{
off_dt_struct += 4 - (str_len % 4); //4字节对齐
}
}
break;
case 2:
break;
case 3:
/* value len */
temp = *((unsigned int*)off_dt_struct);
len = B2L(temp);
off_dt_struct += 4;
/* nameoff */
temp = *((unsigned int*)off_dt_struct);
name_off = B2L(temp);
str = (unsigned char*)(off_dt_strings + name_off);
off_dt_struct += 4;
if (strstr((char*)str, (const char*)"local-mac-address"))
{
unsigned char* pmac;
pmac = (unsigned char*)off_dt_struct;
#if 1
printf("old local-mac-address value len = %d\n", len);
for (int i = 0; i < 6; i++)
{
printf("0x%02x ", pmac[i]);
}
printf("\n");
#endif
pmac[0] = 0x00;
pmac[1] = 0x0a;
pmac[2] = 0x35 + mac_cnt++;
pmac[3] = hlb_qspi_flash_mac[5];
pmac[4] = hlb_qspi_flash_mac[6];
pmac[5] = hlb_qspi_flash_mac[7];
#if 1
printf("local-mac-address value len = %d\n", len);
for (int i = 0; i < 6; i++)
{
printf("0x%02x ", pmac[i]);
}
printf("\n");
#endif
}
off_dt_struct += len; //加上value长度偏移
if (len % 4)
{
off_dt_struct += 4 - (len % 4); //4字节对齐
}
break;
case 9:
off_dt_struct = 0;
break;
default:
break;
}
} while (off_dt_struct);
/* patch by hlb end */
setenv_hex("fdtaddr", addr);
}
由于篇幅较大,这里就不截图了,然后至于dtb的解析,这里也不在赘述,毕竟这篇文章不是讲设备树的,有兴趣的请自行google...
保存->编译->打包->下载->运行...
这样的话,应该就替换成功了吧...
结果....
啊这....
什么鬼,居然没有改变...真是难搞...
找到刚才设备树的peatalinux工程,打开里面的 system-conf.dtsi 文件=看一眼...
发现跟设备树里面的MAC地址也不一样...这个就很尴尬了...
分析一下多半是有个默认的MAC地址在作祟,突然想到了刚才那个配置文件 zynq_ac7010.h,打开一看...
果不其然...
陷入沉思...
继续猜想:u-boot里面是不是通过 ethaddr 这个字符串来获取的这里面的MAC地址...打开si搜索 ethaddr
发现这个函数...修正????...我特么让你修正了???
注释掉...
保存->编译->打包->下载->运行...
成功!!!
================================================================================================
后记:
本篇博客主要还是希望给大家提供一个思路,遇到问题的什么应该怎么去分析.笔者水平有限,如果有什么纰漏或者是不合理的地方,可以联系我,非常感谢!