了解 U-Boot 内存占用
Understand U-Boot memory footprint
我不明白加载 U-Boot 时 RAM 中发生了什么。我正在开发 Xilinx Zynq ZC702 评估套件,我正在尝试使用 U-Boot 在其上加载 Linux 内核。于是我用Xilinx的工具Vivado和SDK生成了一个BOOT.bin的文件一步步写到SD卡上:
- 使用 Vivado 创建硬件项目,
- 使用 SDK 生成 FSBL 和 FPGA 比特流,
- 创建包含 FSBL + 比特流 + U-Boot 的引导映像(我从 xilinx Git 存储库下载了 U-Boot 源)。
简而言之,我遵循了 Xilinx User Guide 中描述的所有步骤。
但是现在,在加载内核之前,我想了解发生了什么但我做不到。根据文档,U-Boot 如果从闪存加载,会将自身复制到 RAM 中,并从那里执行自身,但是在哪里?
在网上查了下,发现U-Boot自己解压的地址在include/configs/zynq-common.h by CONFIG_SYS_TEXT_BASE,好像是0x400_0000.
但是在别的网站上看到debug模式下可以打印重定位地址,所以修改了文件common/board_r.c,覆盖了函数" initr_announce”打印“gd->relocaddr”字段。这次好像U-Boot使用了偏移地址0x3FF3_7000.
当我使用 U-Boot 命令 "md" 检查内存时,我看到使用了两个偏移量,并且我在两个位置都看到了一个幻数 "be00_00ea" :
Xilinx First Stage Boot Loader
Release 2014.4 Feb 8 2016-14:53:56
Devcfg driver initialized
Silicon Version 3.1
Boot mode is SD
SD: rc= 0
SD Init Done
Flash Base Address: 0xE0100000
Reboot status register: 0x60400000
Multiboot Register: 0x0000C000
Image Start Address: 0x00000000
Partition Header Offset:0x00000C80
Partition Count: 3
Partition Number: 1
Header Dump
Image Word Len: 0x000F6EC0
Data Word Len: 0x000F6EC0
Partition Word Len:0x000F6EC0
Load Addr: 0x00000000
Exec Addr: 0x00000000
Partition Start: 0x000065D0
Partition Attr: 0x00000020
Partition Checksum Offset: 0x00000000
Section Count: 0x00000001
Checksum: 0xFFD14B7E
Bitstream
In FsblHookBeforeBitstreamDload function
PCAP:StatusReg = 0x40000A30
PCAP:device ready
PCAP:Clear done
Level Shifter Value = 0xA
Devcfg Status register = 0x40000A30
PCAP:Fabric is Initialized done
PCAP register dump:
PCAP CTRL 0xF8007000: 0x4C00E07F
PCAP LOCK 0xF8007004: 0x0000001A
PCAP CONFIG 0xF8007008: 0x00000508
PCAP ISR 0xF800700C: 0x0802000B
PCAP IMR 0xF8007010: 0xFFFFFFFF
PCAP STATUS 0xF8007014: 0x00000A30
PCAP DMA SRC ADDR 0xF8007018: 0x00100001
PCAP DMA DEST ADDR 0xF800701C: 0xFFFFFFFF
PCAP DMA SRC LEN 0xF8007020: 0x000F6EC0
PCAP DMA DEST LEN 0xF8007024: 0x000F6EC0
PCAP ROM SHADOW CTRL 0xF8007028: 0xFFFFFFFF
PCAP MBOOT 0xF800702C: 0x0000C000
PCAP SW ID 0xF8007030: 0x00000000
PCAP UNLOCK 0xF8007034: 0x757BDF0D
PCAP MCTRL 0xF8007080: 0x30800100
DMA Done !
FPGA Done !
In FsblHookAfterBitstreamDload function
Partition Number: 2
Header Dump
Image Word Len: 0x0001BA12
Data Word Len: 0x0001BA12
Partition Word Len:0x0001BA12
Load Addr: 0x04000000
Exec Addr: 0x04000000
Partition Start: 0x000FD490
Partition Attr: 0x00000010
Partition Checksum Offset: 0x00000000
Section Count: 0x00000001
Checksum: 0xF7EAFAC8
Application
Handoff Address: 0x04000000
In FsblHookBeforeHandoff function
SUCCESSFUL_HANDOFF
FSBL Status = 0x1
U-Boot 2015.07 (Feb 11 2016 - 10:24:28 +0100)
Model: Zynq ZC702 Development Board
I2C: ready
DRAM: ECC disabled 1 GiB
MMC: zynq_sdhci: 0
SF: Detected N25Q128A with page size 256 Bytes, erase size 64 KiB, total 16 MiB
In: serial
Out: serial
Err: serial
Model: Zynq ZC702 Development Board
Net: Gem.e000b000
Hit any key to stop autoboot: 0
zynq-uboot> md 0x4000000
04000000: ea0000be e59ff014 e59ff014 e59ff014 ................
04000010: e59ff014 e59ff014 e59ff014 e59ff014 ................
04000020: 04000060 040000c0 04000120 04000180 `....... .......
04000030: 040001e0 04000240 040002a0 deadbeef ....@...........
04000040: 0badc0de e320f000 e320f000 e320f000 ...... ... ... .
04000050: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
04000060: e51fd028 e58de000 e14fe000 e58de004 (.........O.....
04000070: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
04000080: e24dd048 e88d1fff e51f2050 e892000c H.M.....P ......
04000090: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
040000a0: e1a0000d eb0005dc e320f000 e320f000 .......... ... .
040000b0: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
040000c0: e51fd088 e58de000 e14fe000 e58de004 ..........O.....
040000d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
040000e0: e24dd048 e88d1fff e51f20b0 e892000c H.M...... ......
040000f0: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
zynq-uboot> md 0x3ff37000
3ff37000: ea0000be e59ff014 e59ff014 e59ff014 ................
3ff37010: e59ff014 e59ff014 e59ff014 e59ff014 ................
3ff37020: 3ff37060 3ff370c0 3ff37120 3ff37180 `p.?.p.? q.?.q.?
3ff37030: 3ff371e0 3ff37240 3ff372a0 deadbeef .q.?@r.?.r.?....
3ff37040: 3f312628 e320f000 e320f000 e320f000 (&1?.. ... ... .
3ff37050: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
3ff37060: e51fd028 e58de000 e14fe000 e58de004 (.........O.....
3ff37070: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
3ff37080: e24dd048 e88d1fff e51f2050 e892000c H.M.....P ......
3ff37090: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
3ff370a0: e1a0000d eb0005dc e320f000 e320f000 .......... ... .
3ff370b0: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
3ff370c0: e51fd088 e58de000 e14fe000 e58de004 ..........O.....
3ff370d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
3ff370e0: e24dd048 e88d1fff e51f20b0 e892000c H.M...... ......
3ff370f0: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
zynq-uboot>
为什么 U-Boot 需要这两个偏移量? U-Boot 的实际内存占用是多少?更一般地说,我可以将我的内核放在哪里以确保它不会覆盖某些东西?
ARM 架构上的 u-boot 重定位
这是两阶段启动过程的完整顺序:
- ROM 代码将 SPL(从 SD 卡上的
MLO
文件)读取到 CONFIG_SPL_TEXT_BASE
地址。该地址通常位于 SRAM 中,无需初始化即可发挥作用(与 RAM 相反)。 ROM 代码跳转到 SPL 代码。
- SPL 配置 RAM,然后读取 u-boot(从 SD 卡上的
u-boot.img
文件)到 CONFIG_SYS_TEXT_BASE
RAM 地址(通常在 RAM 的开头)和 运行它
- u-boot 将自身重新定位到
gd->relocaddr
RAM 地址(通常在 RAM 的末尾)并跳转到重新定位的代码
- 现在我们准备启动内核
对于没有 SPL 的单阶段启动,通常只使用 u-boot.bin
文件。在这种情况下,您只有第 3 步和第 4 步。
搬迁有两种情况(如doc/README.arm-relocation所述):
CONFIG_SYS_TEXT_BASE != gd->relocaddr
: 将执行搬迁
CONFIG_SYS_TEXT_BASE == gd->relocaddr
: 不会执行重定位
在您的情况下,您看到执行了重定位(如 CONFIG_SYS_TEXT_BASE != gd->relocaddr
)。
所以,解决你的问题:
Why U-Boot need those two offsets ?
u-boot ARM relocation task 中描述了搬迁背后的原因:
...we can measure the actual size of memory present on the board, and then relocate U-Boot to the very end of the RAM, leaving nearly the whole RAM usable as one big contiguous area for "applications" like loading Linux kernel, ramdisk, etc.
确实,如果您查看代码,您会发现 gd->relocaddr
是 RAM 的末尾减去监视器代码 (U-Boot) 大小:
gd->relocaddr = gd->ram_top;
...
gd->relocaddr -= gd->mon_len;
还可以执行一些额外的内存保留。例如,在我的平台 (TI DRA7XX EVM) 上,我可以看到正在调用的下一个函数:
setup_dest_addr()
reserve_round_4k()
reserve_mmu()
reserve_uboot()
实际搬迁是在 board_init_f()
调用之后完成的。
arch/arm/lib/crt0.S:
bl board_init_f
...
b relocate_code
arch/arm/lib/relocate.S:
ENTRY(relocate_code)
现在可以轻松回答您的下一个问题了:
what is the real memory footprint of U-Boot ?
搬迁前 U-Boot 位于 CONFIG_SYS_TEXT_BASE
。搬迁后 U-Boot 位于 gs->relocaddr
.
关于你的最后一个问题:
where can I put my kernel to be sure that it will not overwrite something?
由于 U-Boot 被重新定位到 RAM 的末尾,理论上您可以使用任何 RAM 地址来放置内核。但是看看 CONFIG_EXTRA_ENV_SETTINGS
中的定义 include/configs/zynq-common.h
:
"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"load mmc 0 ${devicetree_load_address} ${devicetree_image} && " \
"load mmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}; " \
"fi[=14=]" \
从那里您可以看到您应该将内核加载到 ${kernel_load_address}
,即 0x2080000
:
"kernel_load_address=0x2080000[=15=]" \
请参阅该定义的其余部分以了解其他常量。
bdinfo 命令
您会发现 bdinfo
命令很有用:可以使用 U-Boot shell 中的 bdinfo
命令找到重定位地址以及其他有用的信息。例如 DRA7XX EVM:
=> bdinfo
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x60000000
TLB addr = 0xDFFF0000
relocaddr = 0xDFF5D000
reloc off = 0x5F75D000
irq_sp = 0xDEF3CEE0
sp start = 0xDEF3CED0
从这里你可以看到:
- RAM 起始于
0x80000000
- RAM 大小为
0x60000000
- ...所以 RAM 结束 (
gd->ram_top
) 是 0x80000000 + 0x60000000 = 0xE0000000
- 搬迁地址为
0xDFF5D000
- 为重定位保留的内存是
0xE0000000 - 0xDFF5D000 = 652 KB
- 监视器 (U-Boot) 大小约为
TLB addr - relocaddr = 0xDFFF0000 - 0xDFF5D000 = 588 KB
另请参阅:
[1] u-boot : Relocation
[2]
我不明白加载 U-Boot 时 RAM 中发生了什么。我正在开发 Xilinx Zynq ZC702 评估套件,我正在尝试使用 U-Boot 在其上加载 Linux 内核。于是我用Xilinx的工具Vivado和SDK生成了一个BOOT.bin的文件一步步写到SD卡上:
- 使用 Vivado 创建硬件项目,
- 使用 SDK 生成 FSBL 和 FPGA 比特流,
- 创建包含 FSBL + 比特流 + U-Boot 的引导映像(我从 xilinx Git 存储库下载了 U-Boot 源)。
简而言之,我遵循了 Xilinx User Guide 中描述的所有步骤。
但是现在,在加载内核之前,我想了解发生了什么但我做不到。根据文档,U-Boot 如果从闪存加载,会将自身复制到 RAM 中,并从那里执行自身,但是在哪里?
在网上查了下,发现U-Boot自己解压的地址在include/configs/zynq-common.h by CONFIG_SYS_TEXT_BASE,好像是0x400_0000.
但是在别的网站上看到debug模式下可以打印重定位地址,所以修改了文件common/board_r.c,覆盖了函数" initr_announce”打印“gd->relocaddr”字段。这次好像U-Boot使用了偏移地址0x3FF3_7000.
当我使用 U-Boot 命令 "md" 检查内存时,我看到使用了两个偏移量,并且我在两个位置都看到了一个幻数 "be00_00ea" :
Xilinx First Stage Boot Loader
Release 2014.4 Feb 8 2016-14:53:56
Devcfg driver initialized
Silicon Version 3.1
Boot mode is SD
SD: rc= 0
SD Init Done
Flash Base Address: 0xE0100000
Reboot status register: 0x60400000
Multiboot Register: 0x0000C000
Image Start Address: 0x00000000
Partition Header Offset:0x00000C80
Partition Count: 3
Partition Number: 1
Header Dump
Image Word Len: 0x000F6EC0
Data Word Len: 0x000F6EC0
Partition Word Len:0x000F6EC0
Load Addr: 0x00000000
Exec Addr: 0x00000000
Partition Start: 0x000065D0
Partition Attr: 0x00000020
Partition Checksum Offset: 0x00000000
Section Count: 0x00000001
Checksum: 0xFFD14B7E
Bitstream
In FsblHookBeforeBitstreamDload function
PCAP:StatusReg = 0x40000A30
PCAP:device ready
PCAP:Clear done
Level Shifter Value = 0xA
Devcfg Status register = 0x40000A30
PCAP:Fabric is Initialized done
PCAP register dump:
PCAP CTRL 0xF8007000: 0x4C00E07F
PCAP LOCK 0xF8007004: 0x0000001A
PCAP CONFIG 0xF8007008: 0x00000508
PCAP ISR 0xF800700C: 0x0802000B
PCAP IMR 0xF8007010: 0xFFFFFFFF
PCAP STATUS 0xF8007014: 0x00000A30
PCAP DMA SRC ADDR 0xF8007018: 0x00100001
PCAP DMA DEST ADDR 0xF800701C: 0xFFFFFFFF
PCAP DMA SRC LEN 0xF8007020: 0x000F6EC0
PCAP DMA DEST LEN 0xF8007024: 0x000F6EC0
PCAP ROM SHADOW CTRL 0xF8007028: 0xFFFFFFFF
PCAP MBOOT 0xF800702C: 0x0000C000
PCAP SW ID 0xF8007030: 0x00000000
PCAP UNLOCK 0xF8007034: 0x757BDF0D
PCAP MCTRL 0xF8007080: 0x30800100
DMA Done !
FPGA Done !
In FsblHookAfterBitstreamDload function
Partition Number: 2
Header Dump
Image Word Len: 0x0001BA12
Data Word Len: 0x0001BA12
Partition Word Len:0x0001BA12
Load Addr: 0x04000000
Exec Addr: 0x04000000
Partition Start: 0x000FD490
Partition Attr: 0x00000010
Partition Checksum Offset: 0x00000000
Section Count: 0x00000001
Checksum: 0xF7EAFAC8
Application
Handoff Address: 0x04000000
In FsblHookBeforeHandoff function
SUCCESSFUL_HANDOFF
FSBL Status = 0x1
U-Boot 2015.07 (Feb 11 2016 - 10:24:28 +0100)
Model: Zynq ZC702 Development Board
I2C: ready
DRAM: ECC disabled 1 GiB
MMC: zynq_sdhci: 0
SF: Detected N25Q128A with page size 256 Bytes, erase size 64 KiB, total 16 MiB
In: serial
Out: serial
Err: serial
Model: Zynq ZC702 Development Board
Net: Gem.e000b000
Hit any key to stop autoboot: 0
zynq-uboot> md 0x4000000
04000000: ea0000be e59ff014 e59ff014 e59ff014 ................
04000010: e59ff014 e59ff014 e59ff014 e59ff014 ................
04000020: 04000060 040000c0 04000120 04000180 `....... .......
04000030: 040001e0 04000240 040002a0 deadbeef ....@...........
04000040: 0badc0de e320f000 e320f000 e320f000 ...... ... ... .
04000050: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
04000060: e51fd028 e58de000 e14fe000 e58de004 (.........O.....
04000070: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
04000080: e24dd048 e88d1fff e51f2050 e892000c H.M.....P ......
04000090: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
040000a0: e1a0000d eb0005dc e320f000 e320f000 .......... ... .
040000b0: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
040000c0: e51fd088 e58de000 e14fe000 e58de004 ..........O.....
040000d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
040000e0: e24dd048 e88d1fff e51f20b0 e892000c H.M...... ......
040000f0: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
zynq-uboot> md 0x3ff37000
3ff37000: ea0000be e59ff014 e59ff014 e59ff014 ................
3ff37010: e59ff014 e59ff014 e59ff014 e59ff014 ................
3ff37020: 3ff37060 3ff370c0 3ff37120 3ff37180 `p.?.p.? q.?.q.?
3ff37030: 3ff371e0 3ff37240 3ff372a0 deadbeef .q.?@r.?.r.?....
3ff37040: 3f312628 e320f000 e320f000 e320f000 (&1?.. ... ... .
3ff37050: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
3ff37060: e51fd028 e58de000 e14fe000 e58de004 (.........O.....
3ff37070: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
3ff37080: e24dd048 e88d1fff e51f2050 e892000c H.M.....P ......
3ff37090: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
3ff370a0: e1a0000d eb0005dc e320f000 e320f000 .......... ... .
3ff370b0: e320f000 e320f000 e320f000 e320f000 .. ... ... ... .
3ff370c0: e51fd088 e58de000 e14fe000 e58de004 ..........O.....
3ff370d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
3ff370e0: e24dd048 e88d1fff e51f20b0 e892000c H.M...... ......
3ff370f0: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
zynq-uboot>
为什么 U-Boot 需要这两个偏移量? U-Boot 的实际内存占用是多少?更一般地说,我可以将我的内核放在哪里以确保它不会覆盖某些东西?
ARM 架构上的 u-boot 重定位
这是两阶段启动过程的完整顺序:
- ROM 代码将 SPL(从 SD 卡上的
MLO
文件)读取到CONFIG_SPL_TEXT_BASE
地址。该地址通常位于 SRAM 中,无需初始化即可发挥作用(与 RAM 相反)。 ROM 代码跳转到 SPL 代码。 - SPL 配置 RAM,然后读取 u-boot(从 SD 卡上的
u-boot.img
文件)到CONFIG_SYS_TEXT_BASE
RAM 地址(通常在 RAM 的开头)和 运行它 - u-boot 将自身重新定位到
gd->relocaddr
RAM 地址(通常在 RAM 的末尾)并跳转到重新定位的代码 - 现在我们准备启动内核
对于没有 SPL 的单阶段启动,通常只使用 u-boot.bin
文件。在这种情况下,您只有第 3 步和第 4 步。
搬迁有两种情况(如doc/README.arm-relocation所述):
CONFIG_SYS_TEXT_BASE != gd->relocaddr
: 将执行搬迁CONFIG_SYS_TEXT_BASE == gd->relocaddr
: 不会执行重定位
在您的情况下,您看到执行了重定位(如 CONFIG_SYS_TEXT_BASE != gd->relocaddr
)。
所以,解决你的问题:
Why U-Boot need those two offsets ?
u-boot ARM relocation task 中描述了搬迁背后的原因:
...we can measure the actual size of memory present on the board, and then relocate U-Boot to the very end of the RAM, leaving nearly the whole RAM usable as one big contiguous area for "applications" like loading Linux kernel, ramdisk, etc.
确实,如果您查看代码,您会发现 gd->relocaddr
是 RAM 的末尾减去监视器代码 (U-Boot) 大小:
gd->relocaddr = gd->ram_top;
...
gd->relocaddr -= gd->mon_len;
还可以执行一些额外的内存保留。例如,在我的平台 (TI DRA7XX EVM) 上,我可以看到正在调用的下一个函数:
setup_dest_addr()
reserve_round_4k()
reserve_mmu()
reserve_uboot()
实际搬迁是在 board_init_f()
调用之后完成的。
arch/arm/lib/crt0.S:
bl board_init_f
...
b relocate_code
arch/arm/lib/relocate.S:
ENTRY(relocate_code)
现在可以轻松回答您的下一个问题了:
what is the real memory footprint of U-Boot ?
搬迁前 U-Boot 位于 CONFIG_SYS_TEXT_BASE
。搬迁后 U-Boot 位于 gs->relocaddr
.
关于你的最后一个问题:
where can I put my kernel to be sure that it will not overwrite something?
由于 U-Boot 被重新定位到 RAM 的末尾,理论上您可以使用任何 RAM 地址来放置内核。但是看看 CONFIG_EXTRA_ENV_SETTINGS
中的定义 include/configs/zynq-common.h
:
"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"load mmc 0 ${devicetree_load_address} ${devicetree_image} && " \
"load mmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}; " \
"fi[=14=]" \
从那里您可以看到您应该将内核加载到 ${kernel_load_address}
,即 0x2080000
:
"kernel_load_address=0x2080000[=15=]" \
请参阅该定义的其余部分以了解其他常量。
bdinfo 命令
您会发现 bdinfo
命令很有用:可以使用 U-Boot shell 中的 bdinfo
命令找到重定位地址以及其他有用的信息。例如 DRA7XX EVM:
=> bdinfo
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x60000000
TLB addr = 0xDFFF0000
relocaddr = 0xDFF5D000
reloc off = 0x5F75D000
irq_sp = 0xDEF3CEE0
sp start = 0xDEF3CED0
从这里你可以看到:
- RAM 起始于
0x80000000
- RAM 大小为
0x60000000
- ...所以 RAM 结束 (
gd->ram_top
) 是0x80000000 + 0x60000000 = 0xE0000000
- 搬迁地址为
0xDFF5D000
- 为重定位保留的内存是
0xE0000000 - 0xDFF5D000 = 652 KB
- 监视器 (U-Boot) 大小约为
TLB addr - relocaddr = 0xDFFF0000 - 0xDFF5D000 = 588 KB
另请参阅:
[1] u-boot : Relocation
[2]