quinnwencn / blog

Apache License 2.0
0 stars 0 forks source link

01 为Layerscape制作FIT格式的kernel image和initramfs #8

Open quinnwencn opened 8 months ago

quinnwencn commented 8 months ago

1. 背景

由于NXP官方手册中并没有描述如何为Layerscape制作和使用initramfs,而initramfs在嵌入式设备中的使用又非常广泛和必要,例如,在基于ostree升级软件的方案中,就需要用initramfs去进行必要的ostree操作。因此,必须要为Layerscape制作initramfs。使用FIT格式启动的系统文件分布如下: image 接下来,我将介绍如何制作Layerscape的initramfs,并打包FIT格式镜像,和从initramfs如何切换到正常的rootfs。

2. 制作FIT格式系统镜像

2.1 制作initramfs

由于initramfs需要对硬件做初始化工作,因此不具备通用性,必须在芯片厂商提供的initramfs上改造。但是,NXP没有提供制作initramfs的源文件,但是,我们可以基于NXP提供的Layerscape SDK中buildroot编出来,步骤如下:

0 directories, 5 files

其中,cpio.gz文件就是initramfs的源文件,如果对initramfs有定制化需求,就可以对initramfs进行修改了。
## 2.2 修改initramfs
* 首先将rootfs.cpio.gz解压,它是一个gzip压缩的文件:

gunzip rootfs.cpio.gz

* 将cpio解开:

cpio -idmv < rootfs.cpio

解开后的文件列表:

$ tree . -L 1 . ├── bin ├── dev ├── etc ├── init ├── lib ├── lib64 -> lib ├── linuxrc -> bin/busybox ├── media ├── mnt ├── opt ├── proc ├── root ├── rootfs.cpio ├── run ├── sbin ├── sys ├── tmp ├── usr └── var

可以看出来,它就是一个rootfs文件系统,可以根据需要进行修改。
修改后,再重新打包initramfs:

sudo find . | sudo cpio -H newc -o | gzip -9 > initramfs.cpio.gz

完成修改后的initramfs后,就可以基于定制化后的initramfs来制作FIT格式的Image。
## 2.3 制作FIT格式的Image
FIT格式的Image需要以下三种材料:
* Kernel Image文件或者Image.gz
* 设备树文件
* initramfs
FIT格式的Image文件制作需要依赖ITS脚本,首先新建一个ITS(Image Tree Source)脚本:
`ls1046ardb_fit.its`:
```its
/dts-v1/;

/ {
        description = "kernel+dtb/fdt fit image";
        #address-cells = <1>;

        images {
                kernel {
                        description = "kernel image";
                        data = /incbin/("Image.gz");
                        type = "kernel";
                        arch = "arm64";
                        os = "linux";
                        compression = "gzip";
            load = <0x94200000>;
            entry = <0x94200000>;
            kernel-version = <1>;
                        hash {
                                algo = "sha256";
                        };
                };

        initrd {
            description = "initrd for arm64";
            data = /incbin/("initramfs.cpio.gz");
            type = "ramdisk";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            load = <0x80000000>;
            entry = <0x80000000>;
            hash {
                algo = "sha256";
            };
        };

                fdt {
                        description = "dtb blob";
                        data = /incbin/("fsl-ok1046a-1133-5a59-c2.dtb");
                        type = "flat_dt";
                        arch = "arm64";
                        compression = "none";
                        load = <0xA0000000>;
                        fdt-version = <1>;
                        hash {
                                algo = "sha256";
                        };
                };
        };
        configurations {
                default = "standard";
                standard {
                        description = "Standard Boot";
                        kernel = "kernel";
                        fdt = "fdt";
                        ramdisk = "initrd";
                        hash {
                                algo = "sha256";
                        };
                };
        };
};

生成FIT格式的Image:

mkimage -f ls1046ardb_fit.its image.ub

最终将生成一个image.ub文件,我们可以用dumpimage来查看:

$ dumpimage -l image.ub 
FIT description: kernel+dtb/fdt fit image
Created:         Thu Jan 11 16:29:34 2024
 Image 0 (kernel)
  Description:  kernel image
  Created:      Thu Jan 11 16:29:34 2024
  Type:         Kernel Image
  Compression:  gzip compressed
  Data Size:    12277242 Bytes = 11989.49 KiB = 11.71 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0x94200000
  Entry Point:  0x94200000
  Hash algo:    sha256
  Hash value:   c346c152ae2f4c792e7dfd5b6730111fc9e3d2ece39c3bbf57e150a671a7dbb0
 Image 1 (initrd)
  Description:  initrd for arm64
  Created:      Thu Jan 11 16:29:34 2024
  Type:         RAMDisk Image
  Compression:  gzip compressed
  Data Size:    5387897 Bytes = 5261.62 KiB = 5.14 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0x80000000
  Entry Point:  0x80000000
  Hash algo:    sha256
  Hash value:   1dcc074ee0dc129d0e68356250935bee9abb35787c294272ed5a7402ad177663
 Image 2 (fdt)
  Description:  dtb blob
  Created:      Thu Jan 11 16:29:34 2024
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    40530 Bytes = 39.58 KiB = 0.04 MiB
  Architecture: AArch64
  Load Address: 0xa0000000
  Hash algo:    sha256
  Hash value:   1be175107f81f52942ca162e11089b91bae9176dfab20b1fdd8b6a0fdf73ee0a
 Default Configuration: 'standard'
 Configuration 0 (standard)
  Description:  Standard Boot
  Kernel:       kernel
  Init Ramdisk: initrd
  FDT:          fdt
  Hash algo:    sha256
  Hash value:   unavailable

可以看到确实是一个FIT格式的镜像了。

3 启动FIT格式的镜像

将SD卡进行分区,分区信息如下:

Disk /dev/sdb: 30.26 GiB, 32480690176 bytes, 63438848 sectors
Disk model: STORAGE DEVICE  
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5dc644d9

Device     Boot   Start      End  Sectors  Size Id Type
/dev/sdb1         20480  2117631  2097152    1G  c W95 FAT32 (LBA)
/dev/sdb2       2117632 63438847 61321216 29.2G 83 Linux

其中,分区1用于存放FIT格式的Image镜像,分区2用于存放真正的rootfs,现在该rootfs是一个ubuntu rootfs。

3.1 刷写文件

3.2 启动测试

为了成功从FIT格式的镜像启动,我们需要设置uboot环境变量:

add_key: No su[ 3.666973] __wake_up_parent+0x0/0x30 ch device cat: [ 3.672100] el0_svc_common.constprop.0+0x78/0x1a0 can't open '/mnt[ 3.678269] do_el0_svc+0x24/0x90 /etc/encrypted_k[ 3.682961] el0_svc+0x14/0x20 ey.blob': No suc[ 3.687393] el0_sync_handler+0xb0/0xb8 h file or direct[ 3.692606] el0_sync+0x178/0x180 ory add_key: No[ 3.697302] SMP: stopping secondary CPUs such device /i[ 3.702604] Kernel Offset: disabled [ 3.707466] CPU features: 0x0240022,21002000 [ 3.711726] Memory Limit: none [ 3.714774] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---

问题是在启动initramfs的时候,init的权限不是root,我们回过头看,原来是解压initramfs时没有使用root权限解压:``` cpio -idmv < rootfs.cpio```,这里对解压和打包都用root权限重新做一遍,并且,手动将属主和属组都改为了root:

sudo cpio -idmv < rootfs.cpio sudo chown -R root ./ sudo chgrp -R root ./ sudo find . | sudo cpio -H newc -o | gzip -9 > initramfs.cpio.gz

可以看到,确实从FIT Image启动了,也启动了initramfs:

Hit any key to stop autoboot: 0 switch to partitions #0, OK mmc0 is current device 17708815 bytes read in 768 ms (22 MiB/s)

Loading kernel from FIT Image at b0000000 ...

Using 'standard' configuration Trying 'kernel' kernel subimage Description: kernel image Type: Kernel Image Compression: gzip compressed Data Start: 0xb00000c8 Data Size: 12277242 Bytes = 11.7 MiB Architecture: AArch64 OS: Linux Load Address: 0x94200000 Entry Point: 0x94200000 Hash algo: sha256 Hash value: c346c152ae2f4c792e7dfd5b6730111fc9e3d2ece39c3bbf57e150a671a7dbb0 Verifying Hash Integrity ... sha256+ OK

Loading ramdisk from FIT Image at b0000000 ...

Using 'standard' configuration Trying 'initrd' ramdisk subimage Description: initrd for arm64 Type: RAMDisk Image Compression: gzip compressed Data Start: 0xb0bb57d0 Data Size: 5388952 Bytes = 5.1 MiB

但是,initramfs里要对`/dev/mmcblk0p4`做mount操作,我们没有p4,因此当然失败了:

mount: mounting securityfs on /sys/kernel/security failed: No such file or directory partnum: 4[ 3.607799] mmc0: new ultra high speed SDR104 SDHC card at address 59b4

[ 3.608911] /dev/mmcblk0p4: Can't open blockdev [ 3.615336] mmcblk0: mmc0:59b4 SD3.0 30.3 GiB mount: mounting [ 3.625300] mmcblk0: p1 p2 /dev/mmcblk0p4 on /mnt failed: No such device or address Loadin[ 3.633742] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 g blobs cat: ca[ 3.641543] CPU: 2 PID: 1 Comm: busybox Not tainted 5.10.35 #3 n't open '/mnt/e[ 3.648751] Hardware name: LS1046A RDB Board (DT) tc/secure_key.bl[ 3.654835] Call trace: ob': No such fil[ 3.658667] dump_backtrace+0x0/0x1a8 e or directory [ 3.663700] show_stack+0x18/0x68 add_key: No such[ 3.668393] dump_stack+0xd0/0x12c device cat: ca[ 3.673171] panic+0x16c/0x334 n't open '/mnt/e[ 3.677603] do_exit+0x9ec/0xa08 tc/encrypted_key[ 3.682208] do_group_exit+0x44/0xa0 .blob': No such [ 3.687161] __wake_up_parent+0x0/0x30 file or director[ 3.692289] el0_svc_common.constprop.0+0x78/0x1a0 y add_key: No s[ 3.698457] do_el0_svc+0x24/0x90 uch device /ini[ 3.703149] el0_svc+0x14/0x20 t: line 54: can'[ 3.707581] el0_sync_handler+0xb0/0xb8 t create /sys/ke[ 3.712796] el0_sync+0x178/0x180 rnel/security/ev[ 3.717490] SMP: stopping secondary CPUs m: nonexistent d[ 3.722793] Kernel Offset: disabled [ 3.727655] CPU features: 0x0240022,21002000 [ 3.731915] Memory Limit: none [ 3.734963] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100 ]---

原因是,原始的initramfs里的init,默认真正的rootfs在mmcblk0p4上,所以去mount了mmcblk0p4,修改之后,就正常mount和启动了:

[ OK ] Started Internet superserver. [ OK ] Started Weston Wayland Compositor (on tty7). [ OK ] Finished Set console scheme. [ OK ] Created slice system-getty.slice. [ OK ] Started Getty on tty1. [ OK ] Reached target Login Prompts. [FAILED] Failed to start Advanced I�…1X/WPA/WPA2/EAP Authenticator. See 'systemctl status hostapd.service' for details. [ OK ] Finished Set console font and keymap. [ OK ] Started Lighttpd Daemon. [ OK ] Started OpenBSD Secure Shell server. [ OK ] Created slice User Slice of UID 0. Starting User Runtime Directory /run/user/0... [ OK ] Finished Resize root files�…m to fit available disk space. [ OK ] Finished User Runtime Directory /run/user/0.

NXP LSDK 2108 main (custom based on ubuntu 20.04) forlinx login:

quinnwencn commented 6 months ago

对于Image格式的kernel,可以使用以下命令手动压缩成gzip格式的kernel:

cat Image | gzip -n -f -9 > Image.gz

关于内核镜像的格式和制作,请看内核镜像的格式和制作

quinnwencn commented 6 months ago

当然,我们也可以从FIT格式的镜像中提取对应的镜像,例如提取initramfs.cpio.gz:

dumpimage  -T flat_dt -p 1 image.ub -o initramfs.cpio.gz

这里-p指的是在制作image.ub时initramfs的位置,-o是initramfs的名字,这是提取出来的名称,可以自己制定2,如果忘记了对应的位置,那么可以用以下命令查看:

dumpimage -l image.ub
quinnwencn commented 6 months ago

上述将rootfs制作成initramfs.cpio.gz的操作会在initramfs.cpio.gz中多一份initramfs.cpio,可以采用以下方式制作更小的:

sudo find . -print | sudo cpio --create --format=newc | gzip -9 > ../initramfs.cpio.gz