按各地址起作用的顺序,uboot引导linux内核启动涉及到以下地址:
- load address:
- entry point: 这两个地址是mkimage时指定的
- bootm address:bootm为uboot的一个命令,以此从address启动kernel
- kernel运行地址:在具体mach目录中的Makefile.boot中指定,为kernel启动后实际运行的物理地址
mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage
理论上因为mkimage要为zImage加上0x40字节的header,所以entry point = load address + 0x40
但由于uboot 的bootm对uImage处理不是简单的go操作,其对前三个地址都有比较判断,所以在实际的操作中,就分为两种不同的情况:
1. bootm地址和load address一样
此种情况下,bootm不会对uImage header后的zImage进行memory move的动作,而会直接go到entry point开始执行。因此此时的entry point必须设置为load address + 0x40。如果kernel boot过程没有到uncompressing the kernel,就可能是这里设置不对。
boom address == load address == entry point - 0x40
具体细节可参看uboot代码common/cmd_bootm.c中bootm_load_os函数的实现:
switch (comp) {
case IH_COMP_NONE:
if (load == blob_start || load == image_start) {
printf(" XIP %s ... ", type_name);
no_overlap = 1;
} else {
printf(" Loading %s ... ", type_name);
memmove_wd((void *)load, (void *)image_start,
image_len, CHUNKSZ);
}
*load_end = load + image_len;
puts("OK\n");
break;
2. bootm地址和load address不一样(但需要避免出现memory move时出现覆盖导致zImage被破坏的情况)
此种情况下,bootm会把uImage header后的zImage move到load address(见上方代码),然后go到entry point开始执行。 由此知道此时的load address必须等于entry point。
boom address != load address == entry point
因此,在mkimage以及设置uboot boot command的时候需要注意到以上两种情况。
至于kernel的运行地址,其与前3个地址没有关系,除了要避免内存覆盖导致解压后kernel不完整的情况。
zImage的头部有地址无关的自解压程序,因此刚开始执行的时候,zImage所在的内存地址(entry point)不需要同编译kernel的地址相同。自解压程序会把kernel解压到编译时指定的物理地址,然后开始地址相关代码的执行。在开启MMU之前,kernel都是直接使用物理地址(可参看System.map)。
以上转自:uboot load address、entry point、 bootm address以及kernel运行地址的意义及联系 - schips - 博客园 (cnblogs.com)
以下对不同LOADADDR做了个实验:
LOADADDR=0x8000内核可以正常启动
## Booting kernel from Legacy Image at 30008000 ...
Image Name: Linux-5.10.0-xilinx
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4455712 Bytes = 4.2 MiB
Load Address: 00008000
Entry Point: 00008000
Verifying Checksum ... OK
## Flattened Device Tree blob at 31000000
Booting using the fdt blob at 0x31000000
Loading Kernel Image
Loading Device Tree to 2fff9000, end 2ffffcc1 ... OK
Starting kernel ...
Booting Linux on physical CPU 0x0
......
LOADADDR=0x30008000加载内核镜像没有问题,但是fdt_check_header时挂掉
## Booting kernel from Legacy Image at 30008000 ...
Image Name: Linux-5.10.0-xilinx
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4455712 Bytes = 4.2 MiB
Load Address: 30008000
Entry Point: 30008000
Verifying Checksum ... OK
## Flattened Device Tree blob at 31000000
Booting using the fdt blob at 0x31000000
Loading Kernel Image
Loading Device Tree to 2fff9000, end 2ffffcc1 ... OK
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 5.10.0-xilinx (lrp@ubuntu) (arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4, GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git) #6 SMP PREEMPT Wed Nov 22 14:14:58 CST 2023
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
-- 1 -- fdt[0xbfff9000]
magic[0xedfe0dd0]
OF: fdt: Machine model: Xilinx ZC706 board
OF: fdt: Ignoring memory range 0x0 - 0x30000000
printk: bootconsole [earlycon0] enabled
Memory policy: Data cache writealloc
cma: Reserved 16 MiB at 0x3f000000
Zone ranges:
Normal [mem 0x0000000030000000-0x000000003fffffff]
HighMem empty
Movable zone start for each node
Early memory node ranges
node 0: [mem 0x0000000030000000-0x000000003fffffff]
Initmem setup node 0 [mem 0x0000000030000000-0x000000003fffffff]
-- 1 -- fdt[0xbfff9000]
8<--- cut here ---
Unable to handle kernel paging request at virtual address bfff9000
pgd = (ptrval)
[bfff9000] *pgd=3eff1811, *pte=00000000, *ppte=00000000
Internal error: Oops - BUG: 7 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 0 PID: 0 Comm: swapper Not tainted 5.10.0-xilinx #6
Hardware name: Xilinx Zynq Platform
PC is at fdt_check_header+0x14/0x1b8
LR is at fdt_check_header+0x14/0x1b8
pc : [<c0344a3c>] lr : [<c0344a3c>] psr: 600000d3
sp : c0a01ee8 ip : fffffdff fp : c0870c10
r10: c0a01f48 r9 : ceffff00 r8 : 00000000
r7 : bfff9000 r6 : c0a5a920 r5 : c0a0838c r4 : bfff9000
r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : 00000017
Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment none
Control: 18c5387d Table: 3000404a DAC: 00000051
Process swapper (pid: 0, stack limit = 0x(ptrval))
Stack: (0xc0a01ee8 to 0xc0a02000)
1ee0: c09244dc c0a0838c c0a5a920 bfff9000 00000000 c059a8e8
1f00: c09244dc c0a0838c c0a3f1c0 c0a06f40 c0a01f50 ceffff00 c0a01f48 c0925588
1f20: 00000000 ffffffff c092fd3c c09041c8 00000000 c0a01f48 c0a01f4c 00000000
1f40: 00000000 c0a03cc8 30000000 40000000 ffffffff ffffffff c0a03cc8 c0a03cc0
1f60: 2fff9000 413fc090 18c5387d c0155f4c c0a01f94 00000000 00000000 c0900330
1f80: 00000051 c0a03cc8 c0a03cc0 2fff9000 413fc090 18c5387d 00000000 c090095c
1fa0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1fc0: 00000000 c0932a38 00000000 00000000 00000000 c0900330 00000051 10c0387d
1fe0: 00000000 2fff9000 413fc090 18c5387d 00000000 00000000 00000000 00000000
[<c0344a3c>] (fdt_check_header) from [<c059a8e8>] (__unflatten_device_tree+0x1c/0xf8)
[<c059a8e8>] (__unflatten_device_tree) from [<c0925588>] (unflatten_device_tree+0x24/0x40)
[<c0925588>] (unflatten_device_tree) from [<c09041c8>] (setup_arch+0x7d0/0xa84)
[<c09041c8>] (setup_arch) from [<c090095c>] (start_kernel+0x50/0x56c)
[<c090095c>] (start_kernel) from [<00000000>] (0x0)
Code: e1a01000 e1a04000 e59f0198 eb0eb126 (e5941000)
random: get_random_bytes called from init_oops_id+0x20/0x40 with crng_init=0
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Attempted to kill the idle task!
---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---
LOADADDR=0x80008000加载内核镜像直接挂了
## Booting kernel from Legacy Image at 30008000 ...
Image Name: Linux-5.10.0-xilinx
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4455712 Bytes = 4.2 MiB
Load Address: 80008000
Entry Point: 80008000
Verifying Checksum ... OK
## Flattened Device Tree blob at 31000000
Booting using the fdt blob at 0x31000000
Loading Kernel Image
启动信息里有这么两行:(不确定有没有影响)
Zone ranges:
Normal [mem 0x0000000000000000-0x000000002fffffff]
HighMem [mem 0x0000000030000000-0x000000003fffffff]
ZONE_NORMAL | 标记了可直接映射到内存段的普通内存域. 这是在所有体系结构上保证会存在的唯一内存区域, 但无法保证该地址范围对应了实际的物理地址. 例如, 如果AMD64系统只有两2G内存, 那么所有的内存都属于ZONE_DMA32范围, 而ZONE_NORMAL则为空 |
ZONE_HIGHMEM | 标记了超出内核虚拟地址空间的物理内存段, 因此这段地址不能被内核直接映射 |
结论:对于1G内存,当LOADADDR在0-0x2FFFFFFF之间时,可以正常启动,大于0x30000000的内存会有问题。