diff options
Diffstat (limited to 'meta/lib/oe/fitimage.py')
| -rw-r--r-- | meta/lib/oe/fitimage.py | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py new file mode 100644 index 0000000000..f303799155 --- /dev/null +++ b/meta/lib/oe/fitimage.py | |||
| @@ -0,0 +1,547 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # This file contains common functions for the fitimage generation | ||
| 7 | |||
| 8 | import os | ||
| 9 | import shlex | ||
| 10 | import subprocess | ||
| 11 | import bb | ||
| 12 | |||
| 13 | from oeqa.utils.commands import runCmd | ||
| 14 | |||
| 15 | class ItsNode: | ||
| 16 | INDENT_SIZE = 8 | ||
| 17 | |||
| 18 | def __init__(self, name, parent_node, sub_nodes=None, properties=None): | ||
| 19 | self.name = name | ||
| 20 | self.parent_node = parent_node | ||
| 21 | |||
| 22 | self.sub_nodes = [] | ||
| 23 | if sub_nodes: | ||
| 24 | self.sub_nodes = sub_nodes | ||
| 25 | |||
| 26 | self.properties = {} | ||
| 27 | if properties: | ||
| 28 | self.properties = properties | ||
| 29 | |||
| 30 | if parent_node: | ||
| 31 | parent_node.add_sub_node(self) | ||
| 32 | |||
| 33 | def add_sub_node(self, sub_node): | ||
| 34 | self.sub_nodes.append(sub_node) | ||
| 35 | |||
| 36 | def add_property(self, key, value): | ||
| 37 | self.properties[key] = value | ||
| 38 | |||
| 39 | def emit(self, f, indent): | ||
| 40 | indent_str_name = " " * indent | ||
| 41 | indent_str_props = " " * (indent + self.INDENT_SIZE) | ||
| 42 | f.write("%s%s {\n" % (indent_str_name, self.name)) | ||
| 43 | for key, value in self.properties.items(): | ||
| 44 | bb.debug(1, "key: %s, value: %s" % (key, str(value))) | ||
| 45 | # Single integer: <0x12ab> | ||
| 46 | if isinstance(value, int): | ||
| 47 | f.write(indent_str_props + key + ' = <0x%x>;\n' % value) | ||
| 48 | # list of strings: "string1", "string2" or integers: <0x12ab 0x34cd> | ||
| 49 | elif isinstance(value, list): | ||
| 50 | if len(value) == 0: | ||
| 51 | f.write(indent_str_props + key + ' = "";\n') | ||
| 52 | elif isinstance(value[0], int): | ||
| 53 | list_entries = ' '.join('0x%x' % entry for entry in value) | ||
| 54 | f.write(indent_str_props + key + ' = <%s>;\n' % list_entries) | ||
| 55 | else: | ||
| 56 | list_entries = ', '.join('"%s"' % entry for entry in value) | ||
| 57 | f.write(indent_str_props + key + ' = %s;\n' % list_entries) | ||
| 58 | elif isinstance(value, str): | ||
| 59 | # path: /incbin/("path/to/file") | ||
| 60 | if key in ["data"] and value.startswith('/incbin/('): | ||
| 61 | f.write(indent_str_props + key + ' = %s;\n' % value) | ||
| 62 | # Integers which are already string formatted | ||
| 63 | elif value.startswith("<") and value.endswith(">"): | ||
| 64 | f.write(indent_str_props + key + ' = %s;\n' % value) | ||
| 65 | else: | ||
| 66 | f.write(indent_str_props + key + ' = "%s";\n' % value) | ||
| 67 | else: | ||
| 68 | bb.fatal("%s has unexpexted data type." % str(value)) | ||
| 69 | for sub_node in self.sub_nodes: | ||
| 70 | sub_node.emit(f, indent + self.INDENT_SIZE) | ||
| 71 | f.write(indent_str_name + '};\n') | ||
| 72 | |||
| 73 | class ItsNodeImages(ItsNode): | ||
| 74 | def __init__(self, parent_node): | ||
| 75 | super().__init__("images", parent_node) | ||
| 76 | |||
| 77 | class ItsNodeConfigurations(ItsNode): | ||
| 78 | def __init__(self, parent_node): | ||
| 79 | super().__init__("configurations", parent_node) | ||
| 80 | |||
| 81 | class ItsNodeHash(ItsNode): | ||
| 82 | def __init__(self, name, parent_node, algo, opt_props=None): | ||
| 83 | properties = { | ||
| 84 | "algo": algo | ||
| 85 | } | ||
| 86 | if opt_props: | ||
| 87 | properties.update(opt_props) | ||
| 88 | super().__init__(name, parent_node, None, properties) | ||
| 89 | |||
| 90 | class ItsImageSignature(ItsNode): | ||
| 91 | def __init__(self, name, parent_node, algo, keyname, opt_props=None): | ||
| 92 | properties = { | ||
| 93 | "algo": algo, | ||
| 94 | "key-name-hint": keyname | ||
| 95 | } | ||
| 96 | if opt_props: | ||
| 97 | properties.update(opt_props) | ||
| 98 | super().__init__(name, parent_node, None, properties) | ||
| 99 | |||
| 100 | class ItsNodeImage(ItsNode): | ||
| 101 | def __init__(self, name, parent_node, description, type, compression, sub_nodes=None, opt_props=None): | ||
| 102 | properties = { | ||
| 103 | "description": description, | ||
| 104 | "type": type, | ||
| 105 | "compression": compression, | ||
| 106 | } | ||
| 107 | if opt_props: | ||
| 108 | properties.update(opt_props) | ||
| 109 | super().__init__(name, parent_node, sub_nodes, properties) | ||
| 110 | |||
| 111 | class ItsNodeDtb(ItsNodeImage): | ||
| 112 | def __init__(self, name, parent_node, description, type, compression, | ||
| 113 | sub_nodes=None, opt_props=None, compatible=None): | ||
| 114 | super().__init__(name, parent_node, description, type, compression, sub_nodes, opt_props) | ||
| 115 | self.compatible = compatible | ||
| 116 | |||
| 117 | class ItsNodeDtbAlias(ItsNode): | ||
| 118 | """Additional Configuration Node for a DTB | ||
| 119 | |||
| 120 | Symlinks pointing to a DTB file are handled by an addtitional | ||
| 121 | configuration node referring to another DTB image node. | ||
| 122 | """ | ||
| 123 | def __init__(self, name, alias_name, compatible=None): | ||
| 124 | super().__init__(name, parent_node=None, sub_nodes=None, properties=None) | ||
| 125 | self.alias_name = alias_name | ||
| 126 | self.compatible = compatible | ||
| 127 | |||
| 128 | class ItsNodeConfigurationSignature(ItsNode): | ||
| 129 | def __init__(self, name, parent_node, algo, keyname, opt_props=None): | ||
| 130 | properties = { | ||
| 131 | "algo": algo, | ||
| 132 | "key-name-hint": keyname | ||
| 133 | } | ||
| 134 | if opt_props: | ||
| 135 | properties.update(opt_props) | ||
| 136 | super().__init__(name, parent_node, None, properties) | ||
| 137 | |||
| 138 | class ItsNodeConfiguration(ItsNode): | ||
| 139 | def __init__(self, name, parent_node, description, sub_nodes=None, opt_props=None): | ||
| 140 | properties = { | ||
| 141 | "description": description, | ||
| 142 | } | ||
| 143 | if opt_props: | ||
| 144 | properties.update(opt_props) | ||
| 145 | super().__init__(name, parent_node, sub_nodes, properties) | ||
| 146 | |||
| 147 | class ItsNodeRootKernel(ItsNode): | ||
| 148 | """Create FIT images for the kernel | ||
| 149 | |||
| 150 | Currently only a single kernel (no less or more) can be added to the FIT | ||
| 151 | image along with 0 or more device trees and 0 or 1 ramdisk. | ||
| 152 | |||
| 153 | If a device tree included in the FIT image, the default configuration is the | ||
| 154 | firt DTB. If there is no dtb present than the default configuation the kernel. | ||
| 155 | """ | ||
| 156 | def __init__(self, description, address_cells, host_prefix, arch, conf_prefix, | ||
| 157 | sign_enable=False, sign_keydir=None, | ||
| 158 | mkimage=None, mkimage_dtcopts=None, | ||
| 159 | mkimage_sign=None, mkimage_sign_args=None, | ||
| 160 | hash_algo=None, sign_algo=None, pad_algo=None, | ||
| 161 | sign_keyname_conf=None, | ||
| 162 | sign_individual=False, sign_keyname_img=None): | ||
| 163 | props = { | ||
| 164 | "description": description, | ||
| 165 | "#address-cells": f"<{address_cells}>" | ||
| 166 | } | ||
| 167 | super().__init__("/", None, None, props) | ||
| 168 | self.images = ItsNodeImages(self) | ||
| 169 | self.configurations = ItsNodeConfigurations(self) | ||
| 170 | |||
| 171 | self._host_prefix = host_prefix | ||
| 172 | self._arch = arch | ||
| 173 | self._conf_prefix = conf_prefix | ||
| 174 | |||
| 175 | # Signature related properties | ||
| 176 | self._sign_enable = sign_enable | ||
| 177 | self._sign_keydir = sign_keydir | ||
| 178 | self._mkimage = mkimage | ||
| 179 | self._mkimage_dtcopts = mkimage_dtcopts | ||
| 180 | self._mkimage_sign = mkimage_sign | ||
| 181 | self._mkimage_sign_args = mkimage_sign_args | ||
| 182 | self._hash_algo = hash_algo | ||
| 183 | self._sign_algo = sign_algo | ||
| 184 | self._pad_algo = pad_algo | ||
| 185 | self._sign_keyname_conf = sign_keyname_conf | ||
| 186 | self._sign_individual = sign_individual | ||
| 187 | self._sign_keyname_img = sign_keyname_img | ||
| 188 | self._sanitize_sign_config() | ||
| 189 | |||
| 190 | self._dtbs = [] | ||
| 191 | self._dtb_alias = [] | ||
| 192 | self._kernel = None | ||
| 193 | self._ramdisk = None | ||
| 194 | self._bootscr = None | ||
| 195 | self._setup = None | ||
| 196 | |||
| 197 | def _sanitize_sign_config(self): | ||
| 198 | if self._sign_enable: | ||
| 199 | if not self._hash_algo: | ||
| 200 | bb.fatal("FIT image signing is enabled but no hash algorithm is provided.") | ||
| 201 | if not self._sign_algo: | ||
| 202 | bb.fatal("FIT image signing is enabled but no signature algorithm is provided.") | ||
| 203 | if not self._pad_algo: | ||
| 204 | bb.fatal("FIT image signing is enabled but no padding algorithm is provided.") | ||
| 205 | if not self._sign_keyname_conf: | ||
| 206 | bb.fatal("FIT image signing is enabled but no configuration key name is provided.") | ||
| 207 | if self._sign_individual and not self._sign_keyname_img: | ||
| 208 | bb.fatal("FIT image signing is enabled for individual images but no image key name is provided.") | ||
| 209 | |||
| 210 | def write_its_file(self, itsfile): | ||
| 211 | with open(itsfile, 'w') as f: | ||
| 212 | f.write("/dts-v1/;\n\n") | ||
| 213 | self.emit(f, 0) | ||
| 214 | |||
| 215 | def its_add_node_image(self, image_id, description, image_type, compression, opt_props): | ||
| 216 | image_node = ItsNodeImage( | ||
| 217 | image_id, | ||
| 218 | self.images, | ||
| 219 | description, | ||
| 220 | image_type, | ||
| 221 | compression, | ||
| 222 | opt_props=opt_props | ||
| 223 | ) | ||
| 224 | if self._hash_algo: | ||
| 225 | ItsNodeHash( | ||
| 226 | "hash-1", | ||
| 227 | image_node, | ||
| 228 | self._hash_algo | ||
| 229 | ) | ||
| 230 | if self._sign_individual: | ||
| 231 | ItsImageSignature( | ||
| 232 | "signature-1", | ||
| 233 | image_node, | ||
| 234 | f"{self._hash_algo},{self._sign_algo}", | ||
| 235 | self._sign_keyname_img | ||
| 236 | ) | ||
| 237 | return image_node | ||
| 238 | |||
| 239 | def its_add_node_dtb(self, image_id, description, image_type, compression, opt_props, compatible): | ||
| 240 | dtb_node = ItsNodeDtb( | ||
| 241 | image_id, | ||
| 242 | self.images, | ||
| 243 | description, | ||
| 244 | image_type, | ||
| 245 | compression, | ||
| 246 | opt_props=opt_props, | ||
| 247 | compatible=compatible | ||
| 248 | ) | ||
| 249 | if self._hash_algo: | ||
| 250 | ItsNodeHash( | ||
| 251 | "hash-1", | ||
| 252 | dtb_node, | ||
| 253 | self._hash_algo | ||
| 254 | ) | ||
| 255 | if self._sign_individual: | ||
| 256 | ItsImageSignature( | ||
| 257 | "signature-1", | ||
| 258 | dtb_node, | ||
| 259 | f"{self._hash_algo},{self._sign_algo}", | ||
| 260 | self._sign_keyname_img | ||
| 261 | ) | ||
| 262 | return dtb_node | ||
| 263 | |||
| 264 | def fitimage_emit_section_kernel(self, kernel_id, kernel_path, compression, | ||
| 265 | load, entrypoint, mkimage_kernel_type, entrysymbol=None): | ||
| 266 | """Emit the fitImage ITS kernel section""" | ||
| 267 | if self._kernel: | ||
| 268 | bb.fatal("Kernel section already exists in the ITS file.") | ||
| 269 | if entrysymbol: | ||
| 270 | result = subprocess.run([self._host_prefix + "nm", "vmlinux"], capture_output=True, text=True) | ||
| 271 | for line in result.stdout.splitlines(): | ||
| 272 | parts = line.split() | ||
| 273 | if len(parts) == 3 and parts[2] == entrysymbol: | ||
| 274 | entrypoint = "<0x%s>" % parts[0] | ||
| 275 | break | ||
| 276 | kernel_node = self.its_add_node_image( | ||
| 277 | kernel_id, | ||
| 278 | "Linux kernel", | ||
| 279 | mkimage_kernel_type, | ||
| 280 | compression, | ||
| 281 | { | ||
| 282 | "data": '/incbin/("' + kernel_path + '")', | ||
| 283 | "arch": self._arch, | ||
| 284 | "os": "linux", | ||
| 285 | "load": f"<{load}>", | ||
| 286 | "entry": f"<{entrypoint}>" | ||
| 287 | } | ||
| 288 | ) | ||
| 289 | self._kernel = kernel_node | ||
| 290 | |||
| 291 | def fitimage_emit_section_dtb(self, dtb_id, dtb_path, dtb_loadaddress=None, | ||
| 292 | dtbo_loadaddress=None, add_compatible=False): | ||
| 293 | """Emit the fitImage ITS DTB section""" | ||
| 294 | load=None | ||
| 295 | dtb_ext = os.path.splitext(dtb_path)[1] | ||
| 296 | if dtb_ext == ".dtbo": | ||
| 297 | if dtbo_loadaddress: | ||
| 298 | load = dtbo_loadaddress | ||
| 299 | elif dtb_loadaddress: | ||
| 300 | load = dtb_loadaddress | ||
| 301 | |||
| 302 | opt_props = { | ||
| 303 | "data": '/incbin/("' + dtb_path + '")', | ||
| 304 | "arch": self._arch | ||
| 305 | } | ||
| 306 | if load: | ||
| 307 | opt_props["load"] = f"<{load}>" | ||
| 308 | |||
| 309 | # Preserve the DTB's compatible string to be added to the configuration node | ||
| 310 | compatible = None | ||
| 311 | if add_compatible: | ||
| 312 | compatible = get_compatible_from_dtb(dtb_path) | ||
| 313 | |||
| 314 | dtb_node = self.its_add_node_dtb( | ||
| 315 | "fdt-" + dtb_id, | ||
| 316 | "Flattened Device Tree blob", | ||
| 317 | "flat_dt", | ||
| 318 | "none", | ||
| 319 | opt_props, | ||
| 320 | compatible | ||
| 321 | ) | ||
| 322 | self._dtbs.append(dtb_node) | ||
| 323 | |||
| 324 | def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path, add_compatible=False): | ||
| 325 | """Add a configuration node referring to another DTB""" | ||
| 326 | # Preserve the DTB's compatible string to be added to the configuration node | ||
| 327 | compatible = None | ||
| 328 | if add_compatible: | ||
| 329 | compatible = get_compatible_from_dtb(dtb_path) | ||
| 330 | |||
| 331 | dtb_id = os.path.basename(dtb_path) | ||
| 332 | dtb_alias_node = ItsNodeDtbAlias("fdt-" + dtb_id, dtb_alias_id, compatible) | ||
| 333 | self._dtb_alias.append(dtb_alias_node) | ||
| 334 | bb.warn(f"compatible: {compatible}, dtb_alias_id: {dtb_alias_id}, dtb_id: {dtb_id}, dtb_path: {dtb_path}") | ||
| 335 | |||
| 336 | def fitimage_emit_section_boot_script(self, bootscr_id, bootscr_path): | ||
| 337 | """Emit the fitImage ITS u-boot script section""" | ||
| 338 | if self._bootscr: | ||
| 339 | bb.fatal("U-boot script section already exists in the ITS file.") | ||
| 340 | bootscr_node = self.its_add_node_image( | ||
| 341 | bootscr_id, | ||
| 342 | "U-boot script", | ||
| 343 | "script", | ||
| 344 | "none", | ||
| 345 | { | ||
| 346 | "data": '/incbin/("' + bootscr_path + '")', | ||
| 347 | "arch": self._arch, | ||
| 348 | "type": "script" | ||
| 349 | } | ||
| 350 | ) | ||
| 351 | self._bootscr = bootscr_node | ||
| 352 | |||
| 353 | def fitimage_emit_section_setup(self, setup_id, setup_path): | ||
| 354 | """Emit the fitImage ITS setup section""" | ||
| 355 | if self._setup: | ||
| 356 | bb.fatal("Setup section already exists in the ITS file.") | ||
| 357 | load = "<0x00090000>" | ||
| 358 | entry = "<0x00090000>" | ||
| 359 | setup_node = self.its_add_node_image( | ||
| 360 | setup_id, | ||
| 361 | "Linux setup.bin", | ||
| 362 | "x86_setup", | ||
| 363 | "none", | ||
| 364 | { | ||
| 365 | "data": '/incbin/("' + setup_path + '")', | ||
| 366 | "arch": self._arch, | ||
| 367 | "os": "linux", | ||
| 368 | "load": load, | ||
| 369 | "entry": entry | ||
| 370 | } | ||
| 371 | ) | ||
| 372 | self._setup = setup_node | ||
| 373 | |||
| 374 | def fitimage_emit_section_ramdisk(self, ramdisk_id, ramdisk_path, description="ramdisk", load=None, entry=None): | ||
| 375 | """Emit the fitImage ITS ramdisk section""" | ||
| 376 | if self._ramdisk: | ||
| 377 | bb.fatal("Ramdisk section already exists in the ITS file.") | ||
| 378 | opt_props = { | ||
| 379 | "data": '/incbin/("' + ramdisk_path + '")', | ||
| 380 | "type": "ramdisk", | ||
| 381 | "arch": self._arch, | ||
| 382 | "os": "linux" | ||
| 383 | } | ||
| 384 | if load: | ||
| 385 | opt_props["load"] = f"<{load}>" | ||
| 386 | if entry: | ||
| 387 | opt_props["entry"] = f"<{entry}>" | ||
| 388 | |||
| 389 | ramdisk_node = self.its_add_node_image( | ||
| 390 | ramdisk_id, | ||
| 391 | description, | ||
| 392 | "ramdisk", | ||
| 393 | "none", | ||
| 394 | opt_props | ||
| 395 | ) | ||
| 396 | self._ramdisk = ramdisk_node | ||
| 397 | |||
| 398 | def _fitimage_emit_one_section_config(self, conf_node_name, dtb=None): | ||
| 399 | """Emit the fitImage ITS configuration section""" | ||
| 400 | opt_props = {} | ||
| 401 | conf_desc = [] | ||
| 402 | sign_entries = [] | ||
| 403 | |||
| 404 | if self._kernel: | ||
| 405 | conf_desc.append("Linux kernel") | ||
| 406 | opt_props["kernel"] = self._kernel.name | ||
| 407 | if self._sign_enable: | ||
| 408 | sign_entries.append("kernel") | ||
| 409 | |||
| 410 | if dtb: | ||
| 411 | conf_desc.append("FDT blob") | ||
| 412 | opt_props["fdt"] = dtb.name | ||
| 413 | if dtb.compatible: | ||
| 414 | opt_props["compatible"] = dtb.compatible | ||
| 415 | if self._sign_enable: | ||
| 416 | sign_entries.append("fdt") | ||
| 417 | |||
| 418 | if self._ramdisk: | ||
| 419 | conf_desc.append("ramdisk") | ||
| 420 | opt_props["ramdisk"] = self._ramdisk.name | ||
| 421 | if self._sign_enable: | ||
| 422 | sign_entries.append("ramdisk") | ||
| 423 | |||
| 424 | if self._bootscr: | ||
| 425 | conf_desc.append("u-boot script") | ||
| 426 | opt_props["bootscr"] = self._bootscr.name | ||
| 427 | if self._sign_enable: | ||
| 428 | sign_entries.append("bootscr") | ||
| 429 | |||
| 430 | if self._setup: | ||
| 431 | conf_desc.append("setup") | ||
| 432 | opt_props["setup"] = self._setup.name | ||
| 433 | if self._sign_enable: | ||
| 434 | sign_entries.append("setup") | ||
| 435 | |||
| 436 | # First added configuration is the default configuration | ||
| 437 | default_flag = "0" | ||
| 438 | if len(self.configurations.sub_nodes) == 0: | ||
| 439 | default_flag = "1" | ||
| 440 | |||
| 441 | conf_node = ItsNodeConfiguration( | ||
| 442 | conf_node_name, | ||
| 443 | self.configurations, | ||
| 444 | f"{default_flag} {', '.join(conf_desc)}", | ||
| 445 | opt_props=opt_props | ||
| 446 | ) | ||
| 447 | if self._hash_algo: | ||
| 448 | ItsNodeHash( | ||
| 449 | "hash-1", | ||
| 450 | conf_node, | ||
| 451 | self._hash_algo | ||
| 452 | ) | ||
| 453 | if self._sign_enable: | ||
| 454 | ItsNodeConfigurationSignature( | ||
| 455 | "signature-1", | ||
| 456 | conf_node, | ||
| 457 | f"{self._hash_algo},{self._sign_algo}", | ||
| 458 | self._sign_keyname_conf, | ||
| 459 | opt_props={ | ||
| 460 | "padding": self._pad_algo, | ||
| 461 | "sign-images": sign_entries | ||
| 462 | } | ||
| 463 | ) | ||
| 464 | |||
| 465 | def fitimage_emit_section_config(self, default_dtb_image=None): | ||
| 466 | if self._dtbs: | ||
| 467 | for dtb in self._dtbs: | ||
| 468 | dtb_name = dtb.name | ||
| 469 | if dtb.name.startswith("fdt-"): | ||
| 470 | dtb_name = dtb.name[len("fdt-"):] | ||
| 471 | self._fitimage_emit_one_section_config(self._conf_prefix + dtb_name, dtb) | ||
| 472 | for dtb in self._dtb_alias: | ||
| 473 | self._fitimage_emit_one_section_config(self._conf_prefix + dtb.alias_name, dtb) | ||
| 474 | else: | ||
| 475 | # Currently exactly one kernel is supported. | ||
| 476 | self._fitimage_emit_one_section_config(self._conf_prefix + "1") | ||
| 477 | |||
| 478 | default_conf = self.configurations.sub_nodes[0].name | ||
| 479 | if default_dtb_image and self._dtbs: | ||
| 480 | default_conf = self._conf_prefix + default_dtb_image | ||
| 481 | self.configurations.add_property('default', default_conf) | ||
| 482 | |||
| 483 | def run_mkimage_assemble(self, itsfile, fitfile): | ||
| 484 | cmd = [ | ||
| 485 | self._mkimage, | ||
| 486 | '-f', itsfile, | ||
| 487 | fitfile | ||
| 488 | ] | ||
| 489 | if self._mkimage_dtcopts: | ||
| 490 | cmd.insert(1, '-D') | ||
| 491 | cmd.insert(2, self._mkimage_dtcopts) | ||
| 492 | try: | ||
| 493 | subprocess.run(cmd, check=True, capture_output=True) | ||
| 494 | except subprocess.CalledProcessError as e: | ||
| 495 | bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\nitsflile: {os.path.abspath(itsfile)}") | ||
| 496 | |||
| 497 | def run_mkimage_sign(self, fitfile): | ||
| 498 | if not self._sign_enable: | ||
| 499 | bb.debug(1, "FIT image signing is disabled. Skipping signing.") | ||
| 500 | return | ||
| 501 | |||
| 502 | # Some sanity checks because mkimage exits with 0 also without needed keys | ||
| 503 | sign_key_path = os.path.join(self._sign_keydir, self._sign_keyname_conf) | ||
| 504 | if not os.path.exists(sign_key_path + '.key') or not os.path.exists(sign_key_path + '.crt'): | ||
| 505 | bb.fatal("%s.key or .crt does not exist" % sign_key_path) | ||
| 506 | if self._sign_individual: | ||
| 507 | sign_key_img_path = os.path.join(self._sign_keydir, self._sign_keyname_img) | ||
| 508 | if not os.path.exists(sign_key_img_path + '.key') or not os.path.exists(sign_key_img_path + '.crt'): | ||
| 509 | bb.fatal("%s.key or .crt does not exist" % sign_key_img_path) | ||
| 510 | |||
| 511 | cmd = [ | ||
| 512 | self._mkimage_sign, | ||
| 513 | '-F', | ||
| 514 | '-k', self._sign_keydir, | ||
| 515 | '-r', fitfile | ||
| 516 | ] | ||
| 517 | if self._mkimage_dtcopts: | ||
| 518 | cmd.extend(['-D', self._mkimage_dtcopts]) | ||
| 519 | if self._mkimage_sign_args: | ||
| 520 | cmd.extend(shlex.split(self._mkimage_sign_args)) | ||
| 521 | try: | ||
| 522 | subprocess.run(cmd, check=True, capture_output=True) | ||
| 523 | except subprocess.CalledProcessError as e: | ||
| 524 | bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}") | ||
| 525 | |||
| 526 | |||
| 527 | def symlink_points_below(file_or_symlink, expected_parent_dir): | ||
| 528 | """returns symlink destination if it points below directory""" | ||
| 529 | file_path = os.path.join(expected_parent_dir, file_or_symlink) | ||
| 530 | if not os.path.islink(file_path): | ||
| 531 | return None | ||
| 532 | |||
| 533 | realpath = os.path.relpath(os.path.realpath(file_path), expected_parent_dir) | ||
| 534 | if realpath.startswith(".."): | ||
| 535 | return None | ||
| 536 | |||
| 537 | return realpath | ||
| 538 | |||
| 539 | def get_compatible_from_dtb(dtb_path, fdtget_path="fdtget"): | ||
| 540 | compatible = None | ||
| 541 | cmd = [fdtget_path, "-t", "s", dtb_path, "/", "compatible"] | ||
| 542 | try: | ||
| 543 | ret = subprocess.run(cmd, check=True, capture_output=True, text=True) | ||
| 544 | compatible = ret.stdout.strip().split() | ||
| 545 | except subprocess.CalledProcessError: | ||
| 546 | compatible = None | ||
| 547 | return compatible | ||
