24 使用 Wic 创建分区镜像

用 OpenEmbedded 构建系统为某个硬件目标创建镜像,并不意味着设备可以直接启动 该镜像。物理设备接受和启动镜像的方式取决于具体硬件,通常可从硬件资料了解 所需镜像格式。如果设备需要在 SD 卡、闪存或硬盘上使用多个分区,可以通过 OpenEmbedded 镜像创建工具 Wic 生成正确分区的镜像。

wic 根据现有 OpenEmbedded 构建产物生成分区镜像。分区操作由 OpenEmbedded kickstart 文件(.wks)中的命令驱动。可以直接在命令行 指定该文件,也可选择预置文件,见 “使用已有 kickstart 文件生成镜像” 中的 wic list images。将命令应用到一组构建产物后,会生成一个或多个 可直接写入介质并用于特定系统的镜像。

备注

kickstart 文件参考见 Yocto Project 参考手册的 “OpenEmbedded Kickstart(.wks)参考” 一章。

wic 及其基础设施本来就不是封闭完整的功能集合:它面向定制镜像生成, 设计上可通过插件接口完全扩展。插件信息见 “使用 Wic 插件接口”。

本节介绍 Wic 背景、运行前提、工具用法、插件接口,并提供多个使用示例。

24 背景

以下背景知识不是使用 Wic 的前提,但可能有助于了解它。

  • "Wic" 源于 OpenEmbedded Image Creator(oeic)。由于 "oeic" 难记也 难读,双元音 "oe" 被替换为字母 "w"。

  • Wic 大致基于 Meego Image Creator(mic)框架,但经过大量修改,直接 使用 OpenEmbedded 构建产物,不再执行产物中已经完成的软件包安装和配置。

  • Wic 是完全独立的工具,最初用于以更易用、更灵活的方式替代 OE-Core image-live 类中的已有功能。与原有脚本相比,Wic 使用基于 Redhat kickstart 语法的通用分区语言实现相应功能。

24 要求

配合 OpenEmbedded 构建系统使用 Wic,必须满足以下要求:

  • 环境及 Build Directory 已准备就绪,如同即将运行 bitbake。 详见 初始化构建环境。

  • 让 Shell 可以使用 wic 命令行工具:

    1. 首先运行:

      $ bitbake wic-native -c addto_recipe_sysroot
      
    2. 检查是否可通过 oe-run-native 使用 wic:

      $ oe-run-native wic-native wic --help
      
    3. 为上述命令定义本地别名:

      $ alias wic="oe-run-native wic-native wic"
      

      随后即可透明地使用 OpenEmbedded Build System 提供的 wic。

  • 构建产物必须已经存在,通常意味着已经用 OpenEmbedded 构建过镜像,例如 core-image-minimal。先构建镜像再用 Wic 创建镜像看似重复,但当前 Wic 需要 OpenEmbedded 构建系统生成的产物形式。

  • 在 configuration file 或镜像配方中,将 "wic" 加入 IMAGE_FSTYPES。

  • 通过 WKS_FILE 指定 Wic kickstart 文件 名称。 如果不同层可能提供多个候选文件,则通过 WKS_FILES 列出所有可能名称。

    这些设置通常放在机器 configuration file 中。

24 获取帮助

直接输入 wic,或使用以下帮助参数,可以查看一般帮助:

$ wic -h
$ wic --help
$ wic help

Wic 当前支持七个命令:cp、create、help、list、ls、rm 和 write。除 "help" 外,其余命令可用以下形式获取帮助:

$ wic help command

例如,查看 write 帮助:

$ wic help write

Wic 支持 overview、plugins 和 kickstart 三个帮助主题,语法为:

$ wic help topic

例如,查看概览帮助:

$ wic help overview

还可以通过 list 查看单个镜像的帮助。先列出可用 Wic 镜像:

$ wic list images
  genericx86                                 Create an EFI disk image for genericx86*
  beaglebone-yocto                           Create SD card image for Beaglebone
  qemuriscv                                  Create qcow2 image for RISC-V QEMU machines
  mkefidisk                                  Create an EFI disk image
  qemuloongarch                              Create qcow2 image for LoongArch QEMU machines
  directdisk-multi-rootfs                    Create multi rootfs image using rootfs plugin
  directdisk                                 Create a 'pcbios' direct disk image
  efi-bootdisk
  mkhybridiso                                Create a hybrid ISO image
  directdisk-gpt                             Create a 'pcbios' direct disk image
  systemd-bootdisk                           Create an EFI disk image with systemd-boot
  sdimage-bootpart                           Create SD card image with a boot partition
  qemux86-directdisk                         Create a qemu machine 'pcbios' direct disk image
  directdisk-bootloader-config               Create a 'pcbios' direct disk image with custom bootloader config

得知镜像列表后,结合 help 查看具体镜像。以下命令查看 "beaglebone-yocto":

$ wic list beaglebone-yocto help

Creates a partitioned SD card image for Beaglebone.
Boot files are located in the first vfat partition.

24 运行模式

根据需要对构建产物的选择进行多大程度的控制,Wic 提供 Raw 和 Cooked 两种模式:

  • Raw 模式: 通过命令行参数显式指定构建产物。

  • Cooked 模式: 根据当前 MACHINE 和镜像名称自动定位构建产物。 只需提供 kickstart 文件及产物来源镜像的名称。

无论哪种模式,都必须预先准备好构建产物。

24 Raw 模式

Raw 模式允许通过 wic 命令行指定所有分区,主要适用于内核在 Yocto Project Build Directory 之外构建的情况。可以指向任意内核、根文件系统等 位置;而 Cooked 模式会在 Build Directory 中查找,例如 tmp/deploy/images/machine。

Raw 模式的一般命令形式为:

$ wic create wks_file options ...

  Where:

     wks_file:
        An OpenEmbedded kickstart file.  You can provide
        your own custom file or use a file from a set of
        existing files as described by further options.

     optional arguments:
       -h, --help            show this help message and exit
       -o OUTDIR, --outdir OUTDIR
                             name of directory to create image in
       -e IMAGE_NAME, --image-name IMAGE_NAME
                             name of the image to use the artifacts from e.g. core-
                             image-sato
       -r ROOTFS_DIR, --rootfs-dir ROOTFS_DIR
                             path to the /rootfs dir to use as the .wks rootfs
                             source
       -b BOOTIMG_DIR, --bootimg-dir BOOTIMG_DIR
                             path to the dir containing the boot artifacts (e.g.
                             /EFI or /syslinux dirs) to use as the .wks bootimg
                             source
       -k KERNEL_DIR, --kernel-dir KERNEL_DIR
                             path to the dir containing the kernel to use in the
                             .wks bootimg
       -n NATIVE_SYSROOT, --native-sysroot NATIVE_SYSROOT
                             path to the native sysroot containing the tools to use
                             to build the image
       -s, --skip-build-check
                             skip the build check
       -f, --build-rootfs    build rootfs
       -c {gzip,bzip2,xz}, --compress-with {gzip,bzip2,xz}
                             compress image with specified compressor
       -m, --bmap            generate .bmap
       --no-fstab-update     Do not change fstab file.
       -v VARS_DIR, --vars VARS_DIR
                             directory with <image>.env files that store bitbake
                             variables
       -D, --debug           output debug information

备注

运行 Wic 无需 root 权限,实际上也不应以 root 身份使用它。

24 Cooked 模式

Cooked 模式使用 Build Directory 中的产物,不必在命令中指定内核 或根文件系统的位置。只需提供 kickstart 文件,并通过 "-e" 指定产物来源 镜像的名称。Wic 在构建目录中查找,例如 tmp/deploy/images/machine。

一般命令形式如下:

$ wic create wks_file -e IMAGE_NAME

  Where:

     wks_file:
        An OpenEmbedded kickstart file.  You can provide
        your own custom file or use a file from a set of
        existing files provided with the Yocto Project
        release.

     required argument:
        -e IMAGE_NAME, --image-name IMAGE_NAME
                             name of the image to use the artifacts from e.g. core-
                             image-sato

24 使用已有 kickstart 文件

如果不想自行创建 kickstart 文件,可以使用 Wic 提供的现有文件。它们在 Yocto Project 源码仓库 中的以下两个位置:

meta-yocto/meta-yocto-bsp/files/wic
openembedded-core/files/wic

列出可用文件:

$ wic list images
  genericx86                                 Create an EFI disk image for genericx86*
  beaglebone-yocto                           Create SD card image for Beaglebone
  qemuriscv                                  Create qcow2 image for RISC-V QEMU machines
  mkefidisk                                  Create an EFI disk image
  qemuloongarch                              Create qcow2 image for LoongArch QEMU machines
  directdisk-multi-rootfs                    Create multi rootfs image using rootfs plugin
  directdisk                                 Create a 'pcbios' direct disk image
  efi-bootdisk
  mkhybridiso                                Create a hybrid ISO image
  directdisk-gpt                             Create a 'pcbios' direct disk image
  systemd-bootdisk                           Create an EFI disk image with systemd-boot
  sdimage-bootpart                           Create SD card image with a boot partition
  qemux86-directdisk                         Create a qemu machine 'pcbios' direct disk image
  directdisk-bootloader-config               Create a 'pcbios' direct disk image with custom bootloader config

使用已有文件时无需写 .wks 扩展名。以下 Raw 模式示例使用 directdisk:

$ wic create directdisk -r rootfs_dir -b bootimg_dir \
      -k kernel_dir -n native_sysroot

genericx86.wks 用于生成镜像的实际分区命令如下:

# short-description: Create an EFI disk image for genericx86*
# long-description: Creates a partitioned EFI disk image for genericx86* machines
part /boot --source bootimg_efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024
part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
part swap --ondisk sda --size 44 --label swap1 --fstype=swap

bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0"

24 使用 Wic 插件接口

可以通过插件扩展并定制 Wic 功能。本节介绍插件接口。

备注

Wic 插件分为 "source" 和 "imager" 两类,后者不在本节范围内。

source 插件用于在生成镜像时定制分区内容,将 kickstart(*.wks)中的 --source 值映射到用于填充分区的插件实现。

备注

如果插件有构建时依赖,例如本机工具或引导加载程序,必须通过 WKS_FILE_DEPENDS 指定这些依赖。

source 插件是定义在插件文件中的子类。Yocto Project 提供的插件文件见 此处。 每个文件中的 source 插件负责填充某种 Wic 镜像分区。

它们继承 openembedded-core/scripts/lib/wic/pluginbase.py 中定义的 SourcePlugin 类。例如,bootimg_efi.py 中的 BootimgEFIPlugin 就是该类的子类。

也可以在源码仓库之外的外部层中实现 source 插件,只需将文件放在该层的 scripts/lib/wic/plugins/source/ 目录,Wic 就能使用其中的插件。

需要调用分区专用实现时,Wic 查找与该分区 kickstart 命令 --source 参数同名的插件。例如:

part /boot --source bootimg_pcbios --ondisk sda --label boot --active --align 1024

这将调用 bootimg_pcbios.py 中匹配的 bootimg_pcbios source 插件 定义的类方法。

以下是对应插件定义,以及 Wic 准备分区时调用的一个实现专用方法示例:

             .
             .
             .
class BootimgPcbiosPlugin(SourcePlugin):
    """
    Create MBR boot partition and install syslinux on it.
    """

   name = 'bootimg_pcbios'
             .
             .
             .
    @classmethod
    def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
                             oe_builddir, bootimg_dir, kernel_dir,
                             rootfs_dir, native_sysroot):
        """
        Called to do the actual content population for a partition i.e. it
        'prepares' the partition to be incorporated into the image.
        In this case, prepare content for legacy bios boot partition.
        """
             .
             .
             .

如果子类插件没有实现某个函数,Wic 会调用父类中的默认实现。这也是所有 source 插件都继承 SourcePlugin 的原因。

pluginbase.py 中的 SourcePlugin 定义了一组供插件实现或覆盖的方法。 未实现某方法的子类会继承父类实现,详细信息可直接查看该类。

其方法包括:

  • do_prepare_partition():填充分区实际内容,准备将要装入磁盘镜像的 最终分区镜像。

  • do_configure_partition():在 do_prepare_partition() 前调用,创建 分区的自定义配置,例如 syslinux 或 grub 配置文件。

  • do_install_disk():所有分区准备并组装成磁盘镜像后调用,用于最终处理, 例如写入 MBR。

  • do_stage_partition():在 do_prepare_partition() 前调用的特殊内容 暂存钩子,通常为空。

    分区通常直接使用传入参数,例如原样使用 bootimg_dir。有时需要特殊 处理,例如还要从 bootimg_dir + /boot 获取一些文件,此钩子允许按 自定义方式暂存这些文件。

    备注

    get_bitbake_var() 可访问这种处理中可能需要的非标准变量。

可以扩展 source 插件机制,在 SourcePlugin 及对应子类中添加新钩子方法。 调用方用 plugin.get_source_plugin_methods() 查找所需方法:先创建以 方法名为键的字典,成功后字典中会填入实际方法。示例及细节见 Wic 实现。

24 Wic 示例

以下示例假定已满足 “要求” 中的要求,且预先 生成的镜像为 core-image-minimal。

24 使用已有 kickstart 文件生成镜像

本例使用 Cooked 模式和 mkefidisk kickstart 文件:

$ wic create mkefidisk -e core-image-minimal
INFO: Building wic-tools...
          .
          .
          .
INFO: The new image(s) can be found here:
  ./mkefidisk-201804191017-sda.direct

The following build artifacts were used to create the image(s):
  ROOTFS_DIR:                   /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/rootfs
  BOOTIMG_DIR:                  /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/recipe-sysroot/usr/share
  KERNEL_DIR:                   /home/stephano/yocto/build/tmp-glibc/deploy/images/qemux86
  NATIVE_SYSROOT:               /home/stephano/yocto/build/tmp-glibc/work/i586-oe-linux/wic-tools/1.0-r0/recipe-sysroot-native

INFO: The image(s) were created using OE kickstart file:
  /home/stephano/yocto/openembedded-core/files/wic/mkefidisk.wks

这是最简单的创建方式:指定 kickstart 文件,并以 "-e" 指向现有构建产物, 这里使用 "qemux86" 机器。

构建完成后的输出包含镜像位置、所用产物和 kickstart 文件信息。

备注

应始终核对输出详情,确保生成的镜像完全符合预期。

随后可以将 Build Directory 中的镜像写入 U 盘或对应介质,再从其 启动。可使用 bmaptool 或 dd:

$ oe-run-native bmaptool-native bmaptool copy mkefidisk-201804191017-sda.direct /dev/sdX

或者:

$ sudo dd if=mkefidisk-201804191017-sda.direct of=/dev/sdX

备注

bmaptool 烧写方法详见 “使用 bmaptool 烧写镜像”。

24 使用修改后的 kickstart 文件

分区镜像创建由 kickstart 文件驱动,因此修改参数即可影响结果。下面通过修改 directdisk-gpt 文件演示。

如前所述,wic list images 列出已有 kickstart 文件。directdisk-gpt.wks 位于 OpenEmbedded-Core (OE-Core) 的 files/wic。可以在该目录 添加自己的文件,之后运行 wic list images 也会列出它们。

本例现有 directdisk-gpt 已满足大部分需求,但硬件需要从 sdb 启动, 而文件默认使用 sda。

先在 files/wic 中复制 directdisk-gpt.wks,然后修改指定启动磁盘的行:

$ cp /home/stephano/yocto/openembedded-core/files/wic/directdisk-gpt.wks \
     /home/stephano/yocto/openembedded-core/files/wic/directdisksdb-gpt.wks

编辑 directdisksdb-gpt.wks,将所有 "--ondisk sda" 改为 "--ondisk sdb"。本例只修改以下两行,其余保持不变:

part /boot --source bootimg_pcbios --ondisk sdb --label boot --active --align 1024
part / --source rootfs --ondisk sdb --fstype=ext4 --label platform --align 1024 --use-uuid

修改后生成 directdisksdb-gpt 镜像,命令使用 Next Unit of Computing (nuc):term:MACHINE 的 core-image-minimal 产物:

$ wic create directdisksdb-gpt -e core-image-minimal
INFO: Building wic-tools...
           .
           .
           .
Initialising tasks: 100% |#######################################| Time: 0:00:01
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 1161 tasks of which 1157 didn't need to be rerun and all succeeded.
INFO: Creating image(s)...

INFO: The new image(s) can be found here:
  ./directdisksdb-gpt-201710090938-sdb.direct

The following build artifacts were used to create the image(s):
  ROOTFS_DIR:                   /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/rootfs
  BOOTIMG_DIR:                  /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/recipe-sysroot/usr/share
  KERNEL_DIR:                   /home/stephano/yocto/build/tmp-glibc/deploy/images/qemux86
  NATIVE_SYSROOT:               /home/stephano/yocto/build/tmp-glibc/work/i586-oe-linux/wic-tools/1.0-r0/recipe-sysroot-native

INFO: The image(s) were created using OE kickstart file:
  /home/stephano/yocto/openembedded-core/files/wic/directdisksdb-gpt.wks

随后即可用 dd 将镜像写入 U 盘或其他目标介质,再启动:

$ sudo dd if=directdisksdb-gpt-201710090938-sdb.direct of=/dev/sdb
140966+0 records in
140966+0 records out
72174592 bytes (72 MB, 69 MiB) copied, 78.0282 s, 925 kB/s
$ sudo eject /dev/sdb

24 使用修改后的 kickstart 文件并在 Raw 模式运行

以下示例手动指定各构建产物,即使用 Raw 模式,同时采用修改后的 kickstart 文件。-o 使 Wic 将输出写到默认当前目录之外的位置:

$ wic create test.wks -o /home/stephano/testwic \
     --rootfs-dir /home/stephano/yocto/build/tmp/work/qemux86-poky-linux/core-image-minimal/1.0-r0/rootfs \
     --bootimg-dir /home/stephano/yocto/build/tmp/work/qemux86-poky-linux/core-image-minimal/1.0-r0/recipe-sysroot/usr/share \
     --kernel-dir /home/stephano/yocto/build/tmp/deploy/images/qemux86 \
     --native-sysroot /home/stephano/yocto/build/tmp/work/i586-poky-linux/wic-tools/1.0-r0/recipe-sysroot-native

INFO: Creating image(s)...

INFO: The new image(s) can be found here:
  /home/stephano/testwic/test-201710091445-sdb.direct

The following build artifacts were used to create the image(s):
  ROOTFS_DIR:                   /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/rootfs
  BOOTIMG_DIR:                  /home/stephano/yocto/build/tmp-glibc/work/qemux86-oe-linux/core-image-minimal/1.0-r0/recipe-sysroot/usr/share
  KERNEL_DIR:                   /home/stephano/yocto/build/tmp-glibc/deploy/images/qemux86
  NATIVE_SYSROOT:               /home/stephano/yocto/build/tmp-glibc/work/i586-oe-linux/wic-tools/1.0-r0/recipe-sysroot-native

INFO: The image(s) were created using OE kickstart file:
  test.wks

由于手动指定了产物,本例无需指定 MACHINE。

24 使用 Wic 修改镜像

直接修改 Wic 镜像可缩短开发迭代时间。例如,删除镜像中的旧内核再插入新内核, 就不必每次修改内核后都重建整个镜像。

备注

按本例修改镜像时,开发机器必须安装 mtools 软件包。

以下示例检查镜像内容,删除现有内核,再插入新内核:

  1. 列出分区: 使用 wic ls 列出镜像全部分区:

    $ wic ls tmp/deploy/images/qemux86/core-image-minimal-qemux86.wic
    Num     Start        End          Size      Fstype
     1       1048576     25041919     23993344  fat16
     2      25165824     72157183     46991360  ext4
    

    输出显示 core-image-minimal-qemux86.wic 中有两个分区。

  2. 检查特定分区: 再次使用 wic ls,以不同参数查看某个分区。

    备注

    任意 Wic 命令的用法均可按以下形式查询:

    $ wic help command
    

    例如,查看 wic ls 的各种用法:

    $ wic help ls
    

    查看第一个分区内容:

    $ wic ls tmp/deploy/images/qemux86/core-image-minimal-qemux86.wic:1
    Volume in drive : is boot
     Volume Serial Number is E894-1809
    Directory for ::/
    
    libcom32 c32    186500 2017-10-09  16:06
    libutil  c32     24148 2017-10-09  16:06
    syslinux cfg       220 2017-10-09  16:06
    vesamenu c32     27104 2017-10-09  16:06
    vmlinuz        6904608 2017-10-09  16:06
            5 files           7 142 580 bytes
                             16 582 656 bytes free
    

    输出显示五个文件,其中 vmlinuz 为内核。

    备注

    如果看到以下错误,需更新或创建 ~/.mtoolsrc,确保其中包含 "mtools_skip_check=1",再重新运行命令:

    ERROR: _exec_cmd: /usr/bin/mdir -i /tmp/wic-parttfokuwra ::/ returned '1' instead of 0
     output: Total number of sectors (47824) not a multiple of sectors per track (32)!
     Add mtools_skip_check=1 to your .mtoolsrc file to skip this test
    
  3. 删除旧内核: 使用 wic rm 删除 vmlinuz:

    $ wic rm tmp/deploy/images/qemux86/core-image-minimal-qemux86.wic:1/vmlinuz
    
  4. 添加新内核: 使用 wic cp 将更新后的内核加入镜像。内核位置取决于 构建方式:用 devtool 和 SDK 构建时,位于可扩展 SDK 的 tmp/work; 用 make 构建时,位于 workspace/sources。

    以下示例假定通过 devtool 构建:

    $ wic cp poky_sdk/tmp/work/qemux86-poky-linux/linux-yocto/4.12.12+git999-r0/linux-yocto-4.12.12+git999/arch/x86/boot/bzImage \
             build/tmp/deploy/images/qemux86/core-image-minimal-qemux86.wic:1/vmlinuz
    

    新内核加入镜像后,可以用 dd 或 bmaptool 将镜像烧写到 SD 卡或 U 盘,测试目标设备。

    备注

    bmaptool 通常比 dd 快 10 到 20 倍。