ZYNQ通过读取SPI Flash的唯一ID来做MAC地址使用

引言:

几乎所有的研发到最后都会涉及到量产的问题,量产的时候就会遇到设备编号和网络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 

发现这个函数...修正????...我特么让你修正了???

注释掉...

保存->编译->打包->下载->运行...

成功!!!

================================================================================================

后记:

本篇博客主要还是希望给大家提供一个思路,遇到问题的什么应该怎么去分析.笔者水平有限,如果有什么纰漏或者是不合理的地方,可以联系我,非常感谢!

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值