summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/plugins')
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py174
-rw-r--r--scripts/lib/wic/plugins/source/bootimg_biosplusefi.py (renamed from scripts/lib/wic/plugins/source/bootimg-biosplusefi.py)30
-rw-r--r--scripts/lib/wic/plugins/source/bootimg_efi.py (renamed from scripts/lib/wic/plugins/source/bootimg-efi.py)189
-rw-r--r--scripts/lib/wic/plugins/source/bootimg_partition.py (renamed from scripts/lib/wic/plugins/source/bootimg-partition.py)52
-rw-r--r--scripts/lib/wic/plugins/source/bootimg_pcbios.py (renamed from scripts/lib/wic/plugins/source/bootimg-pcbios.py)16
-rw-r--r--scripts/lib/wic/plugins/source/empty.py59
-rw-r--r--scripts/lib/wic/plugins/source/isoimage_isohybrid.py (renamed from scripts/lib/wic/plugins/source/isoimage-isohybrid.py)12
-rw-r--r--scripts/lib/wic/plugins/source/rawcopy.py42
-rw-r--r--scripts/lib/wic/plugins/source/rootfs.py15
9 files changed, 408 insertions, 181 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index ea709e8c54..6e1f1c8cba 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -77,7 +77,8 @@ class DirectPlugin(ImagerPlugin):
77 77
78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct") 78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
79 self._image = PartitionedImage(image_path, self.ptable_format, 79 self._image = PartitionedImage(image_path, self.ptable_format,
80 self.parts, self.native_sysroot) 80 self.parts, self.native_sysroot,
81 options.extra_space)
81 82
82 def setup_workdir(self, workdir): 83 def setup_workdir(self, workdir):
83 if workdir: 84 if workdir:
@@ -116,7 +117,7 @@ class DirectPlugin(ImagerPlugin):
116 updated = False 117 updated = False
117 for part in self.parts: 118 for part in self.parts:
118 if not part.realnum or not part.mountpoint \ 119 if not part.realnum or not part.mountpoint \
119 or part.mountpoint == "/": 120 or part.mountpoint == "/" or not (part.mountpoint.startswith('/') or part.mountpoint == "swap"):
120 continue 121 continue
121 122
122 if part.use_uuid: 123 if part.use_uuid:
@@ -137,8 +138,9 @@ class DirectPlugin(ImagerPlugin):
137 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum) 138 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
138 139
139 opts = part.fsopts if part.fsopts else "defaults" 140 opts = part.fsopts if part.fsopts else "defaults"
141 passno = part.fspassno if part.fspassno else "0"
140 line = "\t".join([device_name, part.mountpoint, part.fstype, 142 line = "\t".join([device_name, part.mountpoint, part.fstype,
141 opts, "0", "0"]) + "\n" 143 opts, "0", passno]) + "\n"
142 144
143 fstab_lines.append(line) 145 fstab_lines.append(line)
144 updated = True 146 updated = True
@@ -147,6 +149,9 @@ class DirectPlugin(ImagerPlugin):
147 self.updated_fstab_path = os.path.join(self.workdir, "fstab") 149 self.updated_fstab_path = os.path.join(self.workdir, "fstab")
148 with open(self.updated_fstab_path, "w") as f: 150 with open(self.updated_fstab_path, "w") as f:
149 f.writelines(fstab_lines) 151 f.writelines(fstab_lines)
152 if os.getenv('SOURCE_DATE_EPOCH'):
153 fstab_time = int(os.getenv('SOURCE_DATE_EPOCH'))
154 os.utime(self.updated_fstab_path, (fstab_time, fstab_time))
150 155
151 def _full_path(self, path, name, extention): 156 def _full_path(self, path, name, extention):
152 """ Construct full file path to a file we generate. """ 157 """ Construct full file path to a file we generate. """
@@ -198,6 +203,8 @@ class DirectPlugin(ImagerPlugin):
198 source_plugin = self.ks.bootloader.source 203 source_plugin = self.ks.bootloader.source
199 disk_name = self.parts[0].disk 204 disk_name = self.parts[0].disk
200 if source_plugin: 205 if source_plugin:
206 # Don't support '-' in plugin names
207 source_plugin = source_plugin.replace("-", "_")
201 plugin = PluginMgr.get_plugins('source')[source_plugin] 208 plugin = PluginMgr.get_plugins('source')[source_plugin]
202 plugin.do_install_disk(self._image, disk_name, self, self.workdir, 209 plugin.do_install_disk(self._image, disk_name, self, self.workdir,
203 self.oe_builddir, self.bootimg_dir, 210 self.oe_builddir, self.bootimg_dir,
@@ -258,6 +265,8 @@ class DirectPlugin(ImagerPlugin):
258 if part.mountpoint == "/": 265 if part.mountpoint == "/":
259 if part.uuid: 266 if part.uuid:
260 return "PARTUUID=%s" % part.uuid 267 return "PARTUUID=%s" % part.uuid
268 elif part.label and self.ptable_format != 'msdos':
269 return "PARTLABEL=%s" % part.label
261 else: 270 else:
262 suffix = 'p' if part.disk.startswith('mmcblk') else '' 271 suffix = 'p' if part.disk.startswith('mmcblk') else ''
263 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum) 272 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
@@ -293,7 +302,7 @@ class PartitionedImage():
293 Partitioned image in a file. 302 Partitioned image in a file.
294 """ 303 """
295 304
296 def __init__(self, path, ptable_format, partitions, native_sysroot=None): 305 def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
297 self.path = path # Path to the image file 306 self.path = path # Path to the image file
298 self.numpart = 0 # Number of allocated partitions 307 self.numpart = 0 # Number of allocated partitions
299 self.realpart = 0 # Number of partitions in the partition table 308 self.realpart = 0 # Number of partitions in the partition table
@@ -306,14 +315,26 @@ class PartitionedImage():
306 # all partitions (in bytes) 315 # all partitions (in bytes)
307 self.ptable_format = ptable_format # Partition table format 316 self.ptable_format = ptable_format # Partition table format
308 # Disk system identifier 317 # Disk system identifier
309 self.identifier = random.SystemRandom().randint(1, 0xffffffff) 318 if os.getenv('SOURCE_DATE_EPOCH'):
319 self.identifier = random.Random(int(os.getenv('SOURCE_DATE_EPOCH'))).randint(1, 0xffffffff)
320 else:
321 self.identifier = random.SystemRandom().randint(1, 0xffffffff)
310 322
311 self.partitions = partitions 323 self.partitions = partitions
312 self.partimages = [] 324 self.partimages = []
313 # Size of a sector used in calculations 325 # Size of a sector used in calculations
314 self.sector_size = SECTOR_SIZE 326 sector_size_str = get_bitbake_var('WIC_SECTOR_SIZE')
327 if sector_size_str is not None:
328 try:
329 self.sector_size = int(sector_size_str)
330 except ValueError:
331 self.sector_size = SECTOR_SIZE
332 else:
333 self.sector_size = SECTOR_SIZE
334
315 self.native_sysroot = native_sysroot 335 self.native_sysroot = native_sysroot
316 num_real_partitions = len([p for p in self.partitions if not p.no_table]) 336 num_real_partitions = len([p for p in self.partitions if not p.no_table])
337 self.extra_space = extra_space
317 338
318 # calculate the real partition number, accounting for partitions not 339 # calculate the real partition number, accounting for partitions not
319 # in the partition table and logical partitions 340 # in the partition table and logical partitions
@@ -331,7 +352,7 @@ class PartitionedImage():
331 # generate parition and filesystem UUIDs 352 # generate parition and filesystem UUIDs
332 for part in self.partitions: 353 for part in self.partitions:
333 if not part.uuid and part.use_uuid: 354 if not part.uuid and part.use_uuid:
334 if self.ptable_format == 'gpt': 355 if self.ptable_format in ('gpt', 'gpt-hybrid'):
335 part.uuid = str(uuid.uuid4()) 356 part.uuid = str(uuid.uuid4())
336 else: # msdos partition table 357 else: # msdos partition table
337 part.uuid = '%08x-%02d' % (self.identifier, part.realnum) 358 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
@@ -387,6 +408,10 @@ class PartitionedImage():
387 raise WicError("setting custom partition type is not " \ 408 raise WicError("setting custom partition type is not " \
388 "implemented for msdos partitions") 409 "implemented for msdos partitions")
389 410
411 if part.mbr and self.ptable_format != 'gpt-hybrid':
412 raise WicError("Partition may only be included in MBR with " \
413 "a gpt-hybrid partition table")
414
390 # Get the disk where the partition is located 415 # Get the disk where the partition is located
391 self.numpart += 1 416 self.numpart += 1
392 if not part.no_table: 417 if not part.no_table:
@@ -395,7 +420,7 @@ class PartitionedImage():
395 if self.numpart == 1: 420 if self.numpart == 1:
396 if self.ptable_format == "msdos": 421 if self.ptable_format == "msdos":
397 overhead = MBR_OVERHEAD 422 overhead = MBR_OVERHEAD
398 elif self.ptable_format == "gpt": 423 elif self.ptable_format in ("gpt", "gpt-hybrid"):
399 overhead = GPT_OVERHEAD 424 overhead = GPT_OVERHEAD
400 425
401 # Skip one sector required for the partitioning scheme overhead 426 # Skip one sector required for the partitioning scheme overhead
@@ -479,10 +504,11 @@ class PartitionedImage():
479 # Once all the partitions have been layed out, we can calculate the 504 # Once all the partitions have been layed out, we can calculate the
480 # minumim disk size 505 # minumim disk size
481 self.min_size = self.offset 506 self.min_size = self.offset
482 if self.ptable_format == "gpt": 507 if self.ptable_format in ("gpt", "gpt-hybrid"):
483 self.min_size += GPT_OVERHEAD 508 self.min_size += GPT_OVERHEAD
484 509
485 self.min_size *= self.sector_size 510 self.min_size *= self.sector_size
511 self.min_size += self.extra_space
486 512
487 def _create_partition(self, device, parttype, fstype, start, size): 513 def _create_partition(self, device, parttype, fstype, start, size):
488 """ Create a partition on an image described by the 'device' object. """ 514 """ Create a partition on an image described by the 'device' object. """
@@ -492,29 +518,58 @@ class PartitionedImage():
492 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors", 518 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
493 parttype, start, end, size) 519 parttype, start, end, size)
494 520
495 cmd = "parted -s %s unit s mkpart %s" % (device, parttype) 521 cmd = "export PARTED_SECTOR_SIZE=%d; parted -s %s unit s mkpart %s" % \
522 (self.sector_size, device, parttype)
496 if fstype: 523 if fstype:
497 cmd += " %s" % fstype 524 cmd += " %s" % fstype
498 cmd += " %d %d" % (start, end) 525 cmd += " %d %d" % (start, end)
499 526
500 return exec_native_cmd(cmd, self.native_sysroot) 527 return exec_native_cmd(cmd, self.native_sysroot)
501 528
529 def _write_identifier(self, device, identifier):
530 logger.debug("Set disk identifier %x", identifier)
531 with open(device, 'r+b') as img:
532 img.seek(0x1B8)
533 img.write(identifier.to_bytes(4, 'little'))
534
535 def _make_disk(self, device, ptable_format, min_size):
536 logger.debug("Creating sparse file %s", device)
537 with open(device, 'w') as sparse:
538 os.ftruncate(sparse.fileno(), min_size)
539
540 logger.debug("Initializing partition table for %s", device)
541 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s mklabel %s" %
542 (self.sector_size, device, ptable_format), self.native_sysroot)
543
544 def _write_disk_guid(self):
545 if self.ptable_format in ('gpt', 'gpt-hybrid'):
546 if os.getenv('SOURCE_DATE_EPOCH'):
547 self.disk_guid = uuid.UUID(int=int(os.getenv('SOURCE_DATE_EPOCH')))
548 else:
549 self.disk_guid = uuid.uuid4()
550
551 logger.debug("Set disk guid %s", self.disk_guid)
552 sfdisk_cmd = "sfdisk --sector-size %s --disk-id %s %s" % \
553 (self.sector_size, self.path, self.disk_guid)
554 exec_native_cmd(sfdisk_cmd, self.native_sysroot)
555
502 def create(self): 556 def create(self):
503 logger.debug("Creating sparse file %s", self.path) 557 self._make_disk(self.path,
504 with open(self.path, 'w') as sparse: 558 "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
505 os.ftruncate(sparse.fileno(), self.min_size) 559 self.min_size)
506 560
507 logger.debug("Initializing partition table for %s", self.path) 561 self._write_identifier(self.path, self.identifier)
508 exec_native_cmd("parted -s %s mklabel %s" % 562 self._write_disk_guid()
509 (self.path, self.ptable_format), self.native_sysroot)
510 563
511 logger.debug("Set disk identifier %x", self.identifier) 564 if self.ptable_format == "gpt-hybrid":
512 with open(self.path, 'r+b') as img: 565 mbr_path = self.path + ".mbr"
513 img.seek(0x1B8) 566 self._make_disk(mbr_path, "msdos", self.min_size)
514 img.write(self.identifier.to_bytes(4, 'little')) 567 self._write_identifier(mbr_path, self.identifier)
515 568
516 logger.debug("Creating partitions") 569 logger.debug("Creating partitions")
517 570
571 hybrid_mbr_part_num = 0
572
518 for part in self.partitions: 573 for part in self.partitions:
519 if part.num == 0: 574 if part.num == 0:
520 continue 575 continue
@@ -559,46 +614,77 @@ class PartitionedImage():
559 self._create_partition(self.path, part.type, 614 self._create_partition(self.path, part.type,
560 parted_fs_type, part.start, part.size_sec) 615 parted_fs_type, part.start, part.size_sec)
561 616
562 if part.part_name: 617 if self.ptable_format == "gpt-hybrid" and part.mbr:
563 logger.debug("partition %d: set name to %s", 618 hybrid_mbr_part_num += 1
564 part.num, part.part_name) 619 if hybrid_mbr_part_num > 4:
565 exec_native_cmd("sgdisk --change-name=%d:%s %s" % \ 620 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
566 (part.num, part.part_name, 621 self._create_partition(mbr_path, "primary",
567 self.path), self.native_sysroot) 622 parted_fs_type, part.start, part.size_sec)
568 623
624 if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
625 partition_label = part.part_name if part.part_name else part.label
626 logger.debug("partition %d: set name to %s",
627 part.num, partition_label)
628 exec_native_cmd("sfdisk --sector-size %s --part-label %s %d %s" % \
629 (self.sector_size, self.path, part.num,
630 partition_label), self.native_sysroot)
569 if part.part_type: 631 if part.part_type:
570 logger.debug("partition %d: set type UID to %s", 632 logger.debug("partition %d: set type UID to %s",
571 part.num, part.part_type) 633 part.num, part.part_type)
572 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ 634 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d %s" % \
573 (part.num, part.part_type, 635 (self.sector_size, self.path, part.num,
574 self.path), self.native_sysroot) 636 part.part_type), self.native_sysroot)
575 637
576 if part.uuid and self.ptable_format == "gpt": 638 if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
577 logger.debug("partition %d: set UUID to %s", 639 logger.debug("partition %d: set UUID to %s",
578 part.num, part.uuid) 640 part.num, part.uuid)
579 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ 641 exec_native_cmd("sfdisk --sector-size %s --part-uuid %s %d %s" % \
580 (part.num, part.uuid, self.path), 642 (self.sector_size, self.path, part.num, part.uuid),
581 self.native_sysroot)
582
583 if part.label and self.ptable_format == "gpt":
584 logger.debug("partition %d: set name to %s",
585 part.num, part.label)
586 exec_native_cmd("parted -s %s name %d %s" % \
587 (self.path, part.num, part.label),
588 self.native_sysroot) 643 self.native_sysroot)
589 644
590 if part.active: 645 if part.active:
591 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" 646 flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
592 logger.debug("Set '%s' flag for partition '%s' on disk '%s'", 647 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
593 flag_name, part.num, self.path) 648 flag_name, part.num, self.path)
594 exec_native_cmd("parted -s %s set %d %s on" % \ 649 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
595 (self.path, part.num, flag_name), 650 (self.sector_size, self.path, part.num, flag_name),
596 self.native_sysroot) 651 self.native_sysroot)
652 if self.ptable_format == 'gpt-hybrid' and part.mbr:
653 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
654 (self.sector_size, mbr_path, hybrid_mbr_part_num, "boot"),
655 self.native_sysroot)
597 if part.system_id: 656 if part.system_id:
598 exec_native_cmd("sfdisk --part-type %s %s %s" % \ 657 exec_native_cmd("sfdisk --sector-size %s --part-type %s %s %s" % \
599 (self.path, part.num, part.system_id), 658 (self.sector_size, self.path, part.num, part.system_id),
600 self.native_sysroot) 659 self.native_sysroot)
601 660
661 if part.hidden and self.ptable_format == "gpt":
662 logger.debug("Set hidden attribute for partition '%s' on disk '%s'",
663 part.num, self.path)
664 exec_native_cmd("sfdisk --sector-size %s --part-attrs %s %s RequiredPartition" % \
665 (self.sector_size, self.path, part.num),
666 self.native_sysroot)
667
668 if self.ptable_format == "gpt-hybrid":
669 # Write a protective GPT partition
670 hybrid_mbr_part_num += 1
671 if hybrid_mbr_part_num > 4:
672 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
673
674 # parted cannot directly create a protective GPT partition, so
675 # create with an arbitrary type, then change it to the correct type
676 # with sfdisk
677 self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
678 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d 0xee" % \
679 (self.sector_size, mbr_path, hybrid_mbr_part_num),
680 self.native_sysroot)
681
682 # Copy hybrid MBR
683 with open(mbr_path, "rb") as mbr_file:
684 with open(self.path, "r+b") as image_file:
685 mbr = mbr_file.read(512)
686 image_file.write(mbr)
687
602 def cleanup(self): 688 def cleanup(self):
603 pass 689 pass
604 690
diff --git a/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py b/scripts/lib/wic/plugins/source/bootimg_biosplusefi.py
index 5bd7390680..4279ddded8 100644
--- a/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py
+++ b/scripts/lib/wic/plugins/source/bootimg_biosplusefi.py
@@ -13,7 +13,7 @@
13# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
14# 14#
15# DESCRIPTION 15# DESCRIPTION
16# This implements the 'bootimg-biosplusefi' source plugin class for 'wic' 16# This implements the 'bootimg_biosplusefi' source plugin class for 'wic'
17# 17#
18# AUTHORS 18# AUTHORS
19# William Bourque <wbourque [at) gmail.com> 19# William Bourque <wbourque [at) gmail.com>
@@ -34,7 +34,7 @@ class BootimgBiosPlusEFIPlugin(SourcePlugin):
34 34
35 Note it is possible to create an image that can boot from both 35 Note it is possible to create an image that can boot from both
36 legacy BIOS and EFI by defining two partitions : one with arg 36 legacy BIOS and EFI by defining two partitions : one with arg
37 --source bootimg-efi and another one with --source bootimg-pcbios. 37 --source bootimg_efi and another one with --source bootimg_pcbios.
38 However, this method has the obvious downside that it requires TWO 38 However, this method has the obvious downside that it requires TWO
39 partitions to be created on the storage device. 39 partitions to be created on the storage device.
40 Both partitions will also be marked as "bootable" which does not work on 40 Both partitions will also be marked as "bootable" which does not work on
@@ -45,7 +45,7 @@ class BootimgBiosPlusEFIPlugin(SourcePlugin):
45 the first partition will be duplicated into the second, even though it 45 the first partition will be duplicated into the second, even though it
46 will not be used at all. 46 will not be used at all.
47 47
48 Also, unlike "isoimage-isohybrid" that also does BIOS and EFI, this plugin 48 Also, unlike "isoimage_isohybrid" that also does BIOS and EFI, this plugin
49 allows you to have more than only a single rootfs partitions and does 49 allows you to have more than only a single rootfs partitions and does
50 not turn the rootfs into an initramfs RAM image. 50 not turn the rootfs into an initramfs RAM image.
51 51
@@ -53,32 +53,32 @@ class BootimgBiosPlusEFIPlugin(SourcePlugin):
53 does not have the limitations listed above. 53 does not have the limitations listed above.
54 54
55 The plugin is made so it does tries not to reimplement what's already 55 The plugin is made so it does tries not to reimplement what's already
56 been done in other plugins; as such it imports "bootimg-pcbios" 56 been done in other plugins; as such it imports "bootimg_pcbios"
57 and "bootimg-efi". 57 and "bootimg_efi".
58 Plugin "bootimg-pcbios" is used to generate legacy BIOS boot. 58 Plugin "bootimg_pcbios" is used to generate legacy BIOS boot.
59 Plugin "bootimg-efi" is used to generate the UEFI boot. Note that it 59 Plugin "bootimg_efi" is used to generate the UEFI boot. Note that it
60 requires a --sourceparams argument to know which loader to use; refer 60 requires a --sourceparams argument to know which loader to use; refer
61 to "bootimg-efi" code/documentation for the list of loader. 61 to "bootimg_efi" code/documentation for the list of loader.
62 62
63 Imports are handled with "SourceFileLoader" from importlib as it is 63 Imports are handled with "SourceFileLoader" from importlib as it is
64 otherwise very difficult to import module that has hyphen "-" in their 64 otherwise very difficult to import module that has hyphen "-" in their
65 filename. 65 filename.
66 The SourcePlugin() methods used in the plugins (do_install_disk, 66 The SourcePlugin() methods used in the plugins (do_install_disk,
67 do_configure_partition, do_prepare_partition) are then called on both, 67 do_configure_partition, do_prepare_partition) are then called on both,
68 beginning by "bootimg-efi". 68 beginning by "bootimg_efi".
69 69
70 Plugin options, such as "--sourceparams" can still be passed to a 70 Plugin options, such as "--sourceparams" can still be passed to a
71 plugin, as long they does not cause issue in the other plugin. 71 plugin, as long they does not cause issue in the other plugin.
72 72
73 Example wic configuration: 73 Example wic configuration:
74 part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\\ 74 part /boot --source bootimg_biosplusefi --sourceparams="loader=grub-efi"\\
75 --ondisk sda --label os_boot --active --align 1024 --use-uuid 75 --ondisk sda --label os_boot --active --align 1024 --use-uuid
76 """ 76 """
77 77
78 name = 'bootimg-biosplusefi' 78 name = 'bootimg_biosplusefi'
79 79
80 __PCBIOS_MODULE_NAME = "bootimg-pcbios" 80 __PCBIOS_MODULE_NAME = "bootimg_pcbios"
81 __EFI_MODULE_NAME = "bootimg-efi" 81 __EFI_MODULE_NAME = "bootimg_efi"
82 82
83 __imgEFIObj = None 83 __imgEFIObj = None
84 __imgBiosObj = None 84 __imgBiosObj = None
@@ -100,7 +100,7 @@ class BootimgBiosPlusEFIPlugin(SourcePlugin):
100 100
101 """ 101 """
102 102
103 # Import bootimg-pcbios (class name "BootimgPcbiosPlugin") 103 # Import bootimg_pcbios (class name "BootimgPcbiosPlugin")
104 modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), 104 modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
105 cls.__PCBIOS_MODULE_NAME + ".py") 105 cls.__PCBIOS_MODULE_NAME + ".py")
106 loader = SourceFileLoader(cls.__PCBIOS_MODULE_NAME, modulePath) 106 loader = SourceFileLoader(cls.__PCBIOS_MODULE_NAME, modulePath)
@@ -108,7 +108,7 @@ class BootimgBiosPlusEFIPlugin(SourcePlugin):
108 loader.exec_module(mod) 108 loader.exec_module(mod)
109 cls.__imgBiosObj = mod.BootimgPcbiosPlugin() 109 cls.__imgBiosObj = mod.BootimgPcbiosPlugin()
110 110
111 # Import bootimg-efi (class name "BootimgEFIPlugin") 111 # Import bootimg_efi (class name "BootimgEFIPlugin")
112 modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), 112 modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
113 cls.__EFI_MODULE_NAME + ".py") 113 cls.__EFI_MODULE_NAME + ".py")
114 loader = SourceFileLoader(cls.__EFI_MODULE_NAME, modulePath) 114 loader = SourceFileLoader(cls.__EFI_MODULE_NAME, modulePath)
diff --git a/scripts/lib/wic/plugins/source/bootimg-efi.py b/scripts/lib/wic/plugins/source/bootimg_efi.py
index cdc72543c2..cf16705a28 100644
--- a/scripts/lib/wic/plugins/source/bootimg-efi.py
+++ b/scripts/lib/wic/plugins/source/bootimg_efi.py
@@ -4,7 +4,7 @@
4# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
5# 5#
6# DESCRIPTION 6# DESCRIPTION
7# This implements the 'bootimg-efi' source plugin class for 'wic' 7# This implements the 'bootimg_efi' source plugin class for 'wic'
8# 8#
9# AUTHORS 9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com> 10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
@@ -12,6 +12,7 @@
12 12
13import logging 13import logging
14import os 14import os
15import tempfile
15import shutil 16import shutil
16import re 17import re
17 18
@@ -31,7 +32,29 @@ class BootimgEFIPlugin(SourcePlugin):
31 This plugin supports GRUB 2 and systemd-boot bootloaders. 32 This plugin supports GRUB 2 and systemd-boot bootloaders.
32 """ 33 """
33 34
34 name = 'bootimg-efi' 35 name = 'bootimg_efi'
36
37 @classmethod
38 def _copy_additional_files(cls, hdddir, initrd, dtb):
39 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
40 if not bootimg_dir:
41 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
42
43 if initrd:
44 initrds = initrd.split(';')
45 for rd in initrds:
46 cp_cmd = "cp -v -p %s/%s %s" % (bootimg_dir, rd, hdddir)
47 out = exec_cmd(cp_cmd, True)
48 logger.debug("initrd files:\n%s" % (out))
49 else:
50 logger.debug("Ignoring missing initrd")
51
52 if dtb:
53 if ';' in dtb:
54 raise WicError("Only one DTB supported, exiting")
55 cp_cmd = "cp -v -p %s/%s %s" % (bootimg_dir, dtb, hdddir)
56 out = exec_cmd(cp_cmd, True)
57 logger.debug("dtb files:\n%s" % (out))
35 58
36 @classmethod 59 @classmethod
37 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params): 60 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
@@ -52,18 +75,9 @@ class BootimgEFIPlugin(SourcePlugin):
52 "get it from %s." % configfile) 75 "get it from %s." % configfile)
53 76
54 initrd = source_params.get('initrd') 77 initrd = source_params.get('initrd')
78 dtb = source_params.get('dtb')
55 79
56 if initrd: 80 cls._copy_additional_files(hdddir, initrd, dtb)
57 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
58 if not bootimg_dir:
59 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
60
61 initrds = initrd.split(';')
62 for rd in initrds:
63 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
64 exec_cmd(cp_cmd, True)
65 else:
66 logger.debug("Ignoring missing initrd")
67 81
68 if not custom_cfg: 82 if not custom_cfg:
69 # Create grub configuration using parameters from wks file 83 # Create grub configuration using parameters from wks file
@@ -97,6 +111,9 @@ class BootimgEFIPlugin(SourcePlugin):
97 grubefi_conf += " /%s" % rd 111 grubefi_conf += " /%s" % rd
98 grubefi_conf += "\n" 112 grubefi_conf += "\n"
99 113
114 if dtb:
115 grubefi_conf += "devicetree /%s\n" % dtb
116
100 grubefi_conf += "}\n" 117 grubefi_conf += "}\n"
101 118
102 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg", 119 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
@@ -108,8 +125,16 @@ class BootimgEFIPlugin(SourcePlugin):
108 @classmethod 125 @classmethod
109 def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params): 126 def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params):
110 """ 127 """
111 Create loader-specific systemd-boot/gummiboot config 128 Create loader-specific systemd-boot/gummiboot config. Unified Kernel Image (uki)
129 support is done in image recipe with uki.bbclass and only systemd-boot loader config
130 and ESP partition structure is created here.
112 """ 131 """
132 # detect uki.bbclass usage
133 image_classes = get_bitbake_var("IMAGE_CLASSES").split()
134 unified_image = False
135 if "uki" in image_classes:
136 unified_image = True
137
113 install_cmd = "install -d %s/loader" % hdddir 138 install_cmd = "install -d %s/loader" % hdddir
114 exec_cmd(install_cmd) 139 exec_cmd(install_cmd)
115 140
@@ -117,34 +142,26 @@ class BootimgEFIPlugin(SourcePlugin):
117 exec_cmd(install_cmd) 142 exec_cmd(install_cmd)
118 143
119 bootloader = creator.ks.bootloader 144 bootloader = creator.ks.bootloader
120
121 loader_conf = "" 145 loader_conf = ""
122 loader_conf += "default boot\n"
123 loader_conf += "timeout %d\n" % bootloader.timeout
124
125 initrd = source_params.get('initrd')
126
127 if initrd:
128 # obviously we need to have a common common deploy var
129 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
130 if not bootimg_dir:
131 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
132 146
133 initrds = initrd.split(';') 147 # 5 seconds is a sensible default timeout
134 for rd in initrds: 148 loader_conf += "timeout %d\n" % (bootloader.timeout or 5)
135 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
136 exec_cmd(cp_cmd, True)
137 else:
138 logger.debug("Ignoring missing initrd")
139 149
140 logger.debug("Writing systemd-boot config " 150 logger.debug("Writing systemd-boot config "
141 "%s/hdd/boot/loader/loader.conf", cr_workdir) 151 "%s/hdd/boot/loader/loader.conf", cr_workdir)
142 cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w") 152 cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w")
143 cfg.write(loader_conf) 153 cfg.write(loader_conf)
154 logger.debug("loader.conf:\n%s" % (loader_conf))
144 cfg.close() 155 cfg.close()
145 156
157 initrd = source_params.get('initrd')
158 dtb = source_params.get('dtb')
159 if not unified_image:
160 cls._copy_additional_files(hdddir, initrd, dtb)
161
146 configfile = creator.ks.bootloader.configfile 162 configfile = creator.ks.bootloader.configfile
147 custom_cfg = None 163 custom_cfg = None
164 boot_conf = ""
148 if configfile: 165 if configfile:
149 custom_cfg = get_custom_config(configfile) 166 custom_cfg = get_custom_config(configfile)
150 if custom_cfg: 167 if custom_cfg:
@@ -155,8 +172,7 @@ class BootimgEFIPlugin(SourcePlugin):
155 else: 172 else:
156 raise WicError("configfile is specified but failed to " 173 raise WicError("configfile is specified but failed to "
157 "get it from %s.", configfile) 174 "get it from %s.", configfile)
158 175 else:
159 if not custom_cfg:
160 # Create systemd-boot configuration using parameters from wks file 176 # Create systemd-boot configuration using parameters from wks file
161 kernel = get_bitbake_var("KERNEL_IMAGETYPE") 177 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
162 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": 178 if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
@@ -166,7 +182,6 @@ class BootimgEFIPlugin(SourcePlugin):
166 182
167 title = source_params.get('title') 183 title = source_params.get('title')
168 184
169 boot_conf = ""
170 boot_conf += "title %s\n" % (title if title else "boot") 185 boot_conf += "title %s\n" % (title if title else "boot")
171 boot_conf += "linux /%s\n" % kernel 186 boot_conf += "linux /%s\n" % kernel
172 187
@@ -183,11 +198,16 @@ class BootimgEFIPlugin(SourcePlugin):
183 for rd in initrds: 198 for rd in initrds:
184 boot_conf += "initrd /%s\n" % rd 199 boot_conf += "initrd /%s\n" % rd
185 200
186 logger.debug("Writing systemd-boot config " 201 if dtb:
187 "%s/hdd/boot/loader/entries/boot.conf", cr_workdir) 202 boot_conf += "devicetree /%s\n" % dtb
188 cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w") 203
189 cfg.write(boot_conf) 204 if not unified_image:
190 cfg.close() 205 logger.debug("Writing systemd-boot config "
206 "%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
207 cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
208 cfg.write(boot_conf)
209 logger.debug("boot.conf:\n%s" % (boot_conf))
210 cfg.close()
191 211
192 212
193 @classmethod 213 @classmethod
@@ -207,10 +227,12 @@ class BootimgEFIPlugin(SourcePlugin):
207 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params) 227 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
208 elif source_params['loader'] == 'systemd-boot': 228 elif source_params['loader'] == 'systemd-boot':
209 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params) 229 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
230 elif source_params['loader'] == 'uefi-kernel':
231 pass
210 else: 232 else:
211 raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader']) 233 raise WicError("unrecognized bootimg_efi loader: %s" % source_params['loader'])
212 except KeyError: 234 except KeyError:
213 raise WicError("bootimg-efi requires a loader, none specified") 235 raise WicError("bootimg_efi requires a loader, none specified")
214 236
215 if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None: 237 if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None:
216 logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES') 238 logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES')
@@ -230,7 +252,7 @@ class BootimgEFIPlugin(SourcePlugin):
230 252
231 # list of tuples (src_name, dst_name) 253 # list of tuples (src_name, dst_name)
232 deploy_files = [] 254 deploy_files = []
233 for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files): 255 for src_entry in re.findall(r'[\w;\-\.\+/\*]+', boot_files):
234 if ';' in src_entry: 256 if ';' in src_entry:
235 dst_entry = tuple(src_entry.split(';')) 257 dst_entry = tuple(src_entry.split(';'))
236 if not dst_entry[0] or not dst_entry[1]: 258 if not dst_entry[0] or not dst_entry[1]:
@@ -288,40 +310,84 @@ class BootimgEFIPlugin(SourcePlugin):
288 kernel = "%s-%s.bin" % \ 310 kernel = "%s-%s.bin" % \
289 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 311 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
290 312
291 install_cmd = "install -m 0644 %s/%s %s/%s" % \ 313 if source_params.get('create-unified-kernel-image') == "true":
292 (staging_kernel_dir, kernel, hdddir, kernel) 314 raise WicError("create-unified-kernel-image is no longer supported. Please use uki.bbclass.")
293 exec_cmd(install_cmd) 315
316 if source_params.get('install-kernel-into-boot-dir') != 'false':
317 install_cmd = "install -v -p -m 0644 %s/%s %s/%s" % \
318 (staging_kernel_dir, kernel, hdddir, kernel)
319 out = exec_cmd(install_cmd)
320 logger.debug("Installed kernel files:\n%s" % out)
294 321
295 if get_bitbake_var("IMAGE_EFI_BOOT_FILES"): 322 if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
296 for src_path, dst_path in cls.install_task: 323 for src_path, dst_path in cls.install_task:
297 install_cmd = "install -m 0644 -D %s %s" \ 324 install_cmd = "install -v -p -m 0644 -D %s %s" \
298 % (os.path.join(kernel_dir, src_path), 325 % (os.path.join(kernel_dir, src_path),
299 os.path.join(hdddir, dst_path)) 326 os.path.join(hdddir, dst_path))
300 exec_cmd(install_cmd) 327 out = exec_cmd(install_cmd)
328 logger.debug("Installed IMAGE_EFI_BOOT_FILES:\n%s" % out)
301 329
302 try: 330 try:
303 if source_params['loader'] == 'grub-efi': 331 if source_params['loader'] == 'grub-efi':
304 shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, 332 shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
305 "%s/grub.cfg" % cr_workdir) 333 "%s/grub.cfg" % cr_workdir)
306 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]: 334 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]:
307 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:]) 335 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:])
308 exec_cmd(cp_cmd, True) 336 exec_cmd(cp_cmd, True)
309 shutil.move("%s/grub.cfg" % cr_workdir, 337 shutil.move("%s/grub.cfg" % cr_workdir,
310 "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir) 338 "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
311 elif source_params['loader'] == 'systemd-boot': 339 elif source_params['loader'] == 'systemd-boot':
312 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]: 340 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
313 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:]) 341 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
314 exec_cmd(cp_cmd, True) 342 out = exec_cmd(cp_cmd, True)
343 logger.debug("systemd-boot files:\n%s" % out)
344 elif source_params['loader'] == 'uefi-kernel':
345 kernel = get_bitbake_var("KERNEL_IMAGETYPE")
346 if not kernel:
347 raise WicError("Empty KERNEL_IMAGETYPE")
348 target = get_bitbake_var("TARGET_SYS")
349 if not target:
350 raise WicError("Empty TARGET_SYS")
351
352 if re.match("x86_64", target):
353 kernel_efi_image = "bootx64.efi"
354 elif re.match('i.86', target):
355 kernel_efi_image = "bootia32.efi"
356 elif re.match('aarch64', target):
357 kernel_efi_image = "bootaa64.efi"
358 elif re.match('arm', target):
359 kernel_efi_image = "bootarm.efi"
360 else:
361 raise WicError("UEFI stub kernel is incompatible with target %s" % target)
362
363 for mod in [x for x in os.listdir(kernel_dir) if x.startswith(kernel)]:
364 cp_cmd = "cp -v -p %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, kernel_efi_image)
365 out = exec_cmd(cp_cmd, True)
366 logger.debug("uefi-kernel files:\n%s" % out)
315 else: 367 else:
316 raise WicError("unrecognized bootimg-efi loader: %s" % 368 raise WicError("unrecognized bootimg_efi loader: %s" %
317 source_params['loader']) 369 source_params['loader'])
370
371 # must have installed at least one EFI bootloader
372 out = glob(os.path.join(hdddir, 'EFI', 'BOOT', 'boot*.efi'))
373 logger.debug("Installed EFI loader files:\n%s" % out)
374 if not out:
375 raise WicError("No EFI loaders installed to ESP partition. Check that grub-efi, systemd-boot or similar is installed.")
376
318 except KeyError: 377 except KeyError:
319 raise WicError("bootimg-efi requires a loader, none specified") 378 raise WicError("bootimg_efi requires a loader, none specified")
320 379
321 startup = os.path.join(kernel_dir, "startup.nsh") 380 startup = os.path.join(kernel_dir, "startup.nsh")
322 if os.path.exists(startup): 381 if os.path.exists(startup):
323 cp_cmd = "cp %s %s/" % (startup, hdddir) 382 cp_cmd = "cp -v -p %s %s/" % (startup, hdddir)
324 exec_cmd(cp_cmd, True) 383 out = exec_cmd(cp_cmd, True)
384 logger.debug("startup files:\n%s" % out)
385
386 for paths in part.include_path or []:
387 for path in paths:
388 cp_cmd = "cp -v -p -r %s %s/" % (path, hdddir)
389 exec_cmd(cp_cmd, True)
390 logger.debug("include_path files:\n%s" % out)
325 391
326 du_cmd = "du -bks %s" % hdddir 392 du_cmd = "du -bks %s" % hdddir
327 out = exec_cmd(du_cmd) 393 out = exec_cmd(du_cmd)
@@ -337,17 +403,26 @@ class BootimgEFIPlugin(SourcePlugin):
337 logger.debug("Added %d extra blocks to %s to get to %d total blocks", 403 logger.debug("Added %d extra blocks to %s to get to %d total blocks",
338 extra_blocks, part.mountpoint, blocks) 404 extra_blocks, part.mountpoint, blocks)
339 405
406 # required for compatibility with certain devices expecting file system
407 # block count to be equal to partition block count
408 if blocks < part.fixed_size:
409 blocks = part.fixed_size
410 logger.debug("Overriding %s to %d total blocks for compatibility",
411 part.mountpoint, blocks)
412
340 # dosfs image, created by mkdosfs 413 # dosfs image, created by mkdosfs
341 bootimg = "%s/boot.img" % cr_workdir 414 bootimg = "%s/boot.img" % cr_workdir
342 415
343 label = part.label if part.label else "ESP" 416 label = part.label if part.label else "ESP"
344 417
345 dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \ 418 dosfs_cmd = "mkdosfs -v -n %s -i %s -C %s %d" % \
346 (label, part.fsuuid, bootimg, blocks) 419 (label, part.fsuuid, bootimg, blocks)
347 exec_native_cmd(dosfs_cmd, native_sysroot) 420 exec_native_cmd(dosfs_cmd, native_sysroot)
421 logger.debug("mkdosfs:\n%s" % (str(out)))
348 422
349 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) 423 mcopy_cmd = "mcopy -v -p -i %s -s %s/* ::/" % (bootimg, hdddir)
350 exec_native_cmd(mcopy_cmd, native_sysroot) 424 out = exec_native_cmd(mcopy_cmd, native_sysroot)
425 logger.debug("mcopy:\n%s" % (str(out)))
351 426
352 chmod_cmd = "chmod 644 %s" % bootimg 427 chmod_cmd = "chmod 644 %s" % bootimg
353 exec_cmd(chmod_cmd) 428 exec_cmd(chmod_cmd)
diff --git a/scripts/lib/wic/plugins/source/bootimg-partition.py b/scripts/lib/wic/plugins/source/bootimg_partition.py
index 5dbe2558d2..cc121a78f0 100644
--- a/scripts/lib/wic/plugins/source/bootimg-partition.py
+++ b/scripts/lib/wic/plugins/source/bootimg_partition.py
@@ -1,8 +1,10 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4# DESCRIPTION 6# DESCRIPTION
5# This implements the 'bootimg-partition' source plugin class for 7# This implements the 'bootimg_partition' source plugin class for
6# 'wic'. The plugin creates an image of boot partition, copying over 8# 'wic'. The plugin creates an image of boot partition, copying over
7# files listed in IMAGE_BOOT_FILES bitbake variable. 9# files listed in IMAGE_BOOT_FILES bitbake variable.
8# 10#
@@ -14,7 +16,7 @@ import logging
14import os 16import os
15import re 17import re
16 18
17from glob import glob 19from oe.bootfiles import get_boot_files
18 20
19from wic import WicError 21from wic import WicError
20from wic.engine import get_custom_config 22from wic.engine import get_custom_config
@@ -29,7 +31,8 @@ class BootimgPartitionPlugin(SourcePlugin):
29 listed in IMAGE_BOOT_FILES bitbake variable. 31 listed in IMAGE_BOOT_FILES bitbake variable.
30 """ 32 """
31 33
32 name = 'bootimg-partition' 34 name = 'bootimg_partition'
35 image_boot_files_var_name = 'IMAGE_BOOT_FILES'
33 36
34 @classmethod 37 @classmethod
35 def do_configure_partition(cls, part, source_params, cr, cr_workdir, 38 def do_configure_partition(cls, part, source_params, cr, cr_workdir,
@@ -54,51 +57,16 @@ class BootimgPartitionPlugin(SourcePlugin):
54 else: 57 else:
55 var = "" 58 var = ""
56 59
57 boot_files = get_bitbake_var("IMAGE_BOOT_FILES" + var) 60 boot_files = get_bitbake_var(cls.image_boot_files_var_name + var)
58 if boot_files is not None: 61 if boot_files is not None:
59 break 62 break
60 63
61 if boot_files is None: 64 if boot_files is None:
62 raise WicError('No boot files defined, IMAGE_BOOT_FILES unset for entry #%d' % part.lineno) 65 raise WicError('No boot files defined, %s unset for entry #%d' % (cls.image_boot_files_var_name, part.lineno))
63 66
64 logger.debug('Boot files: %s', boot_files) 67 logger.debug('Boot files: %s', boot_files)
65 68
66 # list of tuples (src_name, dst_name) 69 cls.install_task = get_boot_files(kernel_dir, boot_files)
67 deploy_files = []
68 for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files):
69 if ';' in src_entry:
70 dst_entry = tuple(src_entry.split(';'))
71 if not dst_entry[0] or not dst_entry[1]:
72 raise WicError('Malformed boot file entry: %s' % src_entry)
73 else:
74 dst_entry = (src_entry, src_entry)
75
76 logger.debug('Destination entry: %r', dst_entry)
77 deploy_files.append(dst_entry)
78
79 cls.install_task = [];
80 for deploy_entry in deploy_files:
81 src, dst = deploy_entry
82 if '*' in src:
83 # by default install files under their basename
84 entry_name_fn = os.path.basename
85 if dst != src:
86 # unless a target name was given, then treat name
87 # as a directory and append a basename
88 entry_name_fn = lambda name: \
89 os.path.join(dst,
90 os.path.basename(name))
91
92 srcs = glob(os.path.join(kernel_dir, src))
93
94 logger.debug('Globbed sources: %s', ', '.join(srcs))
95 for entry in srcs:
96 src = os.path.relpath(entry, kernel_dir)
97 entry_dst_name = entry_name_fn(entry)
98 cls.install_task.append((src, entry_dst_name))
99 else:
100 cls.install_task.append((src, dst))
101
102 if source_params.get('loader') != "u-boot": 70 if source_params.get('loader') != "u-boot":
103 return 71 return
104 72
@@ -110,7 +78,7 @@ class BootimgPartitionPlugin(SourcePlugin):
110 # Use a custom configuration for extlinux.conf 78 # Use a custom configuration for extlinux.conf
111 extlinux_conf = custom_cfg 79 extlinux_conf = custom_cfg
112 logger.debug("Using custom configuration file " 80 logger.debug("Using custom configuration file "
113 "%s for extlinux.cfg", configfile) 81 "%s for extlinux.conf", configfile)
114 else: 82 else:
115 raise WicError("configfile is specified but failed to " 83 raise WicError("configfile is specified but failed to "
116 "get it from %s." % configfile) 84 "get it from %s." % configfile)
diff --git a/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/scripts/lib/wic/plugins/source/bootimg_pcbios.py
index f2639e7004..21f41e00bb 100644
--- a/scripts/lib/wic/plugins/source/bootimg-pcbios.py
+++ b/scripts/lib/wic/plugins/source/bootimg_pcbios.py
@@ -4,7 +4,7 @@
4# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
5# 5#
6# DESCRIPTION 6# DESCRIPTION
7# This implements the 'bootimg-pcbios' source plugin class for 'wic' 7# This implements the 'bootimg_pcbios' source plugin class for 'wic'
8# 8#
9# AUTHORS 9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com> 10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
@@ -27,7 +27,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
27 Create MBR boot partition and install syslinux on it. 27 Create MBR boot partition and install syslinux on it.
28 """ 28 """
29 29
30 name = 'bootimg-pcbios' 30 name = 'bootimg_pcbios'
31 31
32 @classmethod 32 @classmethod
33 def _get_bootimg_dir(cls, bootimg_dir, dirname): 33 def _get_bootimg_dir(cls, bootimg_dir, dirname):
@@ -122,7 +122,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
122 syslinux_conf += "DEFAULT boot\n" 122 syslinux_conf += "DEFAULT boot\n"
123 syslinux_conf += "LABEL boot\n" 123 syslinux_conf += "LABEL boot\n"
124 124
125 kernel = "/vmlinuz" 125 kernel = "/" + get_bitbake_var("KERNEL_IMAGETYPE")
126 syslinux_conf += "KERNEL " + kernel + "\n" 126 syslinux_conf += "KERNEL " + kernel + "\n"
127 127
128 syslinux_conf += "APPEND label=boot root=%s %s\n" % \ 128 syslinux_conf += "APPEND label=boot root=%s %s\n" % \
@@ -155,8 +155,8 @@ class BootimgPcbiosPlugin(SourcePlugin):
155 kernel = "%s-%s.bin" % \ 155 kernel = "%s-%s.bin" % \
156 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 156 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
157 157
158 cmds = ("install -m 0644 %s/%s %s/vmlinuz" % 158 cmds = ("install -m 0644 %s/%s %s/%s" %
159 (staging_kernel_dir, kernel, hdddir), 159 (staging_kernel_dir, kernel, hdddir, get_bitbake_var("KERNEL_IMAGETYPE")),
160 "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" % 160 "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" %
161 (bootimg_dir, hdddir), 161 (bootimg_dir, hdddir),
162 "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" % 162 "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" %
@@ -186,8 +186,10 @@ class BootimgPcbiosPlugin(SourcePlugin):
186 # dosfs image, created by mkdosfs 186 # dosfs image, created by mkdosfs
187 bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno) 187 bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno)
188 188
189 dosfs_cmd = "mkdosfs -n boot -i %s -S 512 -C %s %d" % \ 189 label = part.label if part.label else "boot"
190 (part.fsuuid, bootimg, blocks) 190
191 dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
192 (label, part.fsuuid, bootimg, blocks)
191 exec_native_cmd(dosfs_cmd, native_sysroot) 193 exec_native_cmd(dosfs_cmd, native_sysroot)
192 194
193 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) 195 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
diff --git a/scripts/lib/wic/plugins/source/empty.py b/scripts/lib/wic/plugins/source/empty.py
index 041617d648..4178912377 100644
--- a/scripts/lib/wic/plugins/source/empty.py
+++ b/scripts/lib/wic/plugins/source/empty.py
@@ -1,4 +1,6 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: MIT 4# SPDX-License-Identifier: MIT
3# 5#
4 6
@@ -7,9 +9,19 @@
7# To use it you must pass "empty" as argument for the "--source" parameter in 9# To use it you must pass "empty" as argument for the "--source" parameter in
8# the wks file. For example: 10# the wks file. For example:
9# part foo --source empty --ondisk sda --size="1024" --align 1024 11# part foo --source empty --ondisk sda --size="1024" --align 1024
12#
13# The plugin supports writing zeros to the start of the
14# partition. This is useful to overwrite old content like
15# filesystem signatures which may be re-recognized otherwise.
16# This feature can be enabled with
17# '--sourceparams="[fill|size=<N>[S|s|K|k|M|G]][,][bs=<N>[S|s|K|k|M|G]]"'
18# Conflicting or missing options throw errors.
10 19
11import logging 20import logging
21import os
12 22
23from wic import WicError
24from wic.ksparser import sizetype
13from wic.pluginbase import SourcePlugin 25from wic.pluginbase import SourcePlugin
14 26
15logger = logging.getLogger('wic') 27logger = logging.getLogger('wic')
@@ -17,6 +29,16 @@ logger = logging.getLogger('wic')
17class EmptyPartitionPlugin(SourcePlugin): 29class EmptyPartitionPlugin(SourcePlugin):
18 """ 30 """
19 Populate unformatted empty partition. 31 Populate unformatted empty partition.
32
33 The following sourceparams are supported:
34 - fill
35 Fill the entire partition with zeros. Requires '--fixed-size' option
36 to be set.
37 - size=<N>[S|s|K|k|M|G]
38 Set the first N bytes of the partition to zero. Default unit is 'K'.
39 - bs=<N>[S|s|K|k|M|G]
40 Write at most N bytes at a time during source file creation.
41 Defaults to '1M'. Default unit is 'K'.
20 """ 42 """
21 43
22 name = 'empty' 44 name = 'empty'
@@ -29,4 +51,39 @@ class EmptyPartitionPlugin(SourcePlugin):
29 Called to do the actual content population for a partition i.e. it 51 Called to do the actual content population for a partition i.e. it
30 'prepares' the partition to be incorporated into the image. 52 'prepares' the partition to be incorporated into the image.
31 """ 53 """
32 return 54 get_byte_count = sizetype('K', True)
55 size = 0
56
57 if 'fill' in source_params and 'size' in source_params:
58 raise WicError("Conflicting source parameters 'fill' and 'size' specified, exiting.")
59
60 # Set the size of the zeros to be written to the partition
61 if 'fill' in source_params:
62 if part.fixed_size == 0:
63 raise WicError("Source parameter 'fill' only works with the '--fixed-size' option, exiting.")
64 size = get_byte_count(part.fixed_size)
65 elif 'size' in source_params:
66 size = get_byte_count(source_params['size'])
67
68 if size == 0:
69 # Nothing to do, create empty partition
70 return
71
72 if 'bs' in source_params:
73 bs = get_byte_count(source_params['bs'])
74 else:
75 bs = get_byte_count('1M')
76
77 # Create a binary file of the requested size filled with zeros
78 source_file = os.path.join(cr_workdir, 'empty-plugin-zeros%s.bin' % part.lineno)
79 if not os.path.exists(os.path.dirname(source_file)):
80 os.makedirs(os.path.dirname(source_file))
81
82 quotient, remainder = divmod(size, bs)
83 with open(source_file, 'wb') as file:
84 for _ in range(quotient):
85 file.write(bytearray(bs))
86 file.write(bytearray(remainder))
87
88 part.size = (size + 1024 - 1) // 1024 # size in KB rounded up
89 part.source_file = source_file
diff --git a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/scripts/lib/wic/plugins/source/isoimage_isohybrid.py
index afc9ea0f8f..5d42eb5d3e 100644
--- a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
+++ b/scripts/lib/wic/plugins/source/isoimage_isohybrid.py
@@ -1,8 +1,10 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4# DESCRIPTION 6# DESCRIPTION
5# This implements the 'isoimage-isohybrid' source plugin class for 'wic' 7# This implements the 'isoimage_isohybrid' source plugin class for 'wic'
6# 8#
7# AUTHORS 9# AUTHORS
8# Mihaly Varga <mihaly.varga (at] ni.com> 10# Mihaly Varga <mihaly.varga (at] ni.com>
@@ -33,7 +35,7 @@ class IsoImagePlugin(SourcePlugin):
33 bootloader files. 35 bootloader files.
34 36
35 Example kickstart file: 37 Example kickstart file:
36 part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi, \\ 38 part /boot --source isoimage_isohybrid --sourceparams="loader=grub-efi, \\
37 image_name= IsoImage" --ondisk cd --label LIVECD 39 image_name= IsoImage" --ondisk cd --label LIVECD
38 bootloader --timeout=10 --append=" " 40 bootloader --timeout=10 --append=" "
39 41
@@ -43,7 +45,7 @@ class IsoImagePlugin(SourcePlugin):
43 extension added by direct imeger plugin) and a file named IsoImage-cd.iso 45 extension added by direct imeger plugin) and a file named IsoImage-cd.iso
44 """ 46 """
45 47
46 name = 'isoimage-isohybrid' 48 name = 'isoimage_isohybrid'
47 49
48 @classmethod 50 @classmethod
49 def do_configure_syslinux(cls, creator, cr_workdir): 51 def do_configure_syslinux(cls, creator, cr_workdir):
@@ -338,10 +340,10 @@ class IsoImagePlugin(SourcePlugin):
338 cls.do_configure_grubefi(part, creator, target_dir) 340 cls.do_configure_grubefi(part, creator, target_dir)
339 341
340 else: 342 else:
341 raise WicError("unrecognized bootimg-efi loader: %s" % 343 raise WicError("unrecognized bootimg_efi loader: %s" %
342 source_params['loader']) 344 source_params['loader'])
343 except KeyError: 345 except KeyError:
344 raise WicError("bootimg-efi requires a loader, none specified") 346 raise WicError("bootimg_efi requires a loader, none specified")
345 347
346 # Create efi.img that contains bootloader files for EFI booting 348 # Create efi.img that contains bootloader files for EFI booting
347 # if ISODIR didn't exist or didn't contains it 349 # if ISODIR didn't exist or didn't contains it
diff --git a/scripts/lib/wic/plugins/source/rawcopy.py b/scripts/lib/wic/plugins/source/rawcopy.py
index 3c4997d8ba..21903c2f23 100644
--- a/scripts/lib/wic/plugins/source/rawcopy.py
+++ b/scripts/lib/wic/plugins/source/rawcopy.py
@@ -1,9 +1,13 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4 6
5import logging 7import logging
6import os 8import os
9import signal
10import subprocess
7 11
8from wic import WicError 12from wic import WicError
9from wic.pluginbase import SourcePlugin 13from wic.pluginbase import SourcePlugin
@@ -21,6 +25,10 @@ class RawCopyPlugin(SourcePlugin):
21 25
22 @staticmethod 26 @staticmethod
23 def do_image_label(fstype, dst, label): 27 def do_image_label(fstype, dst, label):
28 # don't create label when fstype is none
29 if fstype == 'none':
30 return
31
24 if fstype.startswith('ext'): 32 if fstype.startswith('ext'):
25 cmd = 'tune2fs -L %s %s' % (label, dst) 33 cmd = 'tune2fs -L %s %s' % (label, dst)
26 elif fstype in ('msdos', 'vfat'): 34 elif fstype in ('msdos', 'vfat'):
@@ -29,15 +37,35 @@ class RawCopyPlugin(SourcePlugin):
29 cmd = 'btrfs filesystem label %s %s' % (dst, label) 37 cmd = 'btrfs filesystem label %s %s' % (dst, label)
30 elif fstype == 'swap': 38 elif fstype == 'swap':
31 cmd = 'mkswap -L %s %s' % (label, dst) 39 cmd = 'mkswap -L %s %s' % (label, dst)
32 elif fstype == 'squashfs': 40 elif fstype in ('squashfs', 'erofs'):
33 raise WicError("It's not possible to update a squashfs " 41 raise WicError("It's not possible to update a %s "
34 "filesystem label '%s'" % (label)) 42 "filesystem label '%s'" % (fstype, label))
35 else: 43 else:
36 raise WicError("Cannot update filesystem label: " 44 raise WicError("Cannot update filesystem label: "
37 "Unknown fstype: '%s'" % (fstype)) 45 "Unknown fstype: '%s'" % (fstype))
38 46
39 exec_cmd(cmd) 47 exec_cmd(cmd)
40 48
49 @staticmethod
50 def do_image_uncompression(src, dst, workdir):
51 def subprocess_setup():
52 # Python installs a SIGPIPE handler by default. This is usually not what
53 # non-Python subprocesses expect.
54 # SIGPIPE errors are known issues with gzip/bash
55 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
56
57 extension = os.path.splitext(src)[1]
58 decompressor = {
59 ".bz2": "bzip2",
60 ".gz": "gzip",
61 ".xz": "xz",
62 ".zst": "zstd -f",
63 }.get(extension)
64 if not decompressor:
65 raise WicError("Not supported compressor filename extension: %s" % extension)
66 cmd = "%s -dc %s > %s" % (decompressor, src, dst)
67 subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=workdir)
68
41 @classmethod 69 @classmethod
42 def do_prepare_partition(cls, part, source_params, cr, cr_workdir, 70 def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
43 oe_builddir, bootimg_dir, kernel_dir, 71 oe_builddir, bootimg_dir, kernel_dir,
@@ -56,7 +84,13 @@ class RawCopyPlugin(SourcePlugin):
56 if 'file' not in source_params: 84 if 'file' not in source_params:
57 raise WicError("No file specified") 85 raise WicError("No file specified")
58 86
59 src = os.path.join(kernel_dir, source_params['file']) 87 if 'unpack' in source_params:
88 img = os.path.join(kernel_dir, source_params['file'])
89 src = os.path.join(cr_workdir, os.path.splitext(source_params['file'])[0])
90 RawCopyPlugin.do_image_uncompression(img, src, cr_workdir)
91 else:
92 src = os.path.join(kernel_dir, source_params['file'])
93
60 dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno)) 94 dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno))
61 95
62 if not os.path.exists(os.path.dirname(dst)): 96 if not os.path.exists(os.path.dirname(dst)):
diff --git a/scripts/lib/wic/plugins/source/rootfs.py b/scripts/lib/wic/plugins/source/rootfs.py
index 96d940a91d..06fce06bb1 100644
--- a/scripts/lib/wic/plugins/source/rootfs.py
+++ b/scripts/lib/wic/plugins/source/rootfs.py
@@ -35,22 +35,22 @@ class RootfsPlugin(SourcePlugin):
35 @staticmethod 35 @staticmethod
36 def __validate_path(cmd, rootfs_dir, path): 36 def __validate_path(cmd, rootfs_dir, path):
37 if os.path.isabs(path): 37 if os.path.isabs(path):
38 logger.error("%s: Must be relative: %s" % (cmd, orig_path)) 38 logger.error("%s: Must be relative: %s" % (cmd, path))
39 sys.exit(1) 39 sys.exit(1)
40 40
41 # Disallow climbing outside of parent directory using '..', 41 # Disallow climbing outside of parent directory using '..',
42 # because doing so could be quite disastrous (we will delete the 42 # because doing so could be quite disastrous (we will delete the
43 # directory, or modify a directory outside OpenEmbedded). 43 # directory, or modify a directory outside OpenEmbedded).
44 full_path = os.path.realpath(os.path.join(rootfs_dir, path)) 44 full_path = os.path.abspath(os.path.join(rootfs_dir, path))
45 if not full_path.startswith(os.path.realpath(rootfs_dir)): 45 if not full_path.startswith(os.path.realpath(rootfs_dir)):
46 logger.error("%s: Must point inside the rootfs:" % (cmd, path)) 46 logger.error("%s: Must point inside the rootfs: %s" % (cmd, path))
47 sys.exit(1) 47 sys.exit(1)
48 48
49 return full_path 49 return full_path
50 50
51 @staticmethod 51 @staticmethod
52 def __get_rootfs_dir(rootfs_dir): 52 def __get_rootfs_dir(rootfs_dir):
53 if os.path.isdir(rootfs_dir): 53 if rootfs_dir and os.path.isdir(rootfs_dir):
54 return os.path.realpath(rootfs_dir) 54 return os.path.realpath(rootfs_dir)
55 55
56 image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir) 56 image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir)
@@ -97,6 +97,9 @@ class RootfsPlugin(SourcePlugin):
97 part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab")) 97 part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab"))
98 pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo") 98 pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo")
99 if not os.path.lexists(pseudo_dir): 99 if not os.path.lexists(pseudo_dir):
100 pseudo_dir = os.path.join(cls.__get_rootfs_dir(None), '../pseudo')
101
102 if not os.path.lexists(pseudo_dir):
100 logger.warn("%s folder does not exist. " 103 logger.warn("%s folder does not exist. "
101 "Usernames and permissions will be invalid " % pseudo_dir) 104 "Usernames and permissions will be invalid " % pseudo_dir)
102 pseudo_dir = None 105 pseudo_dir = None
@@ -218,10 +221,10 @@ class RootfsPlugin(SourcePlugin):
218 # Update part.has_fstab here as fstab may have been added or 221 # Update part.has_fstab here as fstab may have been added or
219 # removed by the above modifications. 222 # removed by the above modifications.
220 part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab")) 223 part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab"))
221 if part.update_fstab_in_rootfs and part.has_fstab: 224 if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update:
222 fstab_path = os.path.join(new_rootfs, "etc/fstab") 225 fstab_path = os.path.join(new_rootfs, "etc/fstab")
223 # Assume that fstab should always be owned by root with fixed permissions 226 # Assume that fstab should always be owned by root with fixed permissions
224 install_cmd = "install -m 0644 %s %s" % (part.updated_fstab_path, fstab_path) 227 install_cmd = "install -m 0644 -p %s %s" % (part.updated_fstab_path, fstab_path)
225 if new_pseudo: 228 if new_pseudo:
226 pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 229 pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
227 else: 230 else: