LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

zImage自解压起始地址:ZRELADDR

由于内存较小,zImage的起始地址受限。按照内存规划,修改zImage的加载地址,但是解压后Linux运行产生异常,可能是DataAbort,或者PrefetchAbort,或者Undefined Instruction。

1. 定位zImage自解压后,Kernel启动异常问题

T32启动运行后发现Kernel进入异常处理。大概位置在zImage自解压成功后,Linux 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自解压过程

posted on 2023-07-01 23:59  ArnoldLu  阅读(534)  评论(0)    收藏  举报

导航