blob: 495518fac81a10ecf2ee3bbe707b1e32245d655c [file] [log] [blame]
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001#
2# Copyright (c) 2013, Intel Corporation.
Brad Bishop6e60e8b2018-02-01 10:27:11 -05003#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
Brad Bishop6e60e8b2018-02-01 10:27:11 -05005#
6# DESCRIPTION
7# This implements the 'direct' imager plugin class for 'wic'
8#
9# AUTHORS
10# Tom Zanussi <tom.zanussi (at] linux.intel.com>
11#
12
13import logging
14import os
Brad Bishopd7bf8c12018-02-25 22:55:05 -050015import random
Brad Bishop6e60e8b2018-02-01 10:27:11 -050016import shutil
17import tempfile
18import uuid
19
20from time import strftime
21
Brad Bishopd7bf8c12018-02-25 22:55:05 -050022from oe.path import copyhardlinktree
23
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024from wic import WicError
25from wic.filemap import sparse_copy
26from wic.ksparser import KickStart, KickStartError
27from wic.pluginbase import PluginMgr, ImagerPlugin
Brad Bishopd7bf8c12018-02-25 22:55:05 -050028from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd
Brad Bishop6e60e8b2018-02-01 10:27:11 -050029
30logger = logging.getLogger('wic')
31
32class DirectPlugin(ImagerPlugin):
33 """
34 Install a system into a file containing a partitioned disk image.
35
36 An image file is formatted with a partition table, each partition
37 created from a rootfs or other OpenEmbedded build artifact and dd'ed
38 into the virtual disk. The disk image can subsequently be dd'ed onto
39 media and used on actual hardware.
40 """
41 name = 'direct'
42
43 def __init__(self, wks_file, rootfs_dir, bootimg_dir, kernel_dir,
44 native_sysroot, oe_builddir, options):
45 try:
46 self.ks = KickStart(wks_file)
47 except KickStartError as err:
48 raise WicError(str(err))
49
50 # parse possible 'rootfs=name' items
51 self.rootfs_dir = dict(rdir.split('=') for rdir in rootfs_dir.split(' '))
52 self.bootimg_dir = bootimg_dir
53 self.kernel_dir = kernel_dir
54 self.native_sysroot = native_sysroot
55 self.oe_builddir = oe_builddir
56
Andrew Geisslerd1e89492021-02-12 15:35:20 -060057 self.debug = options.debug
Brad Bishop6e60e8b2018-02-01 10:27:11 -050058 self.outdir = options.outdir
59 self.compressor = options.compressor
60 self.bmap = options.bmap
Brad Bishopd7bf8c12018-02-25 22:55:05 -050061 self.no_fstab_update = options.no_fstab_update
Andrew Geisslerd1e89492021-02-12 15:35:20 -060062 self.updated_fstab_path = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063
64 self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0],
65 strftime("%Y%m%d%H%M"))
Andrew Geisslerd1e89492021-02-12 15:35:20 -060066 self.workdir = self.setup_workdir(options.workdir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050067 self._image = None
68 self.ptable_format = self.ks.bootloader.ptable
69 self.parts = self.ks.partitions
70
71 # as a convenience, set source to the boot partition source
72 # instead of forcing it to be set via bootloader --source
73 for part in self.parts:
74 if not self.ks.bootloader.source and part.mountpoint == "/boot":
75 self.ks.bootloader.source = part.source
76 break
77
78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
79 self._image = PartitionedImage(image_path, self.ptable_format,
Andrew Geissler5199d832021-09-24 16:47:35 -050080 self.parts, self.native_sysroot,
81 options.extra_space)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050082
Andrew Geisslerd1e89492021-02-12 15:35:20 -060083 def setup_workdir(self, workdir):
84 if workdir:
85 if os.path.exists(workdir):
86 raise WicError("Internal workdir '%s' specified in wic arguments already exists!" % (workdir))
87
88 os.makedirs(workdir)
89 return workdir
90 else:
91 return tempfile.mkdtemp(dir=self.outdir, prefix='tmp.wic.')
92
Brad Bishop6e60e8b2018-02-01 10:27:11 -050093 def do_create(self):
94 """
95 Plugin entry point.
96 """
97 try:
98 self.create()
99 self.assemble()
100 self.finalize()
101 self.print_info()
102 finally:
103 self.cleanup()
104
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600105 def update_fstab(self, image_rootfs):
106 """Assume partition order same as in wks"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500107 if not image_rootfs:
108 return
109
110 fstab_path = image_rootfs + "/etc/fstab"
111 if not os.path.isfile(fstab_path):
112 return
113
114 with open(fstab_path) as fstab:
115 fstab_lines = fstab.readlines()
116
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500117 updated = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600118 for part in self.parts:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500119 if not part.realnum or not part.mountpoint \
Patrick Williams93c203f2021-10-06 16:15:23 -0500120 or part.mountpoint == "/" or not part.mountpoint.startswith('/'):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500121 continue
122
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500123 if part.use_uuid:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400124 if part.fsuuid:
125 # FAT UUID is different from others
126 if len(part.fsuuid) == 10:
127 device_name = "UUID=%s-%s" % \
128 (part.fsuuid[2:6], part.fsuuid[6:])
129 else:
130 device_name = "UUID=%s" % part.fsuuid
131 else:
132 device_name = "PARTUUID=%s" % part.uuid
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800133 elif part.use_label:
134 device_name = "LABEL=%s" % part.label
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500135 else:
136 # mmc device partitions are named mmcblk0p1, mmcblk0p2..
137 prefix = 'p' if part.disk.startswith('mmcblk') else ''
138 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500139
140 opts = part.fsopts if part.fsopts else "defaults"
141 line = "\t".join([device_name, part.mountpoint, part.fstype,
142 opts, "0", "0"]) + "\n"
143
144 fstab_lines.append(line)
145 updated = True
146
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600147 if updated:
148 self.updated_fstab_path = os.path.join(self.workdir, "fstab")
149 with open(self.updated_fstab_path, "w") as f:
150 f.writelines(fstab_lines)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151
152 def _full_path(self, path, name, extention):
153 """ Construct full file path to a file we generate. """
154 return os.path.join(path, "%s-%s.%s" % (self.name, name, extention))
155
156 #
157 # Actual implemention
158 #
159 def create(self):
160 """
161 For 'wic', we already have our build artifacts - we just create
162 filesystems from the artifacts directly and combine them into
163 a partitioned image.
164 """
Brad Bishop96ff1982019-08-19 13:50:42 -0400165 if not self.no_fstab_update:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600166 self.update_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500167
168 for part in self.parts:
169 # get rootfs size from bitbake variable if it's not set in .ks file
170 if not part.size:
171 # and if rootfs name is specified for the partition
172 image_name = self.rootfs_dir.get(part.rootfs_dir)
173 if image_name and os.path.sep not in image_name:
174 # Bitbake variable ROOTFS_SIZE is calculated in
175 # Image._get_rootfs_size method from meta/lib/oe/image.py
176 # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
177 # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
178 rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
179 if rsize_bb:
180 part.size = int(round(float(rsize_bb)))
181
182 self._image.prepare(self)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500183 self._image.layout_partitions()
184 self._image.create()
185
186 def assemble(self):
187 """
188 Assemble partitions into disk image
189 """
190 self._image.assemble()
191
192 def finalize(self):
193 """
194 Finalize the disk image.
195
196 For example, prepare the image to be bootable by e.g.
197 creating and installing a bootloader configuration.
198 """
199 source_plugin = self.ks.bootloader.source
200 disk_name = self.parts[0].disk
201 if source_plugin:
202 plugin = PluginMgr.get_plugins('source')[source_plugin]
203 plugin.do_install_disk(self._image, disk_name, self, self.workdir,
204 self.oe_builddir, self.bootimg_dir,
205 self.kernel_dir, self.native_sysroot)
206
207 full_path = self._image.path
208 # Generate .bmap
209 if self.bmap:
210 logger.debug("Generating bmap file for %s", disk_name)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500211 python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3')
212 bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool')
213 exec_native_cmd("%s %s create %s -o %s.bmap" % \
214 (python, bmaptool, full_path, full_path), self.native_sysroot)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500215 # Compress the image
216 if self.compressor:
217 logger.debug("Compressing disk %s with %s", disk_name, self.compressor)
218 exec_cmd("%s %s" % (self.compressor, full_path))
219
220 def print_info(self):
221 """
222 Print the image(s) and artifacts used, for the user.
223 """
224 msg = "The new image(s) can be found here:\n"
225
226 extension = "direct" + {"gzip": ".gz",
227 "bzip2": ".bz2",
228 "xz": ".xz",
229 None: ""}.get(self.compressor)
230 full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
231 msg += ' %s\n\n' % full_path
232
233 msg += 'The following build artifacts were used to create the image(s):\n'
234 for part in self.parts:
235 if part.rootfs_dir is None:
236 continue
237 if part.mountpoint == '/':
238 suffix = ':'
239 else:
240 suffix = '["%s"]:' % (part.mountpoint or part.label)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400241 rootdir = part.rootfs_dir
Brad Bishop316dfdd2018-06-25 12:45:53 -0400242 msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500243
244 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
245 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
246 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
247
248 logger.info(msg)
249
250 @property
251 def rootdev(self):
252 """
253 Get root device name to use as a 'root' parameter
254 in kernel command line.
255
256 Assume partition order same as in wks
257 """
258 for part in self.parts:
259 if part.mountpoint == "/":
260 if part.uuid:
261 return "PARTUUID=%s" % part.uuid
262 else:
263 suffix = 'p' if part.disk.startswith('mmcblk') else ''
264 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
265
266 def cleanup(self):
267 if self._image:
268 self._image.cleanup()
269
270 # Move results to the output dir
271 if not os.path.exists(self.outdir):
272 os.makedirs(self.outdir)
273
274 for fname in os.listdir(self.workdir):
275 path = os.path.join(self.workdir, fname)
276 if os.path.isfile(path):
277 shutil.move(path, os.path.join(self.outdir, fname))
278
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600279 # remove work directory when it is not in debugging mode
280 if not self.debug:
281 shutil.rmtree(self.workdir, ignore_errors=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500282
283# Overhead of the MBR partitioning scheme (just one sector)
284MBR_OVERHEAD = 1
285
286# Overhead of the GPT partitioning scheme
287GPT_OVERHEAD = 34
288
289# Size of a sector in bytes
290SECTOR_SIZE = 512
291
292class PartitionedImage():
293 """
294 Partitioned image in a file.
295 """
296
Andrew Geissler5199d832021-09-24 16:47:35 -0500297 def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500298 self.path = path # Path to the image file
299 self.numpart = 0 # Number of allocated partitions
300 self.realpart = 0 # Number of partitions in the partition table
Brad Bishop08902b02019-08-20 09:16:51 -0400301 self.primary_part_num = 0 # Number of primary partitions (msdos)
302 self.extendedpart = 0 # Create extended partition before this logical partition (msdos)
303 self.extended_size_sec = 0 # Size of exteded partition (msdos)
304 self.logical_part_cnt = 0 # Number of total logical paritions (msdos)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500305 self.offset = 0 # Offset of next partition (in sectors)
306 self.min_size = 0 # Minimum required disk size to fit
307 # all partitions (in bytes)
308 self.ptable_format = ptable_format # Partition table format
309 # Disk system identifier
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500310 self.identifier = random.SystemRandom().randint(1, 0xffffffff)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500311
312 self.partitions = partitions
313 self.partimages = []
314 # Size of a sector used in calculations
315 self.sector_size = SECTOR_SIZE
316 self.native_sysroot = native_sysroot
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400317 num_real_partitions = len([p for p in self.partitions if not p.no_table])
Andrew Geissler5199d832021-09-24 16:47:35 -0500318 self.extra_space = extra_space
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500319
320 # calculate the real partition number, accounting for partitions not
321 # in the partition table and logical partitions
322 realnum = 0
323 for part in self.partitions:
324 if part.no_table:
325 part.realnum = 0
326 else:
327 realnum += 1
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400328 if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500329 part.realnum = realnum + 1
330 continue
331 part.realnum = realnum
332
Brad Bishop316dfdd2018-06-25 12:45:53 -0400333 # generate parition and filesystem UUIDs
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500334 for part in self.partitions:
335 if not part.uuid and part.use_uuid:
336 if self.ptable_format == 'gpt':
337 part.uuid = str(uuid.uuid4())
338 else: # msdos partition table
339 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400340 if not part.fsuuid:
341 if part.fstype == 'vfat' or part.fstype == 'msdos':
342 part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
343 else:
344 part.fsuuid = str(uuid.uuid4())
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600345 else:
346 #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
347 if part.fstype == 'vfat' or part.fstype == 'msdos':
348 if part.fsuuid.upper().startswith("0X"):
349 part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
350 else:
351 part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352
353 def prepare(self, imager):
354 """Prepare an image. Call prepare method of all image partitions."""
355 for part in self.partitions:
356 # need to create the filesystems in order to get their
357 # sizes before we can add them and do the layout.
358 part.prepare(imager, imager.workdir, imager.oe_builddir,
359 imager.rootfs_dir, imager.bootimg_dir,
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600360 imager.kernel_dir, imager.native_sysroot,
361 imager.updated_fstab_path)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500362
363 # Converting kB to sectors for parted
364 part.size_sec = part.disk_size * 1024 // self.sector_size
365
366 def layout_partitions(self):
367 """ Layout the partitions, meaning calculate the position of every
368 partition on the disk. The 'ptable_format' parameter defines the
369 partition table format and may be "msdos". """
370
371 logger.debug("Assigning %s partitions to disks", self.ptable_format)
372
373 # The number of primary and logical partitions. Extended partition and
374 # partitions not listed in the table are not included.
375 num_real_partitions = len([p for p in self.partitions if not p.no_table])
376
377 # Go through partitions in the order they are added in .ks file
378 for num in range(len(self.partitions)):
379 part = self.partitions[num]
380
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500381 if self.ptable_format == 'msdos' and part.part_name:
382 raise WicError("setting custom partition name is not " \
383 "implemented for msdos partitions")
384
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500385 if self.ptable_format == 'msdos' and part.part_type:
386 # The --part-type can also be implemented for MBR partitions,
387 # in which case it would map to the 1-byte "partition type"
388 # filed at offset 3 of the partition entry.
389 raise WicError("setting custom partition type is not " \
390 "implemented for msdos partitions")
391
392 # Get the disk where the partition is located
393 self.numpart += 1
394 if not part.no_table:
395 self.realpart += 1
396
397 if self.numpart == 1:
398 if self.ptable_format == "msdos":
399 overhead = MBR_OVERHEAD
400 elif self.ptable_format == "gpt":
401 overhead = GPT_OVERHEAD
402
403 # Skip one sector required for the partitioning scheme overhead
404 self.offset += overhead
405
Brad Bishop08902b02019-08-20 09:16:51 -0400406 if self.ptable_format == "msdos":
407 if self.primary_part_num > 3 or \
408 (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
409 part.type = 'logical'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 # Reserve a sector for EBR for every logical partition
411 # before alignment is performed.
Brad Bishop08902b02019-08-20 09:16:51 -0400412 if part.type == 'logical':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500413 self.offset += 2
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500414
Brad Bishop08902b02019-08-20 09:16:51 -0400415 align_sectors = 0
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500416 if part.align:
417 # If not first partition and we do have alignment set we need
418 # to align the partition.
419 # FIXME: This leaves a empty spaces to the disk. To fill the
420 # gaps we could enlargea the previous partition?
421
422 # Calc how much the alignment is off.
423 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
424
425 if align_sectors:
426 # If partition is not aligned as required, we need
427 # to move forward to the next alignment point
428 align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
429
430 logger.debug("Realignment for %s%s with %s sectors, original"
431 " offset %s, target alignment is %sK.",
432 part.disk, self.numpart, align_sectors,
433 self.offset, part.align)
434
435 # increase the offset so we actually start the partition on right alignment
436 self.offset += align_sectors
437
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500438 if part.offset is not None:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500439 offset = part.offset // self.sector_size
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500440
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500441 if offset * self.sector_size != part.offset:
442 raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500443
444 delta = offset - self.offset
445 if delta < 0:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500446 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))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500447
448 logger.debug("Skipping %d sectors to place %s%s at offset %dK",
449 delta, part.disk, self.numpart, part.offset)
450
451 self.offset = offset
452
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500453 part.start = self.offset
454 self.offset += part.size_sec
455
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500456 if not part.no_table:
457 part.num = self.realpart
458 else:
459 part.num = 0
460
Brad Bishop08902b02019-08-20 09:16:51 -0400461 if self.ptable_format == "msdos" and not part.no_table:
462 if part.type == 'logical':
463 self.logical_part_cnt += 1
464 part.num = self.logical_part_cnt + 4
465 if self.extendedpart == 0:
466 # Create extended partition as a primary partition
467 self.primary_part_num += 1
468 self.extendedpart = part.num
469 else:
470 self.extended_size_sec += align_sectors
Andrew Geissler82c905d2020-04-13 13:39:40 -0500471 self.extended_size_sec += part.size_sec + 2
Brad Bishop08902b02019-08-20 09:16:51 -0400472 else:
473 self.primary_part_num += 1
474 part.num = self.primary_part_num
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500475
476 logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
477 "sectors (%d bytes).", part.mountpoint, part.disk,
478 part.num, part.start, self.offset - 1, part.size_sec,
479 part.size_sec * self.sector_size)
480
481 # Once all the partitions have been layed out, we can calculate the
482 # minumim disk size
483 self.min_size = self.offset
484 if self.ptable_format == "gpt":
485 self.min_size += GPT_OVERHEAD
486
487 self.min_size *= self.sector_size
Andrew Geissler5199d832021-09-24 16:47:35 -0500488 self.min_size += self.extra_space
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500489
490 def _create_partition(self, device, parttype, fstype, start, size):
491 """ Create a partition on an image described by the 'device' object. """
492
493 # Start is included to the size so we need to substract one from the end.
494 end = start + size - 1
495 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
496 parttype, start, end, size)
497
498 cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
499 if fstype:
500 cmd += " %s" % fstype
501 cmd += " %d %d" % (start, end)
502
503 return exec_native_cmd(cmd, self.native_sysroot)
504
505 def create(self):
506 logger.debug("Creating sparse file %s", self.path)
507 with open(self.path, 'w') as sparse:
508 os.ftruncate(sparse.fileno(), self.min_size)
509
510 logger.debug("Initializing partition table for %s", self.path)
511 exec_native_cmd("parted -s %s mklabel %s" %
512 (self.path, self.ptable_format), self.native_sysroot)
513
514 logger.debug("Set disk identifier %x", self.identifier)
515 with open(self.path, 'r+b') as img:
516 img.seek(0x1B8)
517 img.write(self.identifier.to_bytes(4, 'little'))
518
519 logger.debug("Creating partitions")
520
521 for part in self.partitions:
522 if part.num == 0:
523 continue
524
Brad Bishop08902b02019-08-20 09:16:51 -0400525 if self.ptable_format == "msdos" and part.num == self.extendedpart:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500526 # Create an extended partition (note: extended
527 # partition is described in MBR and contains all
528 # logical partitions). The logical partitions save a
529 # sector for an EBR just before the start of a
530 # partition. The extended partition must start one
531 # sector before the start of the first logical
532 # partition. This way the first EBR is inside of the
533 # extended partition. Since the extended partitions
534 # starts a sector before the first logical partition,
535 # add a sector at the back, so that there is enough
536 # room for all logical partitions.
537 self._create_partition(self.path, "extended",
Andrew Geissler82c905d2020-04-13 13:39:40 -0500538 None, part.start - 2,
Brad Bishop08902b02019-08-20 09:16:51 -0400539 self.extended_size_sec)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540
541 if part.fstype == "swap":
542 parted_fs_type = "linux-swap"
543 elif part.fstype == "vfat":
544 parted_fs_type = "fat32"
545 elif part.fstype == "msdos":
546 parted_fs_type = "fat16"
547 if not part.system_id:
548 part.system_id = '0x6' # FAT16
549 else:
550 # Type for ext2/ext3/ext4/btrfs
551 parted_fs_type = "ext2"
552
553 # Boot ROM of OMAP boards require vfat boot partition to have an
554 # even number of sectors.
555 if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
556 and part.size_sec % 2:
557 logger.debug("Subtracting one sector from '%s' partition to "
558 "get even number of sectors for the partition",
559 part.mountpoint)
560 part.size_sec -= 1
561
562 self._create_partition(self.path, part.type,
563 parted_fs_type, part.start, part.size_sec)
564
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500565 if part.part_name:
566 logger.debug("partition %d: set name to %s",
567 part.num, part.part_name)
568 exec_native_cmd("sgdisk --change-name=%d:%s %s" % \
569 (part.num, part.part_name,
570 self.path), self.native_sysroot)
571
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500572 if part.part_type:
573 logger.debug("partition %d: set type UID to %s",
574 part.num, part.part_type)
575 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
576 (part.num, part.part_type,
577 self.path), self.native_sysroot)
578
579 if part.uuid and self.ptable_format == "gpt":
580 logger.debug("partition %d: set UUID to %s",
581 part.num, part.uuid)
582 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
583 (part.num, part.uuid, self.path),
584 self.native_sysroot)
585
586 if part.label and self.ptable_format == "gpt":
587 logger.debug("partition %d: set name to %s",
588 part.num, part.label)
589 exec_native_cmd("parted -s %s name %d %s" % \
590 (self.path, part.num, part.label),
591 self.native_sysroot)
592
593 if part.active:
594 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
595 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
596 flag_name, part.num, self.path)
597 exec_native_cmd("parted -s %s set %d %s on" % \
598 (self.path, part.num, flag_name),
599 self.native_sysroot)
600 if part.system_id:
601 exec_native_cmd("sfdisk --part-type %s %s %s" % \
602 (self.path, part.num, part.system_id),
603 self.native_sysroot)
604
605 def cleanup(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500606 pass
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500607
608 def assemble(self):
609 logger.debug("Installing partitions")
610
611 for part in self.partitions:
612 source = part.source_file
613 if source:
614 # install source_file contents into a partition
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500615 sparse_copy(source, self.path, seek=part.start * self.sector_size)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500616
617 logger.debug("Installed %s in partition %d, sectors %d-%d, "
618 "size %d sectors", source, part.num, part.start,
619 part.start + part.size_sec - 1, part.size_sec)
620
621 partimage = self.path + '.p%d' % part.num
Andrew Geisslerc926e172021-05-07 16:11:35 -0500622 bb.utils.rename(source, partimage)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500623 self.partimages.append(partimage)