initramfs开启xz压缩。在启动过程中出现下列日志:
[ 2.510114] Initramfs unpacking failed: XZ-compressed data is corrupt
可以启动到rootfs,但是rootfs中的/web/wifi.htm损坏。md5 checksum发现,md5有了变化。怀疑是memory region冲突导致的。
先研究了下uboot的内存使用区域,打开include/configs/ipq5018.h。
#define CONFIG_SYS_SDRAM_BASE 0x40000000
物理内存的地址。在ipq5018平台上是0x40000000。
#define CONFIG_SYS_SDRAM_SIZE 0x10000000
物理内存的大小,这里是256M。
#define CONFIG_SYS_TEXT_BASE 0x4A920000
uboot链接地址,如果uboot是binary格式,也就是说uboot的第一条指令就是uboot的第一个字节,因此,这个地址也是uboot在ram
中存放的地址。
#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + (64 << 20))
系统加载的地址,也就是tftpboot,bootm的默认地址,物理内存基地址加上64M。也就是0x44000000
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_TEXT_BASE -\
CONFIG_SYS_MALLOC_LEN - CONFIG_ENV_SIZE -GENERATED_BD_INFO_SIZE)
uboot运行栈的地址。可以看到这里的地址在TEXT_BASE之下了,默认应该在物理内存最顶端往下的。
IPQ5018# bdinfo
arch_number = 0x08040000
boot_params = 0x40000100
DRAM bank = 0x00000000
-> start = 0x40000000
-> size = 0x10000000
eth0name = eth0
ethaddr = a:d1:59:71:15:2
eth1name = eth1
eth1addr = a:d1:59:71:15:1
current eth = eth1
ip_addr = 192.168.10.1
baudrate = 115200 bps
TLB addr = 0x4A9D0000
relocaddr = 0x4A920000
reloc off = 0x00000000
irq_sp = 0x4A882A90
sp start = 0x4A882A80
bdinfo查看,sp是0x4A882A80。
dts中定义了一些保留内存:这些内存是保留个某些模块特殊使用的。
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
nss@40000000 {
no-map;
reg = <0x0 0x40000000 0x0 0x0800000>;
};
uboot@4a800000 {
no-map;
reg = <0x0 0x4a800000 0x0 0x00200000>;
};
sbl@4aa00000 {
no-map;
reg = <0x0 0x4aa00000 0x0 0x00100000>;
};
可以看到,初识的8M是个nss使用的。高地址内存中,最小的0x4a800000是个uboot保留的使用的,大小2M。
IPQ5018# tftpboot 48000000 ipq50xx_32.itb
Port1 Down Speed :10M Half duplex
Port3 Up Speed :1000M Full duplex
Using eth1 device
TFTP from server 192.168.10.100; our IP address is 192.168.10.1
Filename 'ipq50xx_32.itb'.
Load address: 0x48000000
Loading: *
Got TFTP_OACK: TFTP remote port: changes from 69 to 56089
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#####################
8.3 MiB/s
done
Bytes transferred = 7929828 (78ffe4 hex)
IPQ5018# bootm
## Loading kernel from FIT Image at 48000000 ...
Using 'config-1' configuration
Trying 'kernel' kernel subimage
Description: Linux kernel 5.15.38
Type: Kernel Image
Compression: uncompressed
Data Start: 0x480000c8
Data Size: 2889808 Bytes = 2.8 MiB
Architecture: ARM
OS: Linux
Load Address: 0x43208000
Entry Point: 0x43208000
Hash algo: sha1
Hash value: 0401e27bc01853c8c05ab27f0394940653444cff
Verifying Hash Integrity ... sha1+ OK
## Loading ramdisk from FIT Image at 48000000 ...
Using 'config-1' configuration
Trying 'initramfs' ramdisk subimage
Description: Compressed Initramfs
Type: RAMDisk Image
Compression: uncompressed
Data Start: 0x482cc834
Data Size: 4993596 Bytes = 4.8 MiB
Architecture: ARM
OS: Linux
Load Address: 0x44000000
Entry Point: 0x44000000
Hash algo: sha1
Hash value: bdb903aee1c5347c31d13d9bc03c89eb765a29f2
Verifying Hash Integrity ... sha1+ OK
Loading ramdisk from 0x482cc834 to 0x44000000
## Loading fdt from FIT Image at 48000000 ...
Using 'config-1' configuration
Trying 'fdt' fdt subimage
Description: Flattened Device Tree blob
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0x482c1a08
Data Size: 44396 Bytes = 43.4 KiB
Architecture: ARM
Hash algo: sha1
Hash value: 922d5291ba07c72a132d3af738b56b39f62dd094
Verifying Hash Integrity ... sha1+ OK
Booting using the fdt blob at 0x482c1a08
Loading Kernel Image ... OK
Loading Ramdisk to 4a3be000, end 4a88123c ... OK
Loading Device Tree to 4a3b0000, end 4a3bdd6b ... OK
Could not find PCI in device tree
Could not find PCI in device tree
Using machid 0x8040000 from environment
可以看到 结尾地址 4a88123c接近栈顶0x4A882A80。差距6212。
查看uboot代码 common/image
int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len,
ulong *initrd_start, ulong *initrd_end)
{
char *s;
ulong initrd_high;
int initrd_copy_to_ram = 1;
if ((s = getenv("initrd_high")) != NULL) {
/* a value of "no" or a similar string will act like 0,
* turning the "load high" feature off. This is intentional.
*/
initrd_high = simple_strtoul(s, NULL, 16);
if (initrd_high == ~0)
initrd_copy_to_ram = 0;
} else {
/* not set, no restrictions to load high */
initrd_high = ~0;
}
if (rd_data) {
if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */
debug(" in-place initrd\n");
*initrd_start = rd_data;
*initrd_end = rd_data + rd_len;
lmb_reserve(lmb, rd_data, rd_len);
} else {
if (initrd_high)
*initrd_start = (ulong)lmb_alloc_base(lmb,
rd_len, 0x1000, initrd_high);
else
*initrd_start = (ulong)lmb_alloc(lmb, rd_len,
0x1000);
if (*initrd_start == 0) {
puts("ramdisk - allocation error\n");
goto error;
}
bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK);
*initrd_end = *initrd_start + rd_len;
printf(" Loading Ramdisk to %08lx, end %08lx ... ",
*initrd_start, *initrd_end);
如果
没有定义initrd_high环境变量,则使用lmb_alloc_base(lmb,rd_len, 0x1000, ~0)分配起始地址
定义了环境initrd_high环境变量,且为0xffffffff。则initrd_copy_to_ram=0,保持地址不变。
定义了环境initrd_high环境变量,且为0,则使用lmb_alloc(lmb, rd_len,0x1000);分配起始地址。
定义了环境initrd_high环境变量,且不为0,则使用lmb_alloc_base(lmb,rd_len, 0x1000, ~0)分配起始地址
参考文档2,移植了内核的lmb到uboot。作为启动镜像的内存管理工具。
lmb包括两个类型,
#define MAX_LMB_REGIONS 8
struct lmb_property {
phys_addr_t base;
phys_size_t size;
};
struct lmb_region {
unsigned long cnt;
phys_size_t size;
struct lmb_property region[MAX_LMB_REGIONS+1];
};
struct lmb {
struct lmb_region memory;
struct lmb_region reserved;
};
memory是总内存区域,reserved是保留内存区域。分配的时候从总内存区域分配,但是不和保留内存区域重叠。
common/bootm.c
设置lmb
static void boot_start_lmb(bootm_headers_t *images)
{
ulong mem_start;
phys_size_t mem_size;
lmb_init(&images->lmb);
mem_start = getenv_bootm_low();
mem_size = getenv_bootm_size();
lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size);
arch_lmb_reserve(&images->lmb);
board_lmb_reserve(&images->lmb);
}
bootm_low和bootm_size默认没有定义,会添加整个dram内存区域到lmb。
arch/arm/lib/bootm.c
void arch_lmb_reserve(struct lmb *lmb)
{
ulong sp;
/*
* Booting a (Linux) kernel image
*
* Allocate space for command line and board info - the
* address should be as high as possible within the reach of
* the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
* memory, which means far enough below the current stack
* pointer.
*/
sp = get_sp();
debug("## Current stack ends at 0x%08lx ", sp);
/* adjust sp by 4K to be safe */
sp -= 4096;
lmb_reserve(lmb, sp,
gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp);
}
添加保留区域。可以看到添加sp-4K到dram结尾为保留区域。
分配
__lmb_alloc_base()
从后往前遍历memory region。一个region内部从高往低分配。并确保不和reserved重叠。
根据文档1。
内核会解压到TEXT_OFFSET。如果当前的压缩内核区域和解压后的区域重叠,则会将当前的压缩的内核往后移动。
IPQ5018的TEXT_OFFSET定义为:0x01208000。因为将fit镜像加载到0X44000000,和内核的解压地址有40多M的距离,
不会发生重叠。
initramfs默认会移动到lmb的高区。高区是顶端是 sp-4K的位置。且对齐到4K。当代码的运行栈大于4K时,可能会写坏。initramfs。
可以手动设置initrd_high环境变量,使其离sp更远一些。比如sp为0x4A882A80,设置initrd_high为0x4a870000
设置之后initramfs的加载位置降低了。不会被sp写坏:
Loading Ramdisk to 4a3ad000, end 4a86f8bc ... OK
启动后/web/wifi.htm正常。且无Initramfs unpacking failed: XZ-compressed data is corrupt打印。
将initramfs的load address设置为44000000后,出现了Error: ramdisk overwritten打印。原因是fit镜像加载在44000000。出现了重叠。
导致common/image-fit.c 抱怨。实际上,initramfs不需要load address和entry address。initramfs会被拷贝到high。将其设置为0即可。
IPQ5018# run u
Port1 Down Speed :10M Half duplex
Port3 Up Speed :1000M Full duplex
Using eth1 device
TFTP from server 192.168.10.100; our IP address is 192.168.10.1
Filename 'ipq50xx_32.itb'.
Load address: 0x44000000
Loading: *
Got TFTP_OACK: TFTP remote port: changes from 69 to 56069
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#####################
8.5 MiB/s
done
Bytes transferred = 7929828 (78ffe4 hex)
IPQ5018# bootm
## Loading kernel from FIT Image at 44000000 ...
Using 'config-1' configuration
Trying 'kernel' kernel subimage
Description: Linux kernel 5.15.38
Type: Kernel Image
Compression: uncompressed
Data Start: 0x440000c8
Data Size: 2889808 Bytes = 2.8 MiB
Architecture: ARM
OS: Linux
Load Address: 0x43208000
Entry Point: 0x43208000
Hash algo: sha1
Hash value: 0401e27bc01853c8c05ab27f0394940653444cff
Verifying Hash Integrity ... sha1+ OK
## Loading ramdisk from FIT Image at 44000000 ...
Using 'config-1' configuration
Trying 'initramfs' ramdisk subimage
Description: Compressed Initramfs
Type: RAMDisk Image
Compression: uncompressed
Data Start: 0x442cc834
Data Size: 4993596 Bytes = 4.8 MiB
Architecture: ARM
OS: Linux
Load Address: 0x44000000
Entry Point: 0x44000000
Hash algo: sha1
Hash value: bdb903aee1c5347c31d13d9bc03c89eb765a29f2
Verifying Hash Integrity ... sha1+ OK
Error: ramdisk overwritten
Ramdisk image is corrupt or invalid
参考:
【1】
linusw. How the ARM32 Linux kernel decompresses. 2020/8/13.
https://people.kernel.org/linusw/how-the-arm32-linux-kernel-decompresses
【2】
Introduce lmb from linux kernel for memory mgmt of boot images
https://lists.denx.de/pipermail/u-boot/2008-February/030053.html