vxworks issue: dtb overwrite

1. 问题现象

在调试 Am5728 + vxworks时,发现在dts中使能完第二个pcie控制器以后,会造成cpsw网卡注册失败。

am572x_idk_ca15.dts加入pcie2控制器:

        pcie2: pcie@30000000
            {
            compatible = "ti,am572x-pcie";
            reg =   <0x51800000 0x2000>,
                    <0x51802000 0x2000>,
                    <0x4A002000 0x2000>,
                    <0x4AE07300 0x2000>,
                    <0x30010000 0x80000>;
            ...

cpsw网卡注册失败:

ERROR: ipcom_drv_eth_init: drvname:cpsw, drvunit: 0
ERROR: ipcom_drv_eth_init: drvname:cpsw, drvunit: 1

2. 分析过程

Step 1. 排查网卡驱动(vxbFdtTiCpswEnd.c)

打开网卡驱动的调试开关:

diff --git a/vxbFdtTiCpswEnd.c b/vxbFdtTiCpswEnd.c
index 71afa1a..909797e 100644
--- a/vxbFdtTiCpswEnd.c
+++ b/vxbFdtTiCpswEnd.c
@@ -192,6 +192,7 @@ SEE ALSO: VxBus, miiBus, ifLib
 /* debug macro */

 #undef CPSW_DEBUG
+#define CPSW_DEBUG
 #ifdef CPSW_DEBUG
 #include <private/kwriteLibP.h>         /* _func_kprintf */

查看具体的出错信息:

cpswSwCtrlAttach,606, cpsw-hdp-offset = 0x00000a00
cpswSwCtrlAttach,616, cpsw-ale-offset = 0x00000d00
cpswSwCtrlAttach,627, cpsw-mdio-offset = 0x00001000
cpswSwCtrlAttach,637, cpsw-wr-offset = 0x00001200
cpswPortAttach,1977, cpsw0 did't find cpsw-gmac-offset property
cpswPortAttach,1977, cpsw1 did't find cpsw-gmac-offset property

发现出错的原因是找不到cpsw dts节点中的cpsw-stats-offset属性。

在am572x_idk_ca15.dts中cpsw device的定义如下:

            cpsw_port1: port@1             /* port device*/
                {
                #address-cells = <1>;
                #size-cells    = <0>;
                device_type    = "network";
                compatible     = "ti,cpsw-port";

                interrupts = <147 0 4>,    /* c0_rx_pend */
                             <148 0 4>,    /* c0_tx_pend */
                             <149 0 4>;    /* c0_misc_pend */

                cpsw-port-index  = <1>;
                cpsw-gmac-offset = <0xd80>;
                cpsw-port-offset = <0x208>;
                ...

总结:读取dts配置出错。

Step 2. 排查dtb文件内容

在pcie2 controller加入dts之前,cpsw驱动读取cpsw-stats-offset属性是没有问题的,会不会是pcie2 controller导入了一些语法错误,导致编译出来的dtb文件中cpsw-stats-offset属性出错?

我们直接用二进制编辑器来查看am572x_idk_ca15.dtb文件中的头部结构fdt_header
在这里插入图片描述

解析出来的含义如下:
在这里插入图片描述
接下来参考下图dtb中node和property的组织形式:
在这里插入图片描述
找到am572x_idk_ca15.dtb文件中port@1设备节点的cpsw-stats-offset属性:
在这里插入图片描述
我们再来看看cpsw-stats-offset属性string的情况,string存储的位置 = off_dt_strings + property_string_offset = 0xFD8C + 0x367 = 0x100F3:
在这里插入图片描述
属性string也是正确的。

总结:dtb文件内容没有错误。

Step 3. 排查vxworks内存中dtb内容

既然编译后的dtb文件内容没有问题,会不会是拷贝到内存中使用的时候出错了呢?

在VxWorks中,dtb的存储位置为0x10000 到 0xFFE00:

DTB_RELOC_ADDR      : LOCAL_MEM_LOCAL_ADDR + 0x10000 = 0x10000
DTB_MAX_LEN         : RAM_LOW_ADRS - STACK_SAVE- DTB_RELOC_ADDR

LOCAL_MEM_LOCAL_ADDR: 0x0
RAM_LOW_ADRS        : 0x100000
STACK_SAVE          : 512

我们使用vxworks下的d命令读出所有dtb内容,按照“step 2”的检查方法,重新检查一遍。

这次检查果然发现了异常,cpsw-stats-offset属性string的位置,内容被改写了:

-> d 0x200f0,100,4
NOTE: memory values are displayed in hexadecimal.
0x000200f0:  63007865 2d777370 63616d67 66666f2d  *ex.cpsw-gmac-off*
0x00020100:  001e4018 001e48e4 001e4008 001e3e0c  *[email protected]...@...>..*
0x00020110:  74657366 001e4330 616d2d77 61732d63  *fset0C..w-mac-sa*
0x00020120:  772d6576 6c007961 6c61636f 63616d2d  *ve-way.local-mac*

因为dtb在内存中偏移是0x10000,string存储位置0x100F3就对应0x200F3,和dtb文件的原始内容比较后可以看到0x20100开始的一行内容都被改写了。

总结:cpsw-stats-offset属性的名称字符串被改写了。

Step 4. 排查uboot拷贝过程

内存中dtb的内容被改写,那么这个改写发生在哪个阶段呢?

dtb首先会被uboot拷贝到内存中,然后把地址传递给vxworks内核,vxworks会再重新拷贝到自己规划的内存位置,再运行程序来使用。

我们逐个排查这个过程中的每个环节,首先从第一环节uboot拷贝开始。

我们在启动过程中会看到uboot拷贝内核和dtb的打印信息:

=> load mmc 0:1 0x90000000 uVxWorks;load mmc 0:1 0xa0000000 am572x_idk_ca15.dtb;bootm 0x90000000 - 0xa0000000;
reading uVxWorks
4076640 bytes read in 203 ms (19.2 MiB/s)
reading am572x_idk_ca15.dtb
66056 bytes read in 8 ms (7.9 MiB/s)
## Booting kernel from Legacy Image at 90000000 ...
   Image Name:   vxWorks
   Image Type:   ARM VxWorks Kernel Image (uncompressed)
   Data Size:    4076576 Bytes = 3.9 MiB
   Load Address: 84100000
   Entry Point:  84100000
   Verifying Checksum ... OK
## Flattened Device Tree blob at a0000000
   Booting using the fdt blob at 0xa0000000
   Loading Kernel Image ... OK
   Using Device Tree in place at a0000000, end a0013207
## Starting vxWorks at 0x84100000, device tree at 0xa0000000 ...

Am5728的内存为2G,物理地址为0x80000000 - 0xFFFFFFFF。

在uboot的开始阶段,我们把uVxWorks加载到内存0x90000000,把am572x_idk_ca15.dtb加载内存0xa0000000。在uboot阶段内存都是物理地址:

=> load mmc 0:1 0x90000000 uVxWorks;load mmc 0:1 0xa0000000 am572x_idk_ca15.dtb;

随后的bootm命令阶段,会校验和根据配置加载到对应位置。

1、首先bootm会把uVxWorks从内存0x90000000加载到0x84100000,vxworks在编译映像的时候使用mkimage指定了这些信息:

VX_CPU_FAMILY=arm D:/WindRiver/vxworks-7/host/x86-win32/bin/mkimage -O vxworks -T kernel -C none -n vxworks -A arm -a 84100000 -e 84100000 -d vxWorks.bin uVxWorks
Image Name:   vxWorks
Created:      Tue Aug 20 17:11:32 2019
Image Type:   ARM VxWorks Kernel Image (uncompressed)
Data Size:    4076576 Bytes = 3981.03 kB = 3.89 MB
Load Address: 84100000
Entry Point:  84100000

2、随后bootm会校验内存0xa0000000处的dtb内容,根据需要判断是否需要重新relocation。因为这里的dtb文件不需要relocation,所以处理完成以后还是0xa0000000地址。

Six_axis_robot_uboot\common\image-fdt.c:

int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
{
	void	*fdt_blob = *of_flat_tree;
	void	*of_start = NULL;
	char	*fdt_high;
	ulong	of_len = 0;
	int	err;
	int	disable_relocation = 0;

	/* nothing to do */
	if (*of_size == 0)
		return 0;

	if (fdt_check_header(fdt_blob) != 0) {
		fdt_error("image is not a fdt");
		goto error;
	}

	/* position on a 4K boundary before the alloc_current */
	/* Pad the FDT by a specified amount */
	of_len = *of_size + CONFIG_SYS_FDT_PAD;

	/* If fdt_high is set use it to select the relocation address */
	fdt_high = getenv("fdt_high");
	if (fdt_high) {
		void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16);

		if (((ulong) desired_addr) == ~0UL) {
			/* All ones means use fdt in place */
			of_start = fdt_blob;
			lmb_reserve(lmb, (ulong)of_start, of_len);
			disable_relocation = 1;
		} else if (desired_addr) {
			of_start =
			    (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
							   (ulong)desired_addr);
			if (of_start == NULL) {
				puts("Failed using fdt_high value for Device Tree");
				goto error;
			}
		} else {
			of_start =
			    (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000);
		}
	} else {
		of_start =
		    (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
						   getenv_bootm_mapsize()
						   + getenv_bootm_low());
	}

	if (of_start == NULL) {
		puts("device tree - allocation error\n");
		goto error;
	}

	if (disable_relocation) {
		/*
		 * We assume there is space after the existing fdt to use
		 * for padding
		 */
		fdt_set_totalsize(of_start, of_len);
		printf("   Using Device Tree in place at %p, end %p\n",
		       of_start, of_start + of_len - 1);
	} else {
		debug("## device tree at %p ... %p (len=%ld [0x%lX])\n",
		      fdt_blob, fdt_blob + *of_size - 1, of_len, of_len);

		printf("   Loading Device Tree to %p, end %p ... ",
		       of_start, of_start + of_len - 1);

		err = fdt_open_into(fdt_blob, of_start, of_len);
		if (err != 0) {
			fdt_error("fdt move failed");
			goto error;
		}
		puts("OK\n");
	}

	*of_flat_tree = of_start;
	*of_size = of_len;

	set_working_fdt_addr((ulong)*of_flat_tree);

error:
	return 1;
}

我们把uboot处理完成后的dtb内容打印出来:

of_start: 

0x0: 0xd0 0xd 0xfe 0xed 0x0 0x1 0x32 0x8 0x0 0x0 0x0 0x38 0x0 0x0 0xfd 0x8c 
0x10: 0x0 0x0 0x0 0x28 0x0 0x0 0x0 0x11 0x0 0x0 0x0 0x10 0x0 0x0 0x0 0x0 
0x20: 0x0 0x0 0x4 0x7c 0x0 0x0 0xfd 0x54 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 
0x30: 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x0 
0x10100: 0x73 0x65 0x74 0x0 0x63 0x70 0x73 0x77 0x2d 0x70 0x6f 0x72 0x74 0x2d 0x6f 0x66 
0x10110: 0x66 0x73 0x65 0x74 0x0 0x63 0x70 0x73 0x77 0x2d 0x6d 0x61 0x63 0x2d 0x73 0x61 

其中0x10100地址保存的cpsw-stats-offset属性string没有被改写。

总结:uboot拷贝完成后,dtb内容没有被改写。

Step 5. 排查Vxworks拷贝过程

我们继续排查下一阶段,dtb数据有没有被改写。uboot会将dtb地址传递给内核,并运行内核。

内核的具体处理过程如下:

sysAlib.s:

_ARM_FUNCTION (sysInit)

#if ARM_THUMB2
        .code   32
#endif

        /* disable interrupts in CPU */

        MRS     r1, cpsr
        ORR     r1, r1, #(I_BIT | F_BIT)
        MSR     cpsr, r1

        /* set initial stack pointer so stack grows down from start of code */

        ADR     sp, sysInit
        MOV     fp, #0     /* initialize frame pointer */

        /* (1) 计算现在运行的地址,和编译时的绝对地址的差值
            可能编译的是地址无关代码,可以在任何地址上运行 
         */
        LDR     r1, =sysInit                /* 获取sysInit编译时的绝对地址 */
        ADR     r6, sysInit                 /* 获取sysInit现在运行的地址 */
        SUB     r6, r6, r1                  /* 计算两者的差值,作为基准值 */
 
#ifdef _WRS_CONFIG_SMP
        MRC     p15, 0, r1, c0, c0, 5
        ANDS    r1, r1, #0xf
        BEQ     1f          /* if core0 */
	    BL	    sysArmGicDevInit
	    B	    2f
#endif

1:
#ifdef INCLUDE_STANDALONE_DTB
        LDR     r0, =dt_blob_start
#endif /* INCLUDE_STANDALONE_DTB */

        /* (2) 获取gpDtbInit全局变量的当前地址
            r0 中存储的是uboot传递过来的dtb的物理地址(0xa0000000)
            把dtb的物理地址存放到gpDtbInit全局变量中
         */
        LDR     r1, =gpDtbInit
        ADD     r1, r1, r6
        STR     r0, [r1]
    
        ...

        /* (3) 跳转到usrInit */
2:
        LDR     r1, =usrInit
        ADD     r1, r1, r6
        MOV     r0, #2      /* legacy BOOT_COLD */
        BX      r1

FUNC_END (sysInit)

我们继续追查其在usrInit中的处理:

prjConfig.c:

void usrInit (int startType)
    {
    {                         void (* cpuInit) (void) = vxCpuInit;                         void (* mmuEarlyInit) (UINT32, UINT32, UINT32) = vxMmuEarlyInit;                         cpuInit = (void (*) (void)) ((uintptr_t) cpuInit + LOCAL_MEM_PHYS_ADRS);                         mmuEarlyInit = (void (*) (UINT32, UINT32, UINT32)) ((uintptr_t) mmuEarlyInit + LOCAL_MEM_PHYS_ADRS);                         cpuInit ();                         mmuEarlyInit (STATIC_MMU_TABLE_BASE, LOCAL_MEM_LOCAL_ADRS, IMA_SIZE);                         excVecBaseSet ((UINT32)VEC_BASE_ADRS);                         } /* This component includes the ARM architecture APU initialization. */

    ...

    /* (3.1) 把dtb临时存储空间(物理地址0xa0000000,存放在gpDtbInit变量当中)映射成虚拟地址 */
    usrAdjustDtb ();                    /* This compnoent includes support for adjusting the device tree blob (DTB) address (architecture internal usage). */

    /* (3.2) 校验临时空间中的dtb内容,如无错误拷贝到DTB_RELOC_ADDR区域中 */
    usrFdtInit ((void*)DTB_RELOC_ADDR, (int)DTB_MAX_LEN); /* This component provides the flat device tree library. */

    ...

    excVecInit ();                      /* This component provides architecture-dependent facilities for handling processor exceptions. */

    ...    

    usrKernelInit (VX_GLOBAL_NO_STACK_FILL); /* Context switch and interrupt handling. This is a required component. DO NOT REMOVE */
    }

可以看到,首先会把传递过来的dtb临时存储空间(物理地址0xa0000000,存放在gpDtbInit变量当中)映射成虚拟地址:

vxworksinstall\vxworks-7\pkgs\os\arch\arm-1.1.10.1\kernel\armbase\configlette\usrAdjustDtb.c:

LOCAL void usrAdjustDtb (void)
    {
#ifndef INCLUDE_STANDALONE_DTB
#ifdef _WRS_CONFIG_LP64	
    gpDtbInit = (void *)vxMmuEarlyRegMap ((PHYS_ADDR)gpDtbInit, DTB_MAX_LEN);
#else  /* !_WRS_CONFIG_LP64 */

    /* type-cast for clean warning */

    gpDtbInit = (void *)vxMmuEarlyRegMap ((PHYS_ADDR)((UINT32)gpDtbInit), DTB_MAX_LEN);
#endif /* _WRS_CONFIG_LP64 */
#endif /* !INCLUDE_STANDALONE_DTB */
    }

随后会校验dtb的内容,如无错误会把dtb拷贝到正式的存储空间(虚拟地址 DTB_RELOC_ADDR = 0x10000):

usrFdtInit() -> vxFdtLibInit() -> fdt_open_into()

vxworksinstall\vxworks-7\pkgs\os\firmware\fdt-1.0.9.5\src\libfdt\fdt_rw.c:

int fdt_open_into
(
    const void *fdt,  /* pointer to source device tree */
    void *buf,        /* pointer to destination source tree */
    int   bufsize     /* total size, in bytes, of destination FDT tree*/
)
{
	int err;
	int mem_rsv_size, struct_size;
	int newsize;
	const char *fdtstart = fdt;
	const char *fdtend = fdtstart + fdt_totalsize(fdt);
	char *tmp;

	FDT_CHECK_HEADER(fdt);                       /* req: VX7-10975 */

	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)      /* req: VX7-10976 */
		* sizeof(struct fdt_reserve_entry);

	                                           /* req: VX7-10977 */
	if (fdt_version(fdt) >= 17) {              /* req: VX7-10978 */
		struct_size = fdt_size_dt_struct(fdt);
	} else {
	                                           /* req: VX7-10979 */
		struct_size = 0;
		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
			;
		if (struct_size < 0)              /* req: VX7-10980 */
			return struct_size;
	}

	                                           /* req: VX7-10981 */

    /* (3.2.1) 判断是否需要调整dtb各个区段的循序 */
	if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
		/* no further work necessary */
        /* (3.2.2) 无需调整,直接拷贝到正式区域DTB_RELOC_ADDR */
		err = fdt_move(fdt, buf, bufsize);
		if (err)                           /* req: VX7-10982 */
			return err;
		fdt_set_version(buf, 17);
		fdt_set_size_dt_struct(buf, struct_size);
		fdt_set_totalsize(buf, bufsize);
		return 0;
	}

	/* Need to reorder */
	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
		+ struct_size + fdt_size_dt_strings(fdt);

	if (bufsize < newsize)                       /* req: VX7-10983 */
		return -FDT_ERR_NOSPACE;

	/* First attempt to build converted tree at beginning of buffer */
	tmp = buf;

	/* But if that overlaps with the old tree... */
	if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
		/* Try right after the old tree instead */
		tmp = (char *)(uintptr_t)fdtend;
	                                           /* req: VX7-10984 */
		if ((tmp + newsize) > ((char *)buf + bufsize))
			return -FDT_ERR_NOSPACE;
	}

	                                           /* req: VX7-10985 */
	_fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);

    /* (3.2.3) 调整完后,拷贝到正式区域DTB_RELOC_ADDR */
	memmove(buf, tmp, newsize);

	fdt_set_magic(buf, FDT_MAGIC);
	fdt_set_totalsize(buf, bufsize);
	fdt_set_version(buf, 17);
	fdt_set_last_comp_version(buf, 16);
	fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));

	return 0;
}

可以看到整个对dtb的核心处理都在usrInit()函数当中,这个阶段非常早,有个很大的限制是串口无法打印。

想用串口打印出当时的dtb内容,但是使用(* _func_kprintf)()打印的数据无法显示。这个时候如果要打印,需要把串口改成轮询模式,才可能打印出来。

总结:无法串口打印,无法判断dtb是否在这个阶段损坏。

Step 6. 排查Vxworks运行过程

既然上一步的路没有走通,我们也没有其他路走呢?

虽然我们没有看到上一步的确切结果,但是我们详细看了上一步的代码,和uboot的处理类似,出错的概率比较小。那么会不会是内核FDT部分处理完,被其他部分踩到了呢?

内核中最难找的就是踩内存问题,人海茫茫去找一个凶手是非常痛苦的事情!

  • 我们首先尝试使用内核中的MMU保护机制,把DTB_RELOC_ADDR开始被踩的页面设置成只读,这样有人写操作以后就会出错:
int fdt_open_into

{
    ...

	                                           /* req: VX7-10977 */
	if (fdt_version(fdt) >= 17) {              /* req: VX7-10978 */
		struct_size = fdt_size_dt_struct(fdt);
		err = fdt_move(fdt, buf, bufsize);
		
        /* (1) 将DTB_RELOC_ADDR页面设置成只读 */
		err = vmStateSet(0,(VIRT_ADDR)buf, 0x20000,MMU_ATTR_PROT_MSK,MMU_ATTR_PROT_SUP_READ);

后来发现这个时候还是太早了,系统的usrMmuInit ()还没有调用,这个机制也不能使用。

  • 接着我们又来尝试使用仿真器,因为踩的地址是固定的,想用仿真器的硬watchpoint功能来抓住对这个地址的写操作。

因为整个过程需要繁琐的学习和调试,先放弃。

  • 整个定位陷入了困境,我们再次回到原点,仔细观察发生的错误:
-> d 0x200f0,100,4
NOTE: memory values are displayed in hexadecimal.
0x000200f0:  63007865 2d777370 63616d67 66666f2d  *ex.cpsw-gmac-off*
0x00020100:  001e4018 001e48e4 001e4008 001e3e0c  *[email protected]...@...>..*
0x00020110:  74657366 001e4330 616d2d77 61732d63  *fset0C..w-mac-sa*
0x00020120:  772d6576 6c007961 6c61636f 63616d2d  *ve-way.local-mac*

从被踩数据来看,它像是一个地址,我们尝试使用地址的方式来解析它:

-> lkAddr 0x001e4018
0x001e4008 excEnterPrefetchAbort     text    
0x001e4018 excEnterUndef             text    
0x001e4240 excVBARSet                text    
0x001e424c excVBARGet                text    
0x001e4258 excDFARGet                text    
0x001e4260 excIFARGet                text    
0x001e4268 excDFSRSet                text    
0x001e4270 excIFSRSet                text    
0x001e4278 excDFSRGet                text    
0x001e4280 excIFSRGet                text    
0x001e42bc intLock                   text    
0x001e42bc intCpuLock                text    
value = 0 = 0x0
-> lkAddr 0x001e48e4
0x001e48dc _sigCtxRtnValSet          text    
0x001e48e4 syscallTrapHandle         text    
0x001e4ac8 vxTas                     text    
0x001e4aec vxTaskEntry               text    
0x001e4b30 vxCpsrGet                 text    
0x001e4b38 vxMidrGet                 text    
0x001e4b40 vxRevidrGet               text    
0x001e4b48 mmuReadId                 text    
0x001e4b50 vxMpidrGet                text    
0x001e4b60 vxMemProbeSup             text    
0x001e4b80 vxMemProbeByteRead        text    
0x001e4b84 vxMemProbeByteWrite       text    
value = 0 = 0x0

我们可以看到解析出来的都是和异常向量相关的符号,我们查看其定义:

vxworks-7\pkgs\os\arch\arm-1.1.10.1\kernel\armv7a\src\excALib.s:

        /* globals */

        FUNC_EXPORT(armInitExceptionModes) /* initialize ARM modes */
        FUNC_EXPORT(excEnterUndef)         /* undefined instruction handler */
        FUNC_EXPORT(excEnterSwi)           /* software interrupt handler */
        FUNC_EXPORT(excEnterPrefetchAbort) /* prefetch abort handler */
        FUNC_EXPORT(excEnterDataAbort)     /* data abort handler */

看起来是异常向量的地址,会不会是某些操作把这些地址配置到了0x20100地址呢?继续查找:

在usrInit()中首先调用excVecBaseSet()设置了异常向量基址:

void usrInit (int startType)
    {
    {                         void (* cpuInit) (void) = vxCpuInit;                         void (* mmuEarlyInit) (UINT32, UINT32, UINT32) = vxMmuEarlyInit;                         cpuInit = (void (*) (void)) ((uintptr_t) cpuInit + LOCAL_MEM_PHYS_ADRS);                         mmuEarlyInit = (void (*) (UINT32, UINT32, UINT32)) ((uintptr_t) mmuEarlyInit + LOCAL_MEM_PHYS_ADRS);                         cpuInit ();                         mmuEarlyInit (STATIC_MMU_TABLE_BASE, LOCAL_MEM_LOCAL_ADRS, IMA_SIZE);                         excVecBaseSet ((UINT32)VEC_BASE_ADRS);                         } /* This component includes the ARM architecture APU initialization. */

    ...

    /* (3.1) 把dtb临时存储空间(物理地址0xa0000000,存放在gpDtbInit变量当中)映射成虚拟地址 */
    usrAdjustDtb ();                    /* This compnoent includes support for adjusting the device tree blob (DTB) address (architecture internal usage). */

    /* (3.2) 校验临时空间中的dtb内容,如无错误拷贝到DTB_RELOC_ADDR区域中 */
    usrFdtInit ((void*)DTB_RELOC_ADDR, (int)DTB_MAX_LEN); /* This component provides the flat device tree library. */

    ...

    excVecInit ();                      /* This component provides architecture-dependent facilities for handling processor exceptions. */

    ...    

    usrKernelInit (VX_GLOBAL_NO_STACK_FILL); /* Context switch and interrupt handling. This is a required component. DO NOT REMOVE */
    }

↓

void excVecBaseSet 
    (
    UINT32 base  /* location of the exception vector table */
    )
    {

    /* set the exception vector base address */

    excVecBaseAddr = base;                    /* req: VX7-14987 */

                                              /* req: VX7-14988 */
    excEnterTbl[0].vecAddr = EXC_OFF_UNDEF    + base;
    excEnterTbl[1].vecAddr = EXC_OFF_SWI      + base;
    excEnterTbl[2].vecAddr = EXC_OFF_PREFETCH + base;
    excEnterTbl[3].vecAddr = EXC_OFF_DATA     + base;

#ifdef _WRS_CONFIG_UNIFIED_FIQ_IRQ

    excEnterTbl[4].vecAddr = EXC_OFF_FIQ      + base;

#else

    excEnterTbl[4].vecAddr = EXC_OFF_IRQ      + base;

#endif /* _WRS_CONFIG_UNIFIED_FIQ_IRQ */
    }

然后在usrInit()又调用excVecInit()往这些基址中设置数据,这些数据正是DTB被覆盖的数据:

STATUS excVecInit (void)
{

    for (i = 0; i < NUM_EXC_VECS; ++i)
	{
	/*
	 * Each vector contains an LDR PC,[PC,#offset] instruction to
         * load the PC from an address stored in the exception pointer
         * table located at (exception vector base address + excPtrTableOffset)
	 */

#if (ARM_THUMB2)

#  ifdef BIG_ENDIAN
	*(UINT32 *)excEnterTbl[i].vecAddr = 0xF8DFF000 |
            (excPtrTableOffset - 4 - FIRST_VECTOR);
#  else /*BIG_ENDIAN*/
	*(UINT32 *)excEnterTbl[i].vecAddr = 0xF000F8DF |
            (excPtrTableOffset - 4 - FIRST_VECTOR) << 16;
#  endif /*BIG_ENDIAN*/

#else /*(ARM_THUMB2)*/

                                              /* req: VX7-14992 */
                                              /* req: VX7-14993 */
                                              /* req: VX7-14994 */
	*(UINT32 *)excEnterTbl[i].vecAddr = SWAP32_BE8(0xE59FF000 |
            (excPtrTableOffset - 8 - FIRST_VECTOR));

#endif /*(ARM_THUMB2)*/

    /* (1) 望异常向量表中存储数据 */
                                                /* req: VX7-14995 */
	*(VOIDFUNCPTR *)
	    (excEnterTbl[i].vecAddr + excPtrTableOffset - FIRST_VECTOR) =
							    excEnterTbl[i].fn;
        /*
         * Note: cache/invalidate completed later, and will encompass the
         * entire exception table (EXC_TABLE_SIZE).
         */

	}

}

查看VIP工程中异常向量表的基址(VEC_BASE_ADRS)定义:

vector base addr : VEC_BASE_ADDR = 0x20000 
excPtrTableOffset : 0x100 
vector addr : 0x20000+0x100 = 0x20100

可以看到这个区域刚好和DTB_RELOC_ADDR重叠。

总结:异常向量表区域和DTB区域重叠。

3. 结论

综上分析:

最后我们根据被踩数据的特征,反向找到了真凶异常向量表。

我们把异常向量表地址更改成0x40000,问题消失:

diff --git a/prjParams.h b/prjParams.h
index ab50e29..cf63d0b 100644
--- a/prjParams.h
+++ b/prjParams.h

@@ -1505,7 +1505,7 @@ DO NOT EDIT - file is regenerated whenever the project changes
 #undef  LOCAL_MEM_PHYS_ADRS
 #define LOCAL_MEM_PHYS_ADRS 0x84000000
 #undef  VEC_BASE_ADRS
-#define VEC_BASE_ADRS ((char *) (LOCAL_MEM_LOCAL_ADRS) + 0x20000)
+#define VEC_BASE_ADRS ((char *) (LOCAL_MEM_LOCAL_ADRS) + 0x40000)
 #undef  EXC_MSG_OFFSET
 #define EXC_MSG_OFFSET 0x1200
 #undef  EXC_MSG_ADRS

猜你喜欢

转载自blog.csdn.net/pwl999/article/details/109412091
今日推荐