使用mkbootfs製做ramdisk根文件系統

環境

Qemu:QEMU emulator version 3.1.0
Linux:Linux-4.14.13
工具鏈:arm-none-linux-gnueabi-gcc  (gcc version 4.8.3 20140320)
Android:7.1.2
busybox:BusyBox v1.24.2
 

概述

         Android系統使用的根文件系統是用mkbootfs和minigzip製做的,其中mkbootfs用於將根文件系統打包成cpio格式,也能夠用cpio工具來打包,未來Linux內核在啓動時會調用init/initramfs.c中的函數unpack_to_rootfs對cpio格式進行解包[調用路徑:start_kernel --> rest_init --> kernel_init --> kernel_init_freeable --> do_basic_setup --> do_initcalls --> do_initcall_level --> do_one_initcall --> populate_rootfs --> unpack_to_rootfs],在內存中構造出根文件系統結構,mkbootfs工具是Android本身實現的,支持的功能也比cpio弱不少。minigzip也是Android實現的一個壓縮工具,是對gzip的簡化。
        minigzip的源碼位於:external/zlib/
        mkbootfs的源碼位於:system/core/cpio/mkbootfs.c
        不過須要注意mkbootfs的功能要比cpio弱,從mkbootfs.c的代碼註釋中能夠看出來:
/* NOTES
**
** - see buffer-format.txt from the linux kernel docs for
**   an explanation of this file format
** - dotfiles are ignored
** - directories named 'root' are ignored
** - device notes, pipes, etc are not supported (error)
*/

 

    上面的信息說明了以下幾點:linux

1. 對cpio格式的說明,Linux內核文檔Documentation/early-userspace/buffer-format.txt中有詳細說明說明:
The full format of the initramfs buffer is defined by the following
grammar, where:
        *       is used to indicate "0 or more occurrences of"
        (|)     indicates alternatives
        +       indicates concatenation
        GZIP()  indicates the gzip(1) of the operand
        ALGN(n) means padding with null bytes to an n-byte boundary

        initramfs  := ("\0" | cpio_archive | cpio_gzip_archive)*

        cpio_gzip_archive := GZIP(cpio_archive)

        cpio_archive := cpio_file* + (<nothing> | cpio_trailer)

        cpio_file := ALGN(4) + cpio_header + filename + "\0" + ALGN(4) + data

        cpio_trailer := ALGN(4) + cpio_header + "TRAILER!!!\0" + ALGN(4)

2. 名爲"."的文件會被忽略,不會進行打包。android

3. 名爲"root"的文件也會被忽略,不會進行打包。
4. 不支持設備節點以及管道文件。因此在使用mkbootfs時,須要確保被打包的路徑下沒有這兩種文件,否者會致使錯誤,而且Linux內核也沒法正常訪問指定的文件。
 
   此外,在使用mkbootfs時還須要注意的時,mkbootfs會對打包的文件的權限以及uid和gid進行修改(在函數fix_stat中),有可能會遇到原本具有可執行權限的文件,在用mkbootfs打包成cpio格式後,該文件的可執行權限丟失了,好比/etc/init.d/rcS。
 

用法

lib_path=`readlink -f ./lib`
bin_path=`readlink -f ./bin/`

export LD_LIBRARY_PATH=${lib_path}:$LD_LIBRARY_PATH
export PATH=${bin_path}:$PATH

rm -f ramdisk.img ramdisk.cpio
rm -rf ./tmp/

# pack
pushd rootfs2
mkbootfs -f ../config.txt . | minigzip > ../ramdisk.img
mkbootfs -f ../config.txt . > ../ramdisk.cpio
popd

# unpack
mkdir -p tmp
pushd tmp
cpio -i < ../ramdisk.cpio
popd

 

其中config.txt是一個配置文件,內容以下:express

etc/init.d/rcS 0 0 0755
 0 0 0744

第1行,把"etc/init.d/rcS"文件的uid設置爲0,gid設置爲0,權限設置爲0755數組

第2行,全部其餘文件的uid都設置0,gid也設置爲0,權限設置爲0744
須要注意的是,第2行開始有一個空格,而且須要放在最後一行,對具體文件的設置要放到前面。詳見mkbootfs.c中函數read_canned_config。
 
    若是沒有個指定config.txt,那麼會使用Android代碼中自帶的配置機制[在fix_stat中會調用fs_config],功能要比config.txt更完善,若是在使用mkbootfs時還設置了-d <path>,那麼函數fs_config會優先使用<path>/system/etc/fs_config_dirs和<path>/system/etc/fs_config_files中描述的規則,前者針對目錄,後者針對其餘文件。若是沒有給mkbootfs傳遞-d <path>參數,那麼使用Android在源碼中指定的規則,它們存放在兩個數組中,一個針對目錄,一個針對其餘文件:
 
針對目錄的配置規則:
/* Rules for directories.
** These rules are applied based on "first match", so they
** should start with the most specific path and work their
** way up to the root.
*/

static const struct fs_path_config android_dirs[] = { 
    { 00770, AID_SYSTEM, AID_CACHE,  0, "cache" },
    { 00500, AID_ROOT,   AID_ROOT,   0, "config" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
    { 00771, AID_ROOT,   AID_ROOT,   0, "data/dalvik-cache" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
    { 00771, AID_SHELL,  AID_SHELL,  0, "data/local/tmp" },
    { 00771, AID_SHELL,  AID_SHELL,  0, "data/local" },
    { 01771, AID_SYSTEM, AID_MISC,   0, "data/misc" },
    { 00770, AID_DHCP,   AID_DHCP,   0, "data/misc/dhcp" },
    { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
    { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
    { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest" },
    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest64" },
    ... ...

 

針對其餘文件的規則:bash

static const struct fs_path_config android_files[] = {
    { 00440, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.rc" },
    { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.sh" },
    { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.ril" },
    { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
    { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
    { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
    { 00444, AID_ROOT,      AID_ROOT,      0, conf_dir + 1 },
    { 00444, AID_ROOT,      AID_ROOT,      0, conf_file + 1 },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
    { 00644, AID_MEDIA_RW,  AID_MEDIA_RW,  0, "data/media/*" },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-ephemeral/*" },
    { 00644, AID_APP,       AID_APP,       0, "data/data/*" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest/tests.txt" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/tests.txt" },
    ... ...

 

驗證

1.  使用壓縮格式的ramdisk.img (mkbootfs -f ../config.txt . | minigzip > ../ramdisk.img)
kernel_dir=./Linux-4.14.13
kernel_image=${kernel_dir}/arch/arm/boot/zImage
dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb
qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm \
    -M vexpress-a9 \
    -m 1024M \
    -smp 1 \
    -kernel ${kernel_image} \
    -nographic \
    -append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/init ignore_loglevel" \
    -initrd ./rootfs/ramdisk.img \
    -dtb ${dtb_image}

 

部分啓動log:
[    0.609270] Trying to unpack rootfs image as initramfs...
[    0.965940] Freeing initrd memory: 3616K
 
2. 使用非壓縮的ramdisk.cpio (mkbootfs -f ../config.txt . > ../ramdisk.cpio)
kernel_dir=./Linux-4.14.13
kernel_image=${kernel_dir}/arch/arm/boot/zImage
dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb
qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm \
    -M vexpress-a9 \
    -m 1024M \
    -smp 1 \
    -kernel ${kernel_image} \
    -nographic \
    -append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/init ignore_loglevel" \
    -initrd ./rootfs/ramdisk.cpio \
    -dtb ${dtb_image}
 
部分啓動log:
[    0.610055] Trying to unpack rootfs image as initramfs...
[    0.760468] Freeing initrd memory: 7040K

 

此外,Linux內核自己也支持在編譯時將指定的外部根文件系統編譯成cpio.gz格式,而後跟Linux內核連接到一塊兒,在啓動時就不須要指定initrd了,Linux內核支持的打包工具usr/gen_init_cpio.c也比mkbootfs強大:
配置內核:
General setup --->
        [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
        (/home/pengdonglin/aarch32/rootfs/rootfs) Initramfs source file(s)
 
Linux內核對這部分的處理請參考usr/Makefile,下面是加入上面的配置以後的部份內核編譯log:
  GEN     usr/initramfs_data.cpio.gz
  GZIP    kernel/config_data.gz
  CHK     kernel/config_data.h
  UPD     kernel/config_data.h
  CC      kernel/configs.o
  AR      kernel/built-in.o
  AS      usr/initramfs_data.o
  AR      usr/built-in.o

 

展開:
/bin/bash ./scripts/gen_initramfs_list.sh -o usr/initramfs_data.cpio.gz  -u 0  -g 0  /home/pengdonglin/disk_ext/Qemu/aarch32/rootfs/rootfs

 

下面是測試命令:
kernel_dir=./Linux-4.14.13
kernel_image=${kernel_dir}/arch/arm/boot/zImage
dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb
qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm \
    -M vexpress-a9 \
    -m 1024M \
    -smp 1 \
    -kernel ${kernel_image} \
    -nographic \
    -append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/init ignore_loglevel" \
    -dtb ${dtb_image}

 

此外,在Documentation/filesystems/ramfs-rootfs-initramfs.txt提供了一個使用cpio打包的腳本:
185   #!/bin/sh
186 
187   # Copyright 2006 Rob Landley <rob@landley.net> and TimeSys Corporation.
188   # Licensed under GPL version 2
189 
190   if [ $# -ne 2 ]
191   then
192     echo "usage: mkinitramfs directory imagename.cpio.gz"
193     exit 1
194   fi
195 
196   if [ -d "$1" ]
197   then
198     echo "creating $2 from $1"
199     (cd "$1"; find . | cpio -o -H newc | gzip) > "$2"
200   else
201     echo "First argument must be a directory"
202     exit 1
203   fi

 

完。
相關文章
相關標籤/搜索