Complete Yocto mirror with license table for TQMa6UL (2038-compliance)

- 264 license table entries with exact download URLs (224/264 resolved)
- Complete sources/ directory with all BitBake recipes
- Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl)
- Full traceability for Softwarefreigabeantrag
- GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4
- License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
This commit is contained in:
Siggi (OpenClaw Agent)
2026-03-01 20:58:18 +00:00
commit 16accb6b24
15086 changed files with 1292356 additions and 0 deletions

View File

@@ -0,0 +1,694 @@
#
# Copyright (c) 2013, Intel Corporation.
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'direct' imager plugin class for 'wic'
#
# AUTHORS
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
import logging
import os
import random
import shutil
import tempfile
import uuid
from time import strftime
from oe.path import copyhardlinktree
from wic import WicError
from wic.filemap import sparse_copy
from wic.ksparser import KickStart, KickStartError
from wic.pluginbase import PluginMgr, ImagerPlugin
from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd
logger = logging.getLogger('wic')
class DirectPlugin(ImagerPlugin):
"""
Install a system into a file containing a partitioned disk image.
An image file is formatted with a partition table, each partition
created from a rootfs or other OpenEmbedded build artifact and dd'ed
into the virtual disk. The disk image can subsequently be dd'ed onto
media and used on actual hardware.
"""
name = 'direct'
def __init__(self, wks_file, rootfs_dir, bootimg_dir, kernel_dir,
native_sysroot, oe_builddir, options):
try:
self.ks = KickStart(wks_file)
except KickStartError as err:
raise WicError(str(err))
# parse possible 'rootfs=name' items
self.rootfs_dir = dict(rdir.split('=') for rdir in rootfs_dir.split(' '))
self.bootimg_dir = bootimg_dir
self.kernel_dir = kernel_dir
self.native_sysroot = native_sysroot
self.oe_builddir = oe_builddir
self.debug = options.debug
self.outdir = options.outdir
self.compressor = options.compressor
self.bmap = options.bmap
self.no_fstab_update = options.no_fstab_update
self.updated_fstab_path = None
self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0],
strftime("%Y%m%d%H%M"))
self.workdir = self.setup_workdir(options.workdir)
self._image = None
self.ptable_format = self.ks.bootloader.ptable
self.parts = self.ks.partitions
# as a convenience, set source to the boot partition source
# instead of forcing it to be set via bootloader --source
for part in self.parts:
if not self.ks.bootloader.source and part.mountpoint == "/boot":
self.ks.bootloader.source = part.source
break
image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
self._image = PartitionedImage(image_path, self.ptable_format,
self.parts, self.native_sysroot,
options.extra_space)
def setup_workdir(self, workdir):
if workdir:
if os.path.exists(workdir):
raise WicError("Internal workdir '%s' specified in wic arguments already exists!" % (workdir))
os.makedirs(workdir)
return workdir
else:
return tempfile.mkdtemp(dir=self.outdir, prefix='tmp.wic.')
def do_create(self):
"""
Plugin entry point.
"""
try:
self.create()
self.assemble()
self.finalize()
self.print_info()
finally:
self.cleanup()
def update_fstab(self, image_rootfs):
"""Assume partition order same as in wks"""
if not image_rootfs:
return
fstab_path = image_rootfs + "/etc/fstab"
if not os.path.isfile(fstab_path):
return
with open(fstab_path) as fstab:
fstab_lines = fstab.readlines()
updated = False
for part in self.parts:
if not part.realnum or not part.mountpoint \
or part.mountpoint == "/" or not (part.mountpoint.startswith('/') or part.mountpoint == "swap"):
continue
if part.use_uuid:
if part.fsuuid:
# FAT UUID is different from others
if len(part.fsuuid) == 10:
device_name = "UUID=%s-%s" % \
(part.fsuuid[2:6], part.fsuuid[6:])
else:
device_name = "UUID=%s" % part.fsuuid
else:
device_name = "PARTUUID=%s" % part.uuid
elif part.use_label:
device_name = "LABEL=%s" % part.label
else:
# mmc device partitions are named mmcblk0p1, mmcblk0p2..
prefix = 'p' if part.disk.startswith('mmcblk') else ''
device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
opts = part.fsopts if part.fsopts else "defaults"
passno = part.fspassno if part.fspassno else "0"
line = "\t".join([device_name, part.mountpoint, part.fstype,
opts, "0", passno]) + "\n"
fstab_lines.append(line)
updated = True
if updated:
self.updated_fstab_path = os.path.join(self.workdir, "fstab")
with open(self.updated_fstab_path, "w") as f:
f.writelines(fstab_lines)
if os.getenv('SOURCE_DATE_EPOCH'):
fstab_time = int(os.getenv('SOURCE_DATE_EPOCH'))
os.utime(self.updated_fstab_path, (fstab_time, fstab_time))
def _full_path(self, path, name, extention):
""" Construct full file path to a file we generate. """
return os.path.join(path, "%s-%s.%s" % (self.name, name, extention))
#
# Actual implemention
#
def create(self):
"""
For 'wic', we already have our build artifacts - we just create
filesystems from the artifacts directly and combine them into
a partitioned image.
"""
if not self.no_fstab_update:
self.update_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
for part in self.parts:
# get rootfs size from bitbake variable if it's not set in .ks file
if not part.size:
# and if rootfs name is specified for the partition
image_name = self.rootfs_dir.get(part.rootfs_dir)
if image_name and os.path.sep not in image_name:
# Bitbake variable ROOTFS_SIZE is calculated in
# Image._get_rootfs_size method from meta/lib/oe/image.py
# using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
# IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
if rsize_bb:
part.size = int(round(float(rsize_bb)))
self._image.prepare(self)
self._image.layout_partitions()
self._image.create()
def assemble(self):
"""
Assemble partitions into disk image
"""
self._image.assemble()
def finalize(self):
"""
Finalize the disk image.
For example, prepare the image to be bootable by e.g.
creating and installing a bootloader configuration.
"""
source_plugin = self.ks.bootloader.source
disk_name = self.parts[0].disk
if source_plugin:
plugin = PluginMgr.get_plugins('source')[source_plugin]
plugin.do_install_disk(self._image, disk_name, self, self.workdir,
self.oe_builddir, self.bootimg_dir,
self.kernel_dir, self.native_sysroot)
full_path = self._image.path
# Generate .bmap
if self.bmap:
logger.debug("Generating bmap file for %s", disk_name)
python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3')
bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool')
exec_native_cmd("%s %s create %s -o %s.bmap" % \
(python, bmaptool, full_path, full_path), self.native_sysroot)
# Compress the image
if self.compressor:
logger.debug("Compressing disk %s with %s", disk_name, self.compressor)
exec_cmd("%s %s" % (self.compressor, full_path))
def print_info(self):
"""
Print the image(s) and artifacts used, for the user.
"""
msg = "The new image(s) can be found here:\n"
extension = "direct" + {"gzip": ".gz",
"bzip2": ".bz2",
"xz": ".xz",
None: ""}.get(self.compressor)
full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
msg += ' %s\n\n' % full_path
msg += 'The following build artifacts were used to create the image(s):\n'
for part in self.parts:
if part.rootfs_dir is None:
continue
if part.mountpoint == '/':
suffix = ':'
else:
suffix = '["%s"]:' % (part.mountpoint or part.label)
rootdir = part.rootfs_dir
msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir)
msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
logger.info(msg)
@property
def rootdev(self):
"""
Get root device name to use as a 'root' parameter
in kernel command line.
Assume partition order same as in wks
"""
for part in self.parts:
if part.mountpoint == "/":
if part.uuid:
return "PARTUUID=%s" % part.uuid
elif part.label and self.ptable_format != 'msdos':
return "PARTLABEL=%s" % part.label
else:
suffix = 'p' if part.disk.startswith('mmcblk') else ''
return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
def cleanup(self):
if self._image:
self._image.cleanup()
# Move results to the output dir
if not os.path.exists(self.outdir):
os.makedirs(self.outdir)
for fname in os.listdir(self.workdir):
path = os.path.join(self.workdir, fname)
if os.path.isfile(path):
shutil.move(path, os.path.join(self.outdir, fname))
# remove work directory when it is not in debugging mode
if not self.debug:
shutil.rmtree(self.workdir, ignore_errors=True)
# Overhead of the MBR partitioning scheme (just one sector)
MBR_OVERHEAD = 1
# Overhead of the GPT partitioning scheme
GPT_OVERHEAD = 34
# Size of a sector in bytes
SECTOR_SIZE = 512
class PartitionedImage():
"""
Partitioned image in a file.
"""
def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
self.path = path # Path to the image file
self.numpart = 0 # Number of allocated partitions
self.realpart = 0 # Number of partitions in the partition table
self.primary_part_num = 0 # Number of primary partitions (msdos)
self.extendedpart = 0 # Create extended partition before this logical partition (msdos)
self.extended_size_sec = 0 # Size of exteded partition (msdos)
self.logical_part_cnt = 0 # Number of total logical paritions (msdos)
self.offset = 0 # Offset of next partition (in sectors)
self.min_size = 0 # Minimum required disk size to fit
# all partitions (in bytes)
self.ptable_format = ptable_format # Partition table format
# Disk system identifier
if os.getenv('SOURCE_DATE_EPOCH'):
self.identifier = random.Random(int(os.getenv('SOURCE_DATE_EPOCH'))).randint(1, 0xffffffff)
else:
self.identifier = random.SystemRandom().randint(1, 0xffffffff)
self.partitions = partitions
self.partimages = []
# Size of a sector used in calculations
self.sector_size = SECTOR_SIZE
self.native_sysroot = native_sysroot
num_real_partitions = len([p for p in self.partitions if not p.no_table])
self.extra_space = extra_space
# calculate the real partition number, accounting for partitions not
# in the partition table and logical partitions
realnum = 0
for part in self.partitions:
if part.no_table:
part.realnum = 0
else:
realnum += 1
if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
part.realnum = realnum + 1
continue
part.realnum = realnum
# generate parition and filesystem UUIDs
for part in self.partitions:
if not part.uuid and part.use_uuid:
if self.ptable_format in ('gpt', 'gpt-hybrid'):
part.uuid = str(uuid.uuid4())
else: # msdos partition table
part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
if not part.fsuuid:
if part.fstype == 'vfat' or part.fstype == 'msdos':
part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
else:
part.fsuuid = str(uuid.uuid4())
else:
#make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
if part.fstype == 'vfat' or part.fstype == 'msdos':
if part.fsuuid.upper().startswith("0X"):
part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
else:
part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
def prepare(self, imager):
"""Prepare an image. Call prepare method of all image partitions."""
for part in self.partitions:
# need to create the filesystems in order to get their
# sizes before we can add them and do the layout.
part.prepare(imager, imager.workdir, imager.oe_builddir,
imager.rootfs_dir, imager.bootimg_dir,
imager.kernel_dir, imager.native_sysroot,
imager.updated_fstab_path)
# Converting kB to sectors for parted
part.size_sec = part.disk_size * 1024 // self.sector_size
def layout_partitions(self):
""" Layout the partitions, meaning calculate the position of every
partition on the disk. The 'ptable_format' parameter defines the
partition table format and may be "msdos". """
logger.debug("Assigning %s partitions to disks", self.ptable_format)
# The number of primary and logical partitions. Extended partition and
# partitions not listed in the table are not included.
num_real_partitions = len([p for p in self.partitions if not p.no_table])
# Go through partitions in the order they are added in .ks file
for num in range(len(self.partitions)):
part = self.partitions[num]
if self.ptable_format == 'msdos' and part.part_name:
raise WicError("setting custom partition name is not " \
"implemented for msdos partitions")
if self.ptable_format == 'msdos' and part.part_type:
# The --part-type can also be implemented for MBR partitions,
# in which case it would map to the 1-byte "partition type"
# filed at offset 3 of the partition entry.
raise WicError("setting custom partition type is not " \
"implemented for msdos partitions")
if part.mbr and self.ptable_format != 'gpt-hybrid':
raise WicError("Partition may only be included in MBR with " \
"a gpt-hybrid partition table")
# Get the disk where the partition is located
self.numpart += 1
if not part.no_table:
self.realpart += 1
if self.numpart == 1:
if self.ptable_format == "msdos":
overhead = MBR_OVERHEAD
elif self.ptable_format in ("gpt", "gpt-hybrid"):
overhead = GPT_OVERHEAD
# Skip one sector required for the partitioning scheme overhead
self.offset += overhead
if self.ptable_format == "msdos":
if self.primary_part_num > 3 or \
(self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
part.type = 'logical'
# Reserve a sector for EBR for every logical partition
# before alignment is performed.
if part.type == 'logical':
self.offset += 2
align_sectors = 0
if part.align:
# If not first partition and we do have alignment set we need
# to align the partition.
# FIXME: This leaves a empty spaces to the disk. To fill the
# gaps we could enlargea the previous partition?
# Calc how much the alignment is off.
align_sectors = self.offset % (part.align * 1024 // self.sector_size)
if align_sectors:
# If partition is not aligned as required, we need
# to move forward to the next alignment point
align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
logger.debug("Realignment for %s%s with %s sectors, original"
" offset %s, target alignment is %sK.",
part.disk, self.numpart, align_sectors,
self.offset, part.align)
# increase the offset so we actually start the partition on right alignment
self.offset += align_sectors
if part.offset is not None:
offset = part.offset // self.sector_size
if offset * self.sector_size != part.offset:
raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
delta = offset - self.offset
if delta < 0:
raise WicError("Could not place %s%s at offset %d: next free sector is %d (delta: %d)" % (part.disk, self.numpart, part.offset, self.offset, delta))
logger.debug("Skipping %d sectors to place %s%s at offset %dK",
delta, part.disk, self.numpart, part.offset)
self.offset = offset
part.start = self.offset
self.offset += part.size_sec
if not part.no_table:
part.num = self.realpart
else:
part.num = 0
if self.ptable_format == "msdos" and not part.no_table:
if part.type == 'logical':
self.logical_part_cnt += 1
part.num = self.logical_part_cnt + 4
if self.extendedpart == 0:
# Create extended partition as a primary partition
self.primary_part_num += 1
self.extendedpart = part.num
else:
self.extended_size_sec += align_sectors
self.extended_size_sec += part.size_sec + 2
else:
self.primary_part_num += 1
part.num = self.primary_part_num
logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
"sectors (%d bytes).", part.mountpoint, part.disk,
part.num, part.start, self.offset - 1, part.size_sec,
part.size_sec * self.sector_size)
# Once all the partitions have been layed out, we can calculate the
# minumim disk size
self.min_size = self.offset
if self.ptable_format in ("gpt", "gpt-hybrid"):
self.min_size += GPT_OVERHEAD
self.min_size *= self.sector_size
self.min_size += self.extra_space
def _create_partition(self, device, parttype, fstype, start, size):
""" Create a partition on an image described by the 'device' object. """
# Start is included to the size so we need to substract one from the end.
end = start + size - 1
logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
parttype, start, end, size)
cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
if fstype:
cmd += " %s" % fstype
cmd += " %d %d" % (start, end)
return exec_native_cmd(cmd, self.native_sysroot)
def _write_identifier(self, device, identifier):
logger.debug("Set disk identifier %x", identifier)
with open(device, 'r+b') as img:
img.seek(0x1B8)
img.write(identifier.to_bytes(4, 'little'))
def _make_disk(self, device, ptable_format, min_size):
logger.debug("Creating sparse file %s", device)
with open(device, 'w') as sparse:
os.ftruncate(sparse.fileno(), min_size)
logger.debug("Initializing partition table for %s", device)
exec_native_cmd("parted -s %s mklabel %s" % (device, ptable_format),
self.native_sysroot)
def _write_disk_guid(self):
if self.ptable_format in ('gpt', 'gpt-hybrid'):
if os.getenv('SOURCE_DATE_EPOCH'):
self.disk_guid = uuid.UUID(int=int(os.getenv('SOURCE_DATE_EPOCH')))
else:
self.disk_guid = uuid.uuid4()
logger.debug("Set disk guid %s", self.disk_guid)
sfdisk_cmd = "sfdisk --disk-id %s %s" % (self.path, self.disk_guid)
exec_native_cmd(sfdisk_cmd, self.native_sysroot)
def create(self):
self._make_disk(self.path,
"gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
self.min_size)
self._write_identifier(self.path, self.identifier)
self._write_disk_guid()
if self.ptable_format == "gpt-hybrid":
mbr_path = self.path + ".mbr"
self._make_disk(mbr_path, "msdos", self.min_size)
self._write_identifier(mbr_path, self.identifier)
logger.debug("Creating partitions")
hybrid_mbr_part_num = 0
for part in self.partitions:
if part.num == 0:
continue
if self.ptable_format == "msdos" and part.num == self.extendedpart:
# Create an extended partition (note: extended
# partition is described in MBR and contains all
# logical partitions). The logical partitions save a
# sector for an EBR just before the start of a
# partition. The extended partition must start one
# sector before the start of the first logical
# partition. This way the first EBR is inside of the
# extended partition. Since the extended partitions
# starts a sector before the first logical partition,
# add a sector at the back, so that there is enough
# room for all logical partitions.
self._create_partition(self.path, "extended",
None, part.start - 2,
self.extended_size_sec)
if part.fstype == "swap":
parted_fs_type = "linux-swap"
elif part.fstype == "vfat":
parted_fs_type = "fat32"
elif part.fstype == "msdos":
parted_fs_type = "fat16"
if not part.system_id:
part.system_id = '0x6' # FAT16
else:
# Type for ext2/ext3/ext4/btrfs
parted_fs_type = "ext2"
# Boot ROM of OMAP boards require vfat boot partition to have an
# even number of sectors.
if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
and part.size_sec % 2:
logger.debug("Subtracting one sector from '%s' partition to "
"get even number of sectors for the partition",
part.mountpoint)
part.size_sec -= 1
self._create_partition(self.path, part.type,
parted_fs_type, part.start, part.size_sec)
if self.ptable_format == "gpt-hybrid" and part.mbr:
hybrid_mbr_part_num += 1
if hybrid_mbr_part_num > 4:
raise WicError("Extended MBR partitions are not supported in hybrid MBR")
self._create_partition(mbr_path, "primary",
parted_fs_type, part.start, part.size_sec)
if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
partition_label = part.part_name if part.part_name else part.label
logger.debug("partition %d: set name to %s",
part.num, partition_label)
exec_native_cmd("sgdisk --change-name=%d:%s %s" % \
(part.num, partition_label,
self.path), self.native_sysroot)
if part.part_type:
logger.debug("partition %d: set type UID to %s",
part.num, part.part_type)
exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
(part.num, part.part_type,
self.path), self.native_sysroot)
if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
logger.debug("partition %d: set UUID to %s",
part.num, part.uuid)
exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
(part.num, part.uuid, self.path),
self.native_sysroot)
if part.active:
flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
flag_name, part.num, self.path)
exec_native_cmd("parted -s %s set %d %s on" % \
(self.path, part.num, flag_name),
self.native_sysroot)
if self.ptable_format == 'gpt-hybrid' and part.mbr:
exec_native_cmd("parted -s %s set %d %s on" % \
(mbr_path, hybrid_mbr_part_num, "boot"),
self.native_sysroot)
if part.system_id:
exec_native_cmd("sfdisk --part-type %s %s %s" % \
(self.path, part.num, part.system_id),
self.native_sysroot)
if part.hidden and self.ptable_format == "gpt":
logger.debug("Set hidden attribute for partition '%s' on disk '%s'",
part.num, self.path)
exec_native_cmd("sfdisk --part-attrs %s %s RequiredPartition" % \
(self.path, part.num),
self.native_sysroot)
if self.ptable_format == "gpt-hybrid":
# Write a protective GPT partition
hybrid_mbr_part_num += 1
if hybrid_mbr_part_num > 4:
raise WicError("Extended MBR partitions are not supported in hybrid MBR")
# parted cannot directly create a protective GPT partition, so
# create with an arbitrary type, then change it to the correct type
# with sfdisk
self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
exec_native_cmd("sfdisk --part-type %s %d 0xee" % (mbr_path, hybrid_mbr_part_num),
self.native_sysroot)
# Copy hybrid MBR
with open(mbr_path, "rb") as mbr_file:
with open(self.path, "r+b") as image_file:
mbr = mbr_file.read(512)
image_file.write(mbr)
def cleanup(self):
pass
def assemble(self):
logger.debug("Installing partitions")
for part in self.partitions:
source = part.source_file
if source:
# install source_file contents into a partition
sparse_copy(source, self.path, seek=part.start * self.sector_size)
logger.debug("Installed %s in partition %d, sectors %d-%d, "
"size %d sectors", source, part.num, part.start,
part.start + part.size_sec - 1, part.size_sec)
partimage = self.path + '.p%d' % part.num
os.rename(source, partimage)
self.partimages.append(partimage)

View File

@@ -0,0 +1,213 @@
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# DESCRIPTION
# This implements the 'bootimg-biosplusefi' source plugin class for 'wic'
#
# AUTHORS
# William Bourque <wbourque [at) gmail.com>
import types
from wic.pluginbase import SourcePlugin
from importlib.machinery import SourceFileLoader
class BootimgBiosPlusEFIPlugin(SourcePlugin):
"""
Create MBR + EFI boot partition
This plugin creates a boot partition that contains both
legacy BIOS and EFI content. It will be able to boot from both.
This is useful when managing PC fleet with some older machines
without EFI support.
Note it is possible to create an image that can boot from both
legacy BIOS and EFI by defining two partitions : one with arg
--source bootimg-efi and another one with --source bootimg-pcbios.
However, this method has the obvious downside that it requires TWO
partitions to be created on the storage device.
Both partitions will also be marked as "bootable" which does not work on
most BIOS, has BIOS often uses the "bootable" flag to determine
what to boot. If you have such a BIOS, you need to manually remove the
"bootable" flag from the EFI partition for the drive to be bootable.
Having two partitions also seems to confuse wic : the content of
the first partition will be duplicated into the second, even though it
will not be used at all.
Also, unlike "isoimage-isohybrid" that also does BIOS and EFI, this plugin
allows you to have more than only a single rootfs partitions and does
not turn the rootfs into an initramfs RAM image.
This plugin is made to put everything into a single /boot partition so it
does not have the limitations listed above.
The plugin is made so it does tries not to reimplement what's already
been done in other plugins; as such it imports "bootimg-pcbios"
and "bootimg-efi".
Plugin "bootimg-pcbios" is used to generate legacy BIOS boot.
Plugin "bootimg-efi" is used to generate the UEFI boot. Note that it
requires a --sourceparams argument to know which loader to use; refer
to "bootimg-efi" code/documentation for the list of loader.
Imports are handled with "SourceFileLoader" from importlib as it is
otherwise very difficult to import module that has hyphen "-" in their
filename.
The SourcePlugin() methods used in the plugins (do_install_disk,
do_configure_partition, do_prepare_partition) are then called on both,
beginning by "bootimg-efi".
Plugin options, such as "--sourceparams" can still be passed to a
plugin, as long they does not cause issue in the other plugin.
Example wic configuration:
part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\\
--ondisk sda --label os_boot --active --align 1024 --use-uuid
"""
name = 'bootimg-biosplusefi'
__PCBIOS_MODULE_NAME = "bootimg-pcbios"
__EFI_MODULE_NAME = "bootimg-efi"
__imgEFIObj = None
__imgBiosObj = None
@classmethod
def __init__(cls):
"""
Constructor (init)
"""
# XXX
# For some reasons, __init__ constructor is never called.
# Something to do with how pluginbase works?
cls.__instanciateSubClasses()
@classmethod
def __instanciateSubClasses(cls):
"""
"""
# Import bootimg-pcbios (class name "BootimgPcbiosPlugin")
modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
cls.__PCBIOS_MODULE_NAME + ".py")
loader = SourceFileLoader(cls.__PCBIOS_MODULE_NAME, modulePath)
mod = types.ModuleType(loader.name)
loader.exec_module(mod)
cls.__imgBiosObj = mod.BootimgPcbiosPlugin()
# Import bootimg-efi (class name "BootimgEFIPlugin")
modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
cls.__EFI_MODULE_NAME + ".py")
loader = SourceFileLoader(cls.__EFI_MODULE_NAME, modulePath)
mod = types.ModuleType(loader.name)
loader.exec_module(mod)
cls.__imgEFIObj = mod.BootimgEFIPlugin()
@classmethod
def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
bootimg_dir, kernel_dir, native_sysroot):
"""
Called after all partitions have been prepared and assembled into a
disk image.
"""
if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
cls.__instanciateSubClasses()
cls.__imgEFIObj.do_install_disk(
disk,
disk_name,
creator,
workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
native_sysroot)
cls.__imgBiosObj.do_install_disk(
disk,
disk_name,
creator,
workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
native_sysroot)
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
"""
Called before do_prepare_partition()
"""
if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
cls.__instanciateSubClasses()
cls.__imgEFIObj.do_configure_partition(
part,
source_params,
creator,
cr_workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
native_sysroot)
cls.__imgBiosObj.do_configure_partition(
part,
source_params,
creator,
cr_workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
native_sysroot)
@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.
"""
if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
cls.__instanciateSubClasses()
cls.__imgEFIObj.do_prepare_partition(
part,
source_params,
creator,
cr_workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
rootfs_dir,
native_sysroot)
cls.__imgBiosObj.do_prepare_partition(
part,
source_params,
creator,
cr_workdir,
oe_builddir,
bootimg_dir,
kernel_dir,
rootfs_dir,
native_sysroot)

View File

@@ -0,0 +1,507 @@
#
# Copyright (c) 2014, Intel Corporation.
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-efi' source plugin class for 'wic'
#
# AUTHORS
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
import logging
import os
import tempfile
import shutil
import re
from glob import glob
from wic import WicError
from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
from wic.misc import (exec_cmd, exec_native_cmd,
get_bitbake_var, BOOTDD_EXTRA_SPACE)
logger = logging.getLogger('wic')
class BootimgEFIPlugin(SourcePlugin):
"""
Create EFI boot partition.
This plugin supports GRUB 2 and systemd-boot bootloaders.
"""
name = 'bootimg-efi'
@classmethod
def _copy_additional_files(cls, hdddir, initrd, dtb):
bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not bootimg_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
if initrd:
initrds = initrd.split(';')
for rd in initrds:
cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
exec_cmd(cp_cmd, True)
else:
logger.debug("Ignoring missing initrd")
if dtb:
if ';' in dtb:
raise WicError("Only one DTB supported, exiting")
cp_cmd = "cp %s/%s %s" % (bootimg_dir, dtb, hdddir)
exec_cmd(cp_cmd, True)
@classmethod
def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
"""
Create loader-specific (grub-efi) config
"""
configfile = creator.ks.bootloader.configfile
custom_cfg = None
if configfile:
custom_cfg = get_custom_config(configfile)
if custom_cfg:
# Use a custom configuration for grub
grubefi_conf = custom_cfg
logger.debug("Using custom configuration file "
"%s for grub.cfg", configfile)
else:
raise WicError("configfile is specified but failed to "
"get it from %s." % configfile)
initrd = source_params.get('initrd')
dtb = source_params.get('dtb')
cls._copy_additional_files(hdddir, initrd, dtb)
if not custom_cfg:
# Create grub configuration using parameters from wks file
bootloader = creator.ks.bootloader
title = source_params.get('title')
grubefi_conf = ""
grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
grubefi_conf += "default=boot\n"
grubefi_conf += "timeout=%s\n" % bootloader.timeout
grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot")
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
label = source_params.get('label')
label_conf = "root=%s" % creator.rootdev
if label:
label_conf = "LABEL=%s" % label
grubefi_conf += "linux /%s %s rootwait %s\n" \
% (kernel, label_conf, bootloader.append)
if initrd:
initrds = initrd.split(';')
grubefi_conf += "initrd"
for rd in initrds:
grubefi_conf += " /%s" % rd
grubefi_conf += "\n"
if dtb:
grubefi_conf += "devicetree /%s\n" % dtb
grubefi_conf += "}\n"
logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
cr_workdir)
cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w")
cfg.write(grubefi_conf)
cfg.close()
@classmethod
def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params):
"""
Create loader-specific systemd-boot/gummiboot config
"""
install_cmd = "install -d %s/loader" % hdddir
exec_cmd(install_cmd)
install_cmd = "install -d %s/loader/entries" % hdddir
exec_cmd(install_cmd)
bootloader = creator.ks.bootloader
unified_image = source_params.get('create-unified-kernel-image') == "true"
loader_conf = ""
if not unified_image:
loader_conf += "default boot\n"
loader_conf += "timeout %d\n" % bootloader.timeout
initrd = source_params.get('initrd')
dtb = source_params.get('dtb')
if not unified_image:
cls._copy_additional_files(hdddir, initrd, dtb)
logger.debug("Writing systemd-boot config "
"%s/hdd/boot/loader/loader.conf", cr_workdir)
cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w")
cfg.write(loader_conf)
cfg.close()
configfile = creator.ks.bootloader.configfile
custom_cfg = None
if configfile:
custom_cfg = get_custom_config(configfile)
if custom_cfg:
# Use a custom configuration for systemd-boot
boot_conf = custom_cfg
logger.debug("Using custom configuration file "
"%s for systemd-boots's boot.conf", configfile)
else:
raise WicError("configfile is specified but failed to "
"get it from %s.", configfile)
if not custom_cfg:
# Create systemd-boot configuration using parameters from wks file
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
title = source_params.get('title')
boot_conf = ""
boot_conf += "title %s\n" % (title if title else "boot")
boot_conf += "linux /%s\n" % kernel
label = source_params.get('label')
label_conf = "LABEL=Boot root=%s" % creator.rootdev
if label:
label_conf = "LABEL=%s" % label
boot_conf += "options %s %s\n" % \
(label_conf, bootloader.append)
if initrd:
initrds = initrd.split(';')
for rd in initrds:
boot_conf += "initrd /%s\n" % rd
if dtb:
boot_conf += "devicetree /%s\n" % dtb
if not unified_image:
logger.debug("Writing systemd-boot config "
"%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
cfg.write(boot_conf)
cfg.close()
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
"""
Called before do_prepare_partition(), creates loader-specific config
"""
hdddir = "%s/hdd/boot" % cr_workdir
install_cmd = "install -d %s/EFI/BOOT" % hdddir
exec_cmd(install_cmd)
try:
if source_params['loader'] == 'grub-efi':
cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
elif source_params['loader'] == 'systemd-boot':
cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
elif source_params['loader'] == 'uefi-kernel':
pass
else:
raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader'])
except KeyError:
raise WicError("bootimg-efi requires a loader, none specified")
if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None:
logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES')
else:
boot_files = None
for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)):
if fmt:
var = fmt % id
else:
var = ""
boot_files = get_bitbake_var("IMAGE_EFI_BOOT_FILES" + var)
if boot_files:
break
logger.debug('Boot files: %s', boot_files)
# list of tuples (src_name, dst_name)
deploy_files = []
for src_entry in re.findall(r'[\w;\-\.\+/\*]+', boot_files):
if ';' in src_entry:
dst_entry = tuple(src_entry.split(';'))
if not dst_entry[0] or not dst_entry[1]:
raise WicError('Malformed boot file entry: %s' % src_entry)
else:
dst_entry = (src_entry, src_entry)
logger.debug('Destination entry: %r', dst_entry)
deploy_files.append(dst_entry)
cls.install_task = [];
for deploy_entry in deploy_files:
src, dst = deploy_entry
if '*' in src:
# by default install files under their basename
entry_name_fn = os.path.basename
if dst != src:
# unless a target name was given, then treat name
# as a directory and append a basename
entry_name_fn = lambda name: \
os.path.join(dst,
os.path.basename(name))
srcs = glob(os.path.join(kernel_dir, src))
logger.debug('Globbed sources: %s', ', '.join(srcs))
for entry in srcs:
src = os.path.relpath(entry, kernel_dir)
entry_dst_name = entry_name_fn(entry)
cls.install_task.append((src, entry_dst_name))
else:
cls.install_task.append((src, dst))
@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 an EFI (grub) boot partition.
"""
if not kernel_dir:
kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not kernel_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
staging_kernel_dir = kernel_dir
hdddir = "%s/hdd/boot" % cr_workdir
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
if source_params.get('create-unified-kernel-image') == "true":
initrd = source_params.get('initrd')
if not initrd:
raise WicError("initrd= must be specified when create-unified-kernel-image=true, exiting")
deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
efi_stub = glob("%s/%s" % (deploy_dir, "linux*.efi.stub"))
if len(efi_stub) == 0:
raise WicError("Unified Kernel Image EFI stub not found, exiting")
efi_stub = efi_stub[0]
with tempfile.TemporaryDirectory() as tmp_dir:
label = source_params.get('label')
label_conf = "root=%s" % creator.rootdev
if label:
label_conf = "LABEL=%s" % label
bootloader = creator.ks.bootloader
cmdline = open("%s/cmdline" % tmp_dir, "w")
cmdline.write("%s %s" % (label_conf, bootloader.append))
cmdline.close()
initrds = initrd.split(';')
initrd = open("%s/initrd" % tmp_dir, "wb")
for f in initrds:
with open("%s/%s" % (deploy_dir, f), 'rb') as in_file:
shutil.copyfileobj(in_file, initrd)
initrd.close()
# Searched by systemd-boot:
# https://systemd.io/BOOT_LOADER_SPECIFICATION/#type-2-efi-unified-kernel-images
install_cmd = "install -d %s/EFI/Linux" % hdddir
exec_cmd(install_cmd)
staging_dir_host = get_bitbake_var("STAGING_DIR_HOST")
target_sys = get_bitbake_var("TARGET_SYS")
objdump_cmd = "%s-objdump" % target_sys
objdump_cmd += " -p %s" % efi_stub
objdump_cmd += " | awk '{ if ($1 == \"SectionAlignment\"){print $2} }'"
ret, align_str = exec_native_cmd(objdump_cmd, native_sysroot)
align = int(align_str, 16)
objdump_cmd = "%s-objdump" % target_sys
objdump_cmd += " -h %s | tail -2" % efi_stub
ret, output = exec_native_cmd(objdump_cmd, native_sysroot)
offset = int(output.split()[2], 16) + int(output.split()[3], 16)
osrel_off = offset + align - offset % align
osrel_path = "%s/usr/lib/os-release" % staging_dir_host
osrel_sz = os.stat(osrel_path).st_size
cmdline_off = osrel_off + osrel_sz
cmdline_off = cmdline_off + align - cmdline_off % align
cmdline_sz = os.stat(cmdline.name).st_size
dtb_off = cmdline_off + cmdline_sz
dtb_off = dtb_off + align - dtb_off % align
dtb = source_params.get('dtb')
if dtb:
if ';' in dtb:
raise WicError("Only one DTB supported, exiting")
dtb_path = "%s/%s" % (deploy_dir, dtb)
dtb_params = '--add-section .dtb=%s --change-section-vma .dtb=0x%x' % \
(dtb_path, dtb_off)
linux_off = dtb_off + os.stat(dtb_path).st_size
linux_off = linux_off + align - linux_off % align
else:
dtb_params = ''
linux_off = dtb_off
linux_path = "%s/%s" % (staging_kernel_dir, kernel)
linux_sz = os.stat(linux_path).st_size
initrd_off = linux_off + linux_sz
initrd_off = initrd_off + align - initrd_off % align
# https://www.freedesktop.org/software/systemd/man/systemd-stub.html
objcopy_cmd = "%s-objcopy" % target_sys
objcopy_cmd += " --enable-deterministic-archives"
objcopy_cmd += " --preserve-dates"
objcopy_cmd += " --add-section .osrel=%s" % osrel_path
objcopy_cmd += " --change-section-vma .osrel=0x%x" % osrel_off
objcopy_cmd += " --add-section .cmdline=%s" % cmdline.name
objcopy_cmd += " --change-section-vma .cmdline=0x%x" % cmdline_off
objcopy_cmd += dtb_params
objcopy_cmd += " --add-section .linux=%s" % linux_path
objcopy_cmd += " --change-section-vma .linux=0x%x" % linux_off
objcopy_cmd += " --add-section .initrd=%s" % initrd.name
objcopy_cmd += " --change-section-vma .initrd=0x%x" % initrd_off
objcopy_cmd += " %s %s/EFI/Linux/linux.efi" % (efi_stub, hdddir)
exec_native_cmd(objcopy_cmd, native_sysroot)
else:
if source_params.get('install-kernel-into-boot-dir') != 'false':
install_cmd = "install -m 0644 %s/%s %s/%s" % \
(staging_kernel_dir, kernel, hdddir, kernel)
exec_cmd(install_cmd)
if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
for src_path, dst_path in cls.install_task:
install_cmd = "install -m 0644 -D %s %s" \
% (os.path.join(kernel_dir, src_path),
os.path.join(hdddir, dst_path))
exec_cmd(install_cmd)
try:
if source_params['loader'] == 'grub-efi':
shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
"%s/grub.cfg" % cr_workdir)
for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]:
cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:])
exec_cmd(cp_cmd, True)
shutil.move("%s/grub.cfg" % cr_workdir,
"%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
elif source_params['loader'] == 'systemd-boot':
for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
exec_cmd(cp_cmd, True)
elif source_params['loader'] == 'uefi-kernel':
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if not kernel:
raise WicError("Empty KERNEL_IMAGETYPE")
target = get_bitbake_var("TARGET_SYS")
if not target:
raise WicError("Empty TARGET_SYS")
if re.match("x86_64", target):
kernel_efi_image = "bootx64.efi"
elif re.match('i.86', target):
kernel_efi_image = "bootia32.efi"
elif re.match('aarch64', target):
kernel_efi_image = "bootaa64.efi"
elif re.match('arm', target):
kernel_efi_image = "bootarm.efi"
else:
raise WicError("UEFI stub kernel is incompatible with target %s" % target)
for mod in [x for x in os.listdir(kernel_dir) if x.startswith(kernel)]:
cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, kernel_efi_image)
exec_cmd(cp_cmd, True)
else:
raise WicError("unrecognized bootimg-efi loader: %s" %
source_params['loader'])
except KeyError:
raise WicError("bootimg-efi requires a loader, none specified")
startup = os.path.join(kernel_dir, "startup.nsh")
if os.path.exists(startup):
cp_cmd = "cp %s %s/" % (startup, hdddir)
exec_cmd(cp_cmd, True)
for paths in part.include_path or []:
for path in paths:
cp_cmd = "cp -r %s %s/" % (path, hdddir)
exec_cmd(cp_cmd, True)
du_cmd = "du -bks %s" % hdddir
out = exec_cmd(du_cmd)
blocks = int(out.split()[0])
extra_blocks = part.get_extra_block_count(blocks)
if extra_blocks < BOOTDD_EXTRA_SPACE:
extra_blocks = BOOTDD_EXTRA_SPACE
blocks += extra_blocks
logger.debug("Added %d extra blocks to %s to get to %d total blocks",
extra_blocks, part.mountpoint, blocks)
# required for compatibility with certain devices expecting file system
# block count to be equal to partition block count
if blocks < part.fixed_size:
blocks = part.fixed_size
logger.debug("Overriding %s to %d total blocks for compatibility",
part.mountpoint, blocks)
# dosfs image, created by mkdosfs
bootimg = "%s/boot.img" % cr_workdir
label = part.label if part.label else "ESP"
dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \
(label, part.fsuuid, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
exec_native_cmd(mcopy_cmd, native_sysroot)
chmod_cmd = "chmod 644 %s" % bootimg
exec_cmd(chmod_cmd)
du_cmd = "du -Lbks %s" % bootimg
out = exec_cmd(du_cmd)
bootimg_size = out.split()[0]
part.size = int(bootimg_size)
part.source_file = bootimg

View File

@@ -0,0 +1,197 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-partition' source plugin class for
# 'wic'. The plugin creates an image of boot partition, copying over
# files listed in IMAGE_BOOT_FILES bitbake variable.
#
# AUTHORS
# Maciej Borzecki <maciej.borzecki (at] open-rnd.pl>
#
import logging
import os
import re
from glob import glob
from wic import WicError
from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
from wic.misc import exec_cmd, get_bitbake_var
logger = logging.getLogger('wic')
class BootimgPartitionPlugin(SourcePlugin):
"""
Create an image of boot partition, copying over files
listed in IMAGE_BOOT_FILES bitbake variable.
"""
name = 'bootimg-partition'
image_boot_files_var_name = 'IMAGE_BOOT_FILES'
@classmethod
def do_configure_partition(cls, part, source_params, cr, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
"""
Called before do_prepare_partition(), create u-boot specific boot config
"""
hdddir = "%s/boot.%d" % (cr_workdir, part.lineno)
install_cmd = "install -d %s" % hdddir
exec_cmd(install_cmd)
if not kernel_dir:
kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not kernel_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
boot_files = None
for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)):
if fmt:
var = fmt % id
else:
var = ""
boot_files = get_bitbake_var(cls.image_boot_files_var_name + var)
if boot_files is not None:
break
if boot_files is None:
raise WicError('No boot files defined, %s unset for entry #%d' % (cls.image_boot_files_var_name, part.lineno))
logger.debug('Boot files: %s', boot_files)
# list of tuples (src_name, dst_name)
deploy_files = []
for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files):
if ';' in src_entry:
dst_entry = tuple(src_entry.split(';'))
if not dst_entry[0] or not dst_entry[1]:
raise WicError('Malformed boot file entry: %s' % src_entry)
else:
dst_entry = (src_entry, src_entry)
logger.debug('Destination entry: %r', dst_entry)
deploy_files.append(dst_entry)
cls.install_task = [];
for deploy_entry in deploy_files:
src, dst = deploy_entry
if '*' in src:
# by default install files under their basename
entry_name_fn = os.path.basename
if dst != src:
# unless a target name was given, then treat name
# as a directory and append a basename
entry_name_fn = lambda name: \
os.path.join(dst,
os.path.basename(name))
srcs = glob(os.path.join(kernel_dir, src))
logger.debug('Globbed sources: %s', ', '.join(srcs))
for entry in srcs:
src = os.path.relpath(entry, kernel_dir)
entry_dst_name = entry_name_fn(entry)
cls.install_task.append((src, entry_dst_name))
else:
cls.install_task.append((src, dst))
if source_params.get('loader') != "u-boot":
return
configfile = cr.ks.bootloader.configfile
custom_cfg = None
if configfile:
custom_cfg = get_custom_config(configfile)
if custom_cfg:
# Use a custom configuration for extlinux.conf
extlinux_conf = custom_cfg
logger.debug("Using custom configuration file "
"%s for extlinux.conf", configfile)
else:
raise WicError("configfile is specified but failed to "
"get it from %s." % configfile)
if not custom_cfg:
# The kernel types supported by the sysboot of u-boot
kernel_types = ["zImage", "Image", "fitImage", "uImage", "vmlinux"]
has_dtb = False
fdt_dir = '/'
kernel_name = None
# Find the kernel image name, from the highest precedence to lowest
for image in kernel_types:
for task in cls.install_task:
src, dst = task
if re.match(image, src):
kernel_name = os.path.join('/', dst)
break
if kernel_name:
break
for task in cls.install_task:
src, dst = task
# We suppose that all the dtb are in the same directory
if re.search(r'\.dtb', src) and fdt_dir == '/':
has_dtb = True
fdt_dir = os.path.join(fdt_dir, os.path.dirname(dst))
break
if not kernel_name:
raise WicError('No kernel file found')
# Compose the extlinux.conf
extlinux_conf = "default Yocto\n"
extlinux_conf += "label Yocto\n"
extlinux_conf += " kernel %s\n" % kernel_name
if has_dtb:
extlinux_conf += " fdtdir %s\n" % fdt_dir
bootloader = cr.ks.bootloader
extlinux_conf += "append root=%s rootwait %s\n" \
% (cr.rootdev, bootloader.append if bootloader.append else '')
install_cmd = "install -d %s/extlinux/" % hdddir
exec_cmd(install_cmd)
cfg = open("%s/extlinux/extlinux.conf" % hdddir, "w")
cfg.write(extlinux_conf)
cfg.close()
@classmethod
def do_prepare_partition(cls, part, source_params, cr, 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, does the following:
- sets up a vfat partition
- copies all files listed in IMAGE_BOOT_FILES variable
"""
hdddir = "%s/boot.%d" % (cr_workdir, part.lineno)
if not kernel_dir:
kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not kernel_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
logger.debug('Kernel dir: %s', bootimg_dir)
for task in cls.install_task:
src_path, dst_path = task
logger.debug('Install %s as %s', src_path, dst_path)
install_cmd = "install -m 0644 -D %s %s" \
% (os.path.join(kernel_dir, src_path),
os.path.join(hdddir, dst_path))
exec_cmd(install_cmd)
logger.debug('Prepare boot partition using rootfs in %s', hdddir)
part.prepare_rootfs(cr_workdir, oe_builddir, hdddir,
native_sysroot, False)

View File

@@ -0,0 +1,209 @@
#
# Copyright (c) 2014, Intel Corporation.
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-pcbios' source plugin class for 'wic'
#
# AUTHORS
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
import logging
import os
import re
from wic import WicError
from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
from wic.misc import (exec_cmd, exec_native_cmd,
get_bitbake_var, BOOTDD_EXTRA_SPACE)
logger = logging.getLogger('wic')
class BootimgPcbiosPlugin(SourcePlugin):
"""
Create MBR boot partition and install syslinux on it.
"""
name = 'bootimg-pcbios'
@classmethod
def _get_bootimg_dir(cls, bootimg_dir, dirname):
"""
Check if dirname exists in default bootimg_dir or in STAGING_DIR.
"""
staging_datadir = get_bitbake_var("STAGING_DATADIR")
for result in (bootimg_dir, staging_datadir):
if os.path.exists("%s/%s" % (result, dirname)):
return result
# STAGING_DATADIR is expanded with MLPREFIX if multilib is enabled
# but dependency syslinux is still populated to original STAGING_DATADIR
nonarch_datadir = re.sub('/[^/]*recipe-sysroot', '/recipe-sysroot', staging_datadir)
if os.path.exists(os.path.join(nonarch_datadir, dirname)):
return nonarch_datadir
raise WicError("Couldn't find correct bootimg_dir, exiting")
@classmethod
def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
bootimg_dir, kernel_dir, native_sysroot):
"""
Called after all partitions have been prepared and assembled into a
disk image. In this case, we install the MBR.
"""
bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
mbrfile = "%s/syslinux/" % bootimg_dir
if creator.ptable_format == 'msdos':
mbrfile += "mbr.bin"
elif creator.ptable_format == 'gpt':
mbrfile += "gptmbr.bin"
else:
raise WicError("Unsupported partition table: %s" %
creator.ptable_format)
if not os.path.exists(mbrfile):
raise WicError("Couldn't find %s. If using the -e option, do you "
"have the right MACHINE set in local.conf? If not, "
"is the bootimg_dir path correct?" % mbrfile)
full_path = creator._full_path(workdir, disk_name, "direct")
logger.debug("Installing MBR on disk %s as %s with size %s bytes",
disk_name, full_path, disk.min_size)
dd_cmd = "dd if=%s of=%s conv=notrunc" % (mbrfile, full_path)
exec_cmd(dd_cmd, native_sysroot)
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
"""
Called before do_prepare_partition(), creates syslinux config
"""
hdddir = "%s/hdd/boot" % cr_workdir
install_cmd = "install -d %s" % hdddir
exec_cmd(install_cmd)
bootloader = creator.ks.bootloader
custom_cfg = None
if bootloader.configfile:
custom_cfg = get_custom_config(bootloader.configfile)
if custom_cfg:
# Use a custom configuration for grub
syslinux_conf = custom_cfg
logger.debug("Using custom configuration file %s "
"for syslinux.cfg", bootloader.configfile)
else:
raise WicError("configfile is specified but failed to "
"get it from %s." % bootloader.configfile)
if not custom_cfg:
# Create syslinux configuration using parameters from wks file
splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg")
if os.path.exists(splash):
splashline = "menu background splash.jpg"
else:
splashline = ""
syslinux_conf = ""
syslinux_conf += "PROMPT 0\n"
syslinux_conf += "TIMEOUT " + str(bootloader.timeout) + "\n"
syslinux_conf += "\n"
syslinux_conf += "ALLOWOPTIONS 1\n"
syslinux_conf += "SERIAL 0 115200\n"
syslinux_conf += "\n"
if splashline:
syslinux_conf += "%s\n" % splashline
syslinux_conf += "DEFAULT boot\n"
syslinux_conf += "LABEL boot\n"
kernel = "/" + get_bitbake_var("KERNEL_IMAGETYPE")
syslinux_conf += "KERNEL " + kernel + "\n"
syslinux_conf += "APPEND label=boot root=%s %s\n" % \
(creator.rootdev, bootloader.append)
logger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg",
cr_workdir)
cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w")
cfg.write(syslinux_conf)
cfg.close()
@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.
"""
bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
staging_kernel_dir = kernel_dir
hdddir = "%s/hdd/boot" % cr_workdir
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
cmds = ("install -m 0644 %s/%s %s/%s" %
(staging_kernel_dir, kernel, hdddir, get_bitbake_var("KERNEL_IMAGETYPE")),
"install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" %
(bootimg_dir, hdddir),
"install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" %
(bootimg_dir, hdddir),
"install -m 444 %s/syslinux/libcom32.c32 %s/libcom32.c32" %
(bootimg_dir, hdddir),
"install -m 444 %s/syslinux/libutil.c32 %s/libutil.c32" %
(bootimg_dir, hdddir))
for install_cmd in cmds:
exec_cmd(install_cmd)
du_cmd = "du -bks %s" % hdddir
out = exec_cmd(du_cmd)
blocks = int(out.split()[0])
extra_blocks = part.get_extra_block_count(blocks)
if extra_blocks < BOOTDD_EXTRA_SPACE:
extra_blocks = BOOTDD_EXTRA_SPACE
blocks += extra_blocks
logger.debug("Added %d extra blocks to %s to get to %d total blocks",
extra_blocks, part.mountpoint, blocks)
# dosfs image, created by mkdosfs
bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno)
label = part.label if part.label else "boot"
dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
(label, part.fsuuid, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
exec_native_cmd(mcopy_cmd, native_sysroot)
syslinux_cmd = "syslinux %s" % bootimg
exec_native_cmd(syslinux_cmd, native_sysroot)
chmod_cmd = "chmod 644 %s" % bootimg
exec_cmd(chmod_cmd)
du_cmd = "du -Lbks %s" % bootimg
out = exec_cmd(du_cmd)
bootimg_size = out.split()[0]
part.size = int(bootimg_size)
part.source_file = bootimg

View File

@@ -0,0 +1,89 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: MIT
#
# The empty wic plugin is used to create unformatted empty partitions for wic
# images.
# To use it you must pass "empty" as argument for the "--source" parameter in
# the wks file. For example:
# part foo --source empty --ondisk sda --size="1024" --align 1024
#
# The plugin supports writing zeros to the start of the
# partition. This is useful to overwrite old content like
# filesystem signatures which may be re-recognized otherwise.
# This feature can be enabled with
# '--sourceparams="[fill|size=<N>[S|s|K|k|M|G]][,][bs=<N>[S|s|K|k|M|G]]"'
# Conflicting or missing options throw errors.
import logging
import os
from wic import WicError
from wic.ksparser import sizetype
from wic.pluginbase import SourcePlugin
logger = logging.getLogger('wic')
class EmptyPartitionPlugin(SourcePlugin):
"""
Populate unformatted empty partition.
The following sourceparams are supported:
- fill
Fill the entire partition with zeros. Requires '--fixed-size' option
to be set.
- size=<N>[S|s|K|k|M|G]
Set the first N bytes of the partition to zero. Default unit is 'K'.
- bs=<N>[S|s|K|k|M|G]
Write at most N bytes at a time during source file creation.
Defaults to '1M'. Default unit is 'K'.
"""
name = 'empty'
@classmethod
def do_prepare_partition(cls, part, source_params, cr, 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.
"""
get_byte_count = sizetype('K', True)
size = 0
if 'fill' in source_params and 'size' in source_params:
raise WicError("Conflicting source parameters 'fill' and 'size' specified, exiting.")
# Set the size of the zeros to be written to the partition
if 'fill' in source_params:
if part.fixed_size == 0:
raise WicError("Source parameter 'fill' only works with the '--fixed-size' option, exiting.")
size = get_byte_count(part.fixed_size)
elif 'size' in source_params:
size = get_byte_count(source_params['size'])
if size == 0:
# Nothing to do, create empty partition
return
if 'bs' in source_params:
bs = get_byte_count(source_params['bs'])
else:
bs = get_byte_count('1M')
# Create a binary file of the requested size filled with zeros
source_file = os.path.join(cr_workdir, 'empty-plugin-zeros%s.bin' % part.lineno)
if not os.path.exists(os.path.dirname(source_file)):
os.makedirs(os.path.dirname(source_file))
quotient, remainder = divmod(size, bs)
with open(source_file, 'wb') as file:
for _ in range(quotient):
file.write(bytearray(bs))
file.write(bytearray(remainder))
part.size = (size + 1024 - 1) // 1024 # size in KB rounded up
part.source_file = source_file

View File

@@ -0,0 +1,463 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'isoimage-isohybrid' source plugin class for 'wic'
#
# AUTHORS
# Mihaly Varga <mihaly.varga (at] ni.com>
import glob
import logging
import os
import re
import shutil
from wic import WicError
from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var
logger = logging.getLogger('wic')
class IsoImagePlugin(SourcePlugin):
"""
Create a bootable ISO image
This plugin creates a hybrid, legacy and EFI bootable ISO image. The
generated image can be used on optical media as well as USB media.
Legacy boot uses syslinux and EFI boot uses grub or gummiboot (not
implemented yet) as bootloader. The plugin creates the directories required
by bootloaders and populates them by creating and configuring the
bootloader files.
Example kickstart file:
part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi, \\
image_name= IsoImage" --ondisk cd --label LIVECD
bootloader --timeout=10 --append=" "
In --sourceparams "loader" specifies the bootloader used for booting in EFI
mode, while "image_name" specifies the name of the generated image. In the
example above, wic creates an ISO image named IsoImage-cd.direct (default
extension added by direct imeger plugin) and a file named IsoImage-cd.iso
"""
name = 'isoimage-isohybrid'
@classmethod
def do_configure_syslinux(cls, creator, cr_workdir):
"""
Create loader-specific (syslinux) config
"""
splash = os.path.join(cr_workdir, "ISO/boot/splash.jpg")
if os.path.exists(splash):
splashline = "menu background splash.jpg"
else:
splashline = ""
bootloader = creator.ks.bootloader
syslinux_conf = ""
syslinux_conf += "PROMPT 0\n"
syslinux_conf += "TIMEOUT %s \n" % (bootloader.timeout or 10)
syslinux_conf += "\n"
syslinux_conf += "ALLOWOPTIONS 1\n"
syslinux_conf += "SERIAL 0 115200\n"
syslinux_conf += "\n"
if splashline:
syslinux_conf += "%s\n" % splashline
syslinux_conf += "DEFAULT boot\n"
syslinux_conf += "LABEL boot\n"
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
syslinux_conf += "KERNEL /" + kernel + "\n"
syslinux_conf += "APPEND initrd=/initrd LABEL=boot %s\n" \
% bootloader.append
logger.debug("Writing syslinux config %s/ISO/isolinux/isolinux.cfg",
cr_workdir)
with open("%s/ISO/isolinux/isolinux.cfg" % cr_workdir, "w") as cfg:
cfg.write(syslinux_conf)
@classmethod
def do_configure_grubefi(cls, part, creator, target_dir):
"""
Create loader-specific (grub-efi) config
"""
configfile = creator.ks.bootloader.configfile
if configfile:
grubefi_conf = get_custom_config(configfile)
if grubefi_conf:
logger.debug("Using custom configuration file %s for grub.cfg",
configfile)
else:
raise WicError("configfile is specified "
"but failed to get it from %s", configfile)
else:
splash = os.path.join(target_dir, "splash.jpg")
if os.path.exists(splash):
splashline = "menu background splash.jpg"
else:
splashline = ""
bootloader = creator.ks.bootloader
grubefi_conf = ""
grubefi_conf += "serial --unit=0 --speed=115200 --word=8 "
grubefi_conf += "--parity=no --stop=1\n"
grubefi_conf += "default=boot\n"
grubefi_conf += "timeout=%s\n" % (bootloader.timeout or 10)
grubefi_conf += "\n"
grubefi_conf += "search --set=root --label %s " % part.label
grubefi_conf += "\n"
grubefi_conf += "menuentry 'boot'{\n"
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
grubefi_conf += "linux /%s rootwait %s\n" \
% (kernel, bootloader.append)
grubefi_conf += "initrd /initrd \n"
grubefi_conf += "}\n"
if splashline:
grubefi_conf += "%s\n" % splashline
cfg_path = os.path.join(target_dir, "grub.cfg")
logger.debug("Writing grubefi config %s", cfg_path)
with open(cfg_path, "w") as cfg:
cfg.write(grubefi_conf)
@staticmethod
def _build_initramfs_path(rootfs_dir, cr_workdir):
"""
Create path for initramfs image
"""
initrd = get_bitbake_var("INITRD_LIVE") or get_bitbake_var("INITRD")
if not initrd:
initrd_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not initrd_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting.")
image_name = get_bitbake_var("IMAGE_BASENAME")
if not image_name:
raise WicError("Couldn't find IMAGE_BASENAME, exiting.")
image_type = get_bitbake_var("INITRAMFS_FSTYPES")
if not image_type:
raise WicError("Couldn't find INITRAMFS_FSTYPES, exiting.")
machine = os.path.basename(initrd_dir)
pattern = '%s/%s*%s.%s' % (initrd_dir, image_name, machine, image_type)
files = glob.glob(pattern)
if files:
initrd = files[0]
if not initrd or not os.path.exists(initrd):
# Create initrd from rootfs directory
initrd = "%s/initrd.cpio.gz" % cr_workdir
initrd_dir = "%s/INITRD" % cr_workdir
shutil.copytree("%s" % rootfs_dir, \
"%s" % initrd_dir, symlinks=True)
if os.path.isfile("%s/init" % rootfs_dir):
shutil.copy2("%s/init" % rootfs_dir, "%s/init" % initrd_dir)
elif os.path.lexists("%s/init" % rootfs_dir):
os.symlink(os.readlink("%s/init" % rootfs_dir), \
"%s/init" % initrd_dir)
elif os.path.isfile("%s/sbin/init" % rootfs_dir):
shutil.copy2("%s/sbin/init" % rootfs_dir, \
"%s" % initrd_dir)
elif os.path.lexists("%s/sbin/init" % rootfs_dir):
os.symlink(os.readlink("%s/sbin/init" % rootfs_dir), \
"%s/init" % initrd_dir)
else:
raise WicError("Couldn't find or build initrd, exiting.")
exec_cmd("cd %s && find . | cpio -o -H newc -R root:root >%s/initrd.cpio " \
% (initrd_dir, cr_workdir), as_shell=True)
exec_cmd("gzip -f -9 %s/initrd.cpio" % cr_workdir, as_shell=True)
shutil.rmtree(initrd_dir)
return initrd
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
"""
Called before do_prepare_partition(), creates loader-specific config
"""
isodir = "%s/ISO/" % cr_workdir
if os.path.exists(isodir):
shutil.rmtree(isodir)
install_cmd = "install -d %s " % isodir
exec_cmd(install_cmd)
# Overwrite the name of the created image
logger.debug(source_params)
if 'image_name' in source_params and \
source_params['image_name'].strip():
creator.name = source_params['image_name'].strip()
logger.debug("The name of the image is: %s", creator.name)
@staticmethod
def _install_payload(source_params, iso_dir):
"""
Copies contents of payload directory (as specified in 'payload_dir' param) into iso_dir
"""
if source_params.get('payload_dir'):
payload_dir = source_params['payload_dir']
logger.debug("Payload directory: %s", payload_dir)
shutil.copytree(payload_dir, iso_dir, symlinks=True, dirs_exist_ok=True)
@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 a bootable ISO image.
"""
isodir = "%s/ISO" % cr_workdir
cls._install_payload(source_params, isodir)
if part.rootfs_dir is None:
if not 'ROOTFS_DIR' in rootfs_dir:
raise WicError("Couldn't find --rootfs-dir, exiting.")
rootfs_dir = rootfs_dir['ROOTFS_DIR']
else:
if part.rootfs_dir in rootfs_dir:
rootfs_dir = rootfs_dir[part.rootfs_dir]
elif part.rootfs_dir:
rootfs_dir = part.rootfs_dir
else:
raise WicError("Couldn't find --rootfs-dir=%s connection "
"or it is not a valid path, exiting." %
part.rootfs_dir)
if not os.path.isdir(rootfs_dir):
rootfs_dir = get_bitbake_var("IMAGE_ROOTFS")
if not os.path.isdir(rootfs_dir):
raise WicError("Couldn't find IMAGE_ROOTFS, exiting.")
part.rootfs_dir = rootfs_dir
deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
img_iso_dir = get_bitbake_var("ISODIR")
# Remove the temporary file created by part.prepare_rootfs()
if os.path.isfile(part.source_file):
os.remove(part.source_file)
# Support using a different initrd other than default
if source_params.get('initrd'):
initrd = source_params['initrd']
if not deploy_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
cp_cmd = "cp %s/%s %s" % (deploy_dir, initrd, cr_workdir)
exec_cmd(cp_cmd)
else:
# Prepare initial ramdisk
initrd = "%s/initrd" % deploy_dir
if not os.path.isfile(initrd):
initrd = "%s/initrd" % img_iso_dir
if not os.path.isfile(initrd):
initrd = cls._build_initramfs_path(rootfs_dir, cr_workdir)
install_cmd = "install -m 0644 %s %s/initrd" % (initrd, isodir)
exec_cmd(install_cmd)
# Remove the temporary file created by _build_initramfs_path function
if os.path.isfile("%s/initrd.cpio.gz" % cr_workdir):
os.remove("%s/initrd.cpio.gz" % cr_workdir)
kernel = get_bitbake_var("KERNEL_IMAGETYPE")
if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
if get_bitbake_var("INITRAMFS_IMAGE"):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
install_cmd = "install -m 0644 %s/%s %s/%s" % \
(kernel_dir, kernel, isodir, kernel)
exec_cmd(install_cmd)
#Create bootloader for efi boot
try:
target_dir = "%s/EFI/BOOT" % isodir
if os.path.exists(target_dir):
shutil.rmtree(target_dir)
os.makedirs(target_dir)
if source_params['loader'] == 'grub-efi':
# Builds bootx64.efi/bootia32.efi if ISODIR didn't exist or
# didn't contains it
target_arch = get_bitbake_var("TARGET_SYS")
if not target_arch:
raise WicError("Coludn't find target architecture")
if re.match("x86_64", target_arch):
grub_src_image = "grub-efi-bootx64.efi"
grub_dest_image = "bootx64.efi"
elif re.match('i.86', target_arch):
grub_src_image = "grub-efi-bootia32.efi"
grub_dest_image = "bootia32.efi"
else:
raise WicError("grub-efi is incompatible with target %s" %
target_arch)
grub_target = os.path.join(target_dir, grub_dest_image)
if not os.path.isfile(grub_target):
grub_src = os.path.join(deploy_dir, grub_src_image)
if not os.path.exists(grub_src):
raise WicError("Grub loader %s is not found in %s. "
"Please build grub-efi first" % (grub_src_image, deploy_dir))
shutil.copy(grub_src, grub_target)
if not os.path.isfile(os.path.join(target_dir, "boot.cfg")):
cls.do_configure_grubefi(part, creator, target_dir)
else:
raise WicError("unrecognized bootimg-efi loader: %s" %
source_params['loader'])
except KeyError:
raise WicError("bootimg-efi requires a loader, none specified")
# Create efi.img that contains bootloader files for EFI booting
# if ISODIR didn't exist or didn't contains it
if os.path.isfile("%s/efi.img" % img_iso_dir):
install_cmd = "install -m 0644 %s/efi.img %s/efi.img" % \
(img_iso_dir, isodir)
exec_cmd(install_cmd)
else:
# Default to 100 blocks of extra space for file system overhead
esp_extra_blocks = int(source_params.get('esp_extra_blocks', '100'))
du_cmd = "du -bks %s/EFI" % isodir
out = exec_cmd(du_cmd)
blocks = int(out.split()[0])
blocks += esp_extra_blocks
logger.debug("Added 100 extra blocks to %s to get to %d "
"total blocks", part.mountpoint, blocks)
# dosfs image for EFI boot
bootimg = "%s/efi.img" % isodir
esp_label = source_params.get('esp_label', 'EFIimg')
dosfs_cmd = 'mkfs.vfat -n \'%s\' -S 512 -C %s %d' \
% (esp_label, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mmd_cmd = "mmd -i %s ::/EFI" % bootimg
exec_native_cmd(mmd_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/EFI/* ::/EFI/" \
% (bootimg, isodir)
exec_native_cmd(mcopy_cmd, native_sysroot)
chmod_cmd = "chmod 644 %s" % bootimg
exec_cmd(chmod_cmd)
# Prepare files for legacy boot
syslinux_dir = get_bitbake_var("STAGING_DATADIR")
if not syslinux_dir:
raise WicError("Couldn't find STAGING_DATADIR, exiting.")
if os.path.exists("%s/isolinux" % isodir):
shutil.rmtree("%s/isolinux" % isodir)
install_cmd = "install -d %s/isolinux" % isodir
exec_cmd(install_cmd)
cls.do_configure_syslinux(creator, cr_workdir)
install_cmd = "install -m 444 %s/syslinux/ldlinux.sys " % syslinux_dir
install_cmd += "%s/isolinux/ldlinux.sys" % isodir
exec_cmd(install_cmd)
install_cmd = "install -m 444 %s/syslinux/isohdpfx.bin " % syslinux_dir
install_cmd += "%s/isolinux/isohdpfx.bin" % isodir
exec_cmd(install_cmd)
install_cmd = "install -m 644 %s/syslinux/isolinux.bin " % syslinux_dir
install_cmd += "%s/isolinux/isolinux.bin" % isodir
exec_cmd(install_cmd)
install_cmd = "install -m 644 %s/syslinux/ldlinux.c32 " % syslinux_dir
install_cmd += "%s/isolinux/ldlinux.c32" % isodir
exec_cmd(install_cmd)
#create ISO image
iso_img = "%s/tempiso_img.iso" % cr_workdir
iso_bootimg = "isolinux/isolinux.bin"
iso_bootcat = "isolinux/boot.cat"
efi_img = "efi.img"
mkisofs_cmd = "mkisofs -V %s " % part.label
mkisofs_cmd += "-o %s -U " % iso_img
mkisofs_cmd += "-J -joliet-long -r -iso-level 2 -b %s " % iso_bootimg
mkisofs_cmd += "-c %s -no-emul-boot -boot-load-size 4 " % iso_bootcat
mkisofs_cmd += "-boot-info-table -eltorito-alt-boot "
mkisofs_cmd += "-eltorito-platform 0xEF -eltorito-boot %s " % efi_img
mkisofs_cmd += "-no-emul-boot %s " % isodir
logger.debug("running command: %s", mkisofs_cmd)
exec_native_cmd(mkisofs_cmd, native_sysroot)
shutil.rmtree(isodir)
du_cmd = "du -Lbks %s" % iso_img
out = exec_cmd(du_cmd)
isoimg_size = int(out.split()[0])
part.size = isoimg_size
part.source_file = iso_img
@classmethod
def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
bootimg_dir, kernel_dir, native_sysroot):
"""
Called after all partitions have been prepared and assembled into a
disk image. In this case, we insert/modify the MBR using isohybrid
utility for booting via BIOS from disk storage devices.
"""
iso_img = "%s.p1" % disk.path
full_path = creator._full_path(workdir, disk_name, "direct")
full_path_iso = creator._full_path(workdir, disk_name, "iso")
isohybrid_cmd = "isohybrid -u %s" % iso_img
logger.debug("running command: %s", isohybrid_cmd)
exec_native_cmd(isohybrid_cmd, native_sysroot)
# Replace the image created by direct plugin with the one created by
# mkisofs command. This is necessary because the iso image created by
# mkisofs has a very specific MBR is system area of the ISO image, and
# direct plugin adds and configures an another MBR.
logger.debug("Replaceing the image created by direct plugin\n")
os.remove(disk.path)
shutil.copy2(iso_img, full_path_iso)
shutil.copy2(full_path_iso, full_path)

View File

@@ -0,0 +1,115 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
import logging
import os
import signal
import subprocess
from wic import WicError
from wic.pluginbase import SourcePlugin
from wic.misc import exec_cmd, get_bitbake_var
from wic.filemap import sparse_copy
logger = logging.getLogger('wic')
class RawCopyPlugin(SourcePlugin):
"""
Populate partition content from raw image file.
"""
name = 'rawcopy'
@staticmethod
def do_image_label(fstype, dst, label):
# don't create label when fstype is none
if fstype == 'none':
return
if fstype.startswith('ext'):
cmd = 'tune2fs -L %s %s' % (label, dst)
elif fstype in ('msdos', 'vfat'):
cmd = 'dosfslabel %s %s' % (dst, label)
elif fstype == 'btrfs':
cmd = 'btrfs filesystem label %s %s' % (dst, label)
elif fstype == 'swap':
cmd = 'mkswap -L %s %s' % (label, dst)
elif fstype in ('squashfs', 'erofs'):
raise WicError("It's not possible to update a %s "
"filesystem label '%s'" % (fstype, label))
else:
raise WicError("Cannot update filesystem label: "
"Unknown fstype: '%s'" % (fstype))
exec_cmd(cmd)
@staticmethod
def do_image_uncompression(src, dst, workdir):
def subprocess_setup():
# Python installs a SIGPIPE handler by default. This is usually not what
# non-Python subprocesses expect.
# SIGPIPE errors are known issues with gzip/bash
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
extension = os.path.splitext(src)[1]
decompressor = {
".bz2": "bzip2",
".gz": "gzip",
".xz": "xz",
".zst": "zstd -f",
}.get(extension)
if not decompressor:
raise WicError("Not supported compressor filename extension: %s" % extension)
cmd = "%s -dc %s > %s" % (decompressor, src, dst)
subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=workdir)
@classmethod
def do_prepare_partition(cls, part, source_params, cr, 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.
"""
if not kernel_dir:
kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not kernel_dir:
raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
logger.debug('Kernel dir: %s', kernel_dir)
if 'file' not in source_params:
raise WicError("No file specified")
if 'unpack' in source_params:
img = os.path.join(kernel_dir, source_params['file'])
src = os.path.join(cr_workdir, os.path.splitext(source_params['file'])[0])
RawCopyPlugin.do_image_uncompression(img, src, cr_workdir)
else:
src = os.path.join(kernel_dir, source_params['file'])
dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno))
if not os.path.exists(os.path.dirname(dst)):
os.makedirs(os.path.dirname(dst))
if 'skip' in source_params:
sparse_copy(src, dst, skip=int(source_params['skip']))
else:
sparse_copy(src, dst)
# get the size in the right units for kickstart (kB)
du_cmd = "du -Lbks %s" % dst
out = exec_cmd(du_cmd)
filesize = int(out.split()[0])
if filesize > part.size:
part.size = filesize
if part.label:
RawCopyPlugin.do_image_label(part.fstype, dst, part.label)
part.source_file = dst

View File

@@ -0,0 +1,236 @@
#
# Copyright (c) 2014, Intel Corporation.
#
# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'rootfs' source plugin class for 'wic'
#
# AUTHORS
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
# Joao Henrique Ferreira de Freitas <joaohf (at] gmail.com>
#
import logging
import os
import shutil
import sys
from oe.path import copyhardlinktree
from pathlib import Path
from wic import WicError
from wic.pluginbase import SourcePlugin
from wic.misc import get_bitbake_var, exec_native_cmd
logger = logging.getLogger('wic')
class RootfsPlugin(SourcePlugin):
"""
Populate partition content from a rootfs directory.
"""
name = 'rootfs'
@staticmethod
def __validate_path(cmd, rootfs_dir, path):
if os.path.isabs(path):
logger.error("%s: Must be relative: %s" % (cmd, path))
sys.exit(1)
# Disallow climbing outside of parent directory using '..',
# because doing so could be quite disastrous (we will delete the
# directory, or modify a directory outside OpenEmbedded).
full_path = os.path.realpath(os.path.join(rootfs_dir, path))
if not full_path.startswith(os.path.realpath(rootfs_dir)):
logger.error("%s: Must point inside the rootfs:" % (cmd, path))
sys.exit(1)
return full_path
@staticmethod
def __get_rootfs_dir(rootfs_dir):
if rootfs_dir and os.path.isdir(rootfs_dir):
return os.path.realpath(rootfs_dir)
image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir)
if not os.path.isdir(image_rootfs_dir):
raise WicError("No valid artifact IMAGE_ROOTFS from image "
"named %s has been found at %s, exiting." %
(rootfs_dir, image_rootfs_dir))
return os.path.realpath(image_rootfs_dir)
@staticmethod
def __get_pseudo(native_sysroot, rootfs, pseudo_dir):
pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir
pseudo += "export PSEUDO_PASSWD=%s;" % rootfs
pseudo += "export PSEUDO_NOSYMLINKEXP=1;"
pseudo += "%s " % get_bitbake_var("FAKEROOTCMD")
return pseudo
@classmethod
def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
krootfs_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.
"""
if part.rootfs_dir is None:
if not 'ROOTFS_DIR' in krootfs_dir:
raise WicError("Couldn't find --rootfs-dir, exiting")
rootfs_dir = krootfs_dir['ROOTFS_DIR']
else:
if part.rootfs_dir in krootfs_dir:
rootfs_dir = krootfs_dir[part.rootfs_dir]
elif part.rootfs_dir:
rootfs_dir = part.rootfs_dir
else:
raise WicError("Couldn't find --rootfs-dir=%s connection or "
"it is not a valid path, exiting" % part.rootfs_dir)
part.rootfs_dir = cls.__get_rootfs_dir(rootfs_dir)
part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab"))
pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo")
if not os.path.lexists(pseudo_dir):
pseudo_dir = os.path.join(cls.__get_rootfs_dir(None), '../pseudo')
if not os.path.lexists(pseudo_dir):
logger.warn("%s folder does not exist. "
"Usernames and permissions will be invalid " % pseudo_dir)
pseudo_dir = None
new_rootfs = None
new_pseudo = None
# Handle excluded paths.
if part.exclude_path or part.include_path or part.change_directory or part.update_fstab_in_rootfs:
# We need a new rootfs directory we can safely modify without
# interfering with other tasks. Copy to workdir.
new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno))
if os.path.lexists(new_rootfs):
shutil.rmtree(os.path.join(new_rootfs))
if part.change_directory:
cd = part.change_directory
if cd[-1] == '/':
cd = cd[:-1]
orig_dir = cls.__validate_path("--change-directory", part.rootfs_dir, cd)
else:
orig_dir = part.rootfs_dir
copyhardlinktree(orig_dir, new_rootfs)
# Convert the pseudo directory to its new location
if (pseudo_dir):
new_pseudo = os.path.realpath(
os.path.join(cr_workdir, "pseudo%d" % part.lineno))
if os.path.lexists(new_pseudo):
shutil.rmtree(new_pseudo)
os.mkdir(new_pseudo)
shutil.copy(os.path.join(pseudo_dir, "files.db"),
os.path.join(new_pseudo, "files.db"))
pseudo_cmd = "%s -B -m %s -M %s" % (cls.__get_pseudo(native_sysroot,
new_rootfs,
new_pseudo),
orig_dir, new_rootfs)
exec_native_cmd(pseudo_cmd, native_sysroot)
for in_path in part.include_path or []:
#parse arguments
include_path = in_path[0]
if len(in_path) > 2:
logger.error("'Invalid number of arguments for include-path")
sys.exit(1)
if len(in_path) == 2:
path = in_path[1]
else:
path = None
# Pack files to be included into a tar file.
# We need to create a tar file, because that way we can keep the
# permissions from the files even when they belong to different
# pseudo enviroments.
# If we simply copy files using copyhardlinktree/copytree... the
# copied files will belong to the user running wic.
tar_file = os.path.realpath(
os.path.join(cr_workdir, "include-path%d.tar" % part.lineno))
if os.path.isfile(include_path):
parent = os.path.dirname(os.path.realpath(include_path))
tar_cmd = "tar c --owner=root --group=root -f %s -C %s %s" % (
tar_file, parent, os.path.relpath(include_path, parent))
exec_native_cmd(tar_cmd, native_sysroot)
else:
if include_path in krootfs_dir:
include_path = krootfs_dir[include_path]
include_path = cls.__get_rootfs_dir(include_path)
include_pseudo = os.path.join(include_path, "../pseudo")
if os.path.lexists(include_pseudo):
pseudo = cls.__get_pseudo(native_sysroot, include_path,
include_pseudo)
tar_cmd = "tar cf %s -C %s ." % (tar_file, include_path)
else:
pseudo = None
tar_cmd = "tar c --owner=root --group=root -f %s -C %s ." % (
tar_file, include_path)
exec_native_cmd(tar_cmd, native_sysroot, pseudo)
#create destination
if path:
destination = cls.__validate_path("--include-path", new_rootfs, path)
Path(destination).mkdir(parents=True, exist_ok=True)
else:
destination = new_rootfs
#extract destination
untar_cmd = "tar xf %s -C %s" % (tar_file, destination)
if new_pseudo:
pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
else:
pseudo = None
exec_native_cmd(untar_cmd, native_sysroot, pseudo)
os.remove(tar_file)
for orig_path in part.exclude_path or []:
path = orig_path
full_path = cls.__validate_path("--exclude-path", new_rootfs, path)
if not os.path.lexists(full_path):
continue
if new_pseudo:
pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
else:
pseudo = None
if path.endswith(os.sep):
# Delete content only.
for entry in os.listdir(full_path):
full_entry = os.path.join(full_path, entry)
rm_cmd = "rm -rf %s" % (full_entry)
exec_native_cmd(rm_cmd, native_sysroot, pseudo)
else:
# Delete whole directory.
rm_cmd = "rm -rf %s" % (full_path)
exec_native_cmd(rm_cmd, native_sysroot, pseudo)
# Update part.has_fstab here as fstab may have been added or
# removed by the above modifications.
part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab"))
if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update:
fstab_path = os.path.join(new_rootfs, "etc/fstab")
# Assume that fstab should always be owned by root with fixed permissions
install_cmd = "install -m 0644 -p %s %s" % (part.updated_fstab_path, fstab_path)
if new_pseudo:
pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
else:
pseudo = None
exec_native_cmd(install_cmd, native_sysroot, pseudo)
part.prepare_rootfs(cr_workdir, oe_builddir,
new_rootfs or part.rootfs_dir, native_sysroot,
pseudo_dir = new_pseudo or pseudo_dir)