由于内存较小,zImage的起始地址受限。按照内存规划,修改zImage的加载地址,但是解压后Linux运行产生异常,可能是DataAbort,或者PrefetchAbort,或者Undefined Instruction。
1. 定位zImage自解压后,Kernel启动异常问题
T32启动运行后发现Kernel进入异常处理。大概位置在zImage自解压成功后,Linun earlyprintk之前。
T32跟踪从start_kernel开始的执行流程,发现问题异常在arm_memblock_init(),这属于memblock一部分。
尝试打开对memblock的调试,在bootargs中增加:
bootargs = "xxx earlyprintk memblock=debug"
从T32中可以看出memblock_reserve()的区间不符合预期。从启动日志打印的memblock_reserver信息:
Memory policy: Data cache writeback
memblock reserve: [0x80100000-0x8125113b] arm memblock init+0x30/0x158
memblock reserve: [0x80004000-0x80007fff] arm memblock init+0x108/0x158
memblock reserve: [0x86dfb@00-0x86dfcfff] arm memblock init+0x11c/0x1583
由于zImage位于0x82E00000,期望解压后镜像位于之后的地址。但是实际解压到了0x80000000后的地址。问题指向解压缩地址。
2. ZREALADDR定义解压缩地址
分析zImage解压缩代码,decompress_kernel()是入口。分析可知r0是解压目的起始地址,来自于r4。
/* * The C runtime environment should now be setup sufficiently. * Set up some pointers, and start decompressing. * r4 = kernel execution address * r7 = architecture ID * r8 = atags pointer */ mov r0, r4 mov r1, sp @ malloc space above stack add r2, sp, #0x10000 @ 64k max mov r3, r7 bl decompress_kernel
往前追溯r4的来源,可知对当前PC地址往前进行128MB对齐,然后加上0x8000偏移。对于0x82XXXXXX地址,最终r4即为0x80008000。
#ifdef CONFIG_AUTO_ZRELADDR /* * Find the start of physical memory. As we are executing * without the MMU on, we are in the physical address space. * We just need to get rid of any offset by aligning the * address. * * This alignment is a balance between the requirements of * different platforms - we have chosen 128MB to allow * platforms which align the start of their physical memory * to 128MB to use this feature, while allowing the zImage * to be placed within the first 128MB of memory on other * platforms. Increasing the alignment means we place * stricter alignment requirements on the start of physical * memory, but relaxing it means that we break people who * are already placing their zImage in (eg) the top 64MB * of this range. */ mov r4, pc and r4, r4, #0xf8000000 /* Determine final kernel image address. */ add r4, r4, #TEXT_OFFSET #else ldr r4, =zreladdr #endif
所以找到了问题根源。关闭CONFIG_AUTO_ZRELADDR,配置zreladdr的值。
3. 配置zreladdr指定解压地址
在arch/arm/boot/Makefile中将zreladdr-y的值赋给ZRELADDR。
# Note: the following conditions must always be true: # ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET) # PARAMS_PHYS must be within 4MB of ZRELADDR # INITRD_PHYS must be in RAM ZRELADDR := $(zreladdr-y) PARAMS_PHYS := $(params_phys-y) INITRD_PHYS := $(initrd_phys-y)
在arch/arm/boot/compressed/Makefile中将ZRELADDR的值赋给符号zreladdr,传入compressed vmlinux编译。
# Supply kernel BSS size to the decompressor via a linker symbol. KBSS_SZ = $(shell echo $$(($$($(CROSS_COMPILE)nm $(obj)/../../../../vmlinux | \ sed -n -e 's/^\([^ ]*\) [AB] __bss_start$$/-0x\1/p' \ -e 's/^\([^ ]*\) [AB] __bss_stop$$/+0x\1/p') )) ) LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ) # Supply ZRELADDR to the decompressor via a linker symbol. ifneq ($(CONFIG_AUTO_ZRELADDR),y) LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR) endif
通用的做法是在mach-xxx的Makefile.boot中增加zreladdr-y的赋值。
zreladdr-y += 0x82E08000
这里相对于zImage的实际加载地址有一个0x00008000的偏移。是因为前面存在内核启动参数以及MMU页表。
或者增加一个配置选项CONFIG_ZRELADDR_ADDR,然后赋值给ZRELADDR。
ZRELADDR := $(CONFIG_ZRELADDR_ADDR)
更多zImage自解压流程参考:《Linux 启动流程之自解压(一)》、《Linux 启动流程之自解压(二)》、《Linux 启动流程之自解压(三)》、《Linux 启动流程之自解压(四)》。《Linux内核源码分析--内核启动之zImage自解压过程》
标签:解压,r4,ZRELADDR,zreladdr,memblock,zImage From: https://www.cnblogs.com/arnoldlu/p/17515078.html