blob: 05b1a1d4b08541c83b9d9e43835692beaa09a9a9 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbe39f052016-07-25 18:59:08 -06002#
3# Copyright (c) 2016 Google, Inc
4#
Simon Glassbe39f052016-07-25 18:59:08 -06005
Simon Glassac6328c2018-09-14 04:57:28 -06006import glob
Simon Glassbe39f052016-07-25 18:59:08 -06007import os
8import shutil
Simon Glass37fdd142019-07-20 12:24:06 -06009import struct
Simon Glassac0d4952019-05-14 15:53:47 -060010import sys
Simon Glassbe39f052016-07-25 18:59:08 -060011import tempfile
12
Simon Glassa997ea52020-04-17 18:09:04 -060013from patman import command
14from patman import tout
Simon Glassbe39f052016-07-25 18:59:08 -060015
Simon Glass0362cd22018-07-17 13:25:43 -060016# Output directly (generally this is temporary)
Simon Glassbe39f052016-07-25 18:59:08 -060017outdir = None
Simon Glass0362cd22018-07-17 13:25:43 -060018
19# True to keep the output directory around after exiting
Simon Glassbe39f052016-07-25 18:59:08 -060020preserve_outdir = False
21
Simon Glass0362cd22018-07-17 13:25:43 -060022# Path to the Chrome OS chroot, if we know it
23chroot_path = None
24
25# Search paths to use for Filename(), used to find files
26search_paths = []
27
Simon Glass90c3c4c2019-07-08 13:18:27 -060028tool_search_paths = []
29
Simon Glass778ab842018-09-14 04:57:25 -060030# Tools and the packages that contain them, on debian
31packages = {
32 'lz4': 'liblz4-tool',
33 }
Simon Glass0362cd22018-07-17 13:25:43 -060034
Simon Glass867b9062018-10-01 21:12:44 -060035# List of paths to use when looking for an input file
36indir = []
37
Simon Glassbe39f052016-07-25 18:59:08 -060038def PrepareOutputDir(dirname, preserve=False):
39 """Select an output directory, ensuring it exists.
40
41 This either creates a temporary directory or checks that the one supplied
42 by the user is valid. For a temporary directory, it makes a note to
43 remove it later if required.
44
45 Args:
46 dirname: a string, name of the output directory to use to store
47 intermediate and output files. If is None - create a temporary
48 directory.
49 preserve: a Boolean. If outdir above is None and preserve is False, the
50 created temporary directory will be destroyed on exit.
51
52 Raises:
53 OSError: If it cannot create the output directory.
54 """
55 global outdir, preserve_outdir
56
57 preserve_outdir = dirname or preserve
58 if dirname:
59 outdir = dirname
60 if not os.path.isdir(outdir):
61 try:
62 os.makedirs(outdir)
63 except OSError as err:
64 raise CmdError("Cannot make output directory '%s': '%s'" %
65 (outdir, err.strerror))
66 tout.Debug("Using output directory '%s'" % outdir)
67 else:
68 outdir = tempfile.mkdtemp(prefix='binman.')
69 tout.Debug("Using temporary directory '%s'" % outdir)
70
71def _RemoveOutputDir():
72 global outdir
73
74 shutil.rmtree(outdir)
75 tout.Debug("Deleted temporary directory '%s'" % outdir)
76 outdir = None
77
78def FinaliseOutputDir():
79 global outdir, preserve_outdir
80
81 """Tidy up: delete output directory if temporary and not preserved."""
82 if outdir and not preserve_outdir:
83 _RemoveOutputDir()
Simon Glass7f47e082019-07-20 12:24:07 -060084 outdir = None
Simon Glassbe39f052016-07-25 18:59:08 -060085
86def GetOutputFilename(fname):
87 """Return a filename within the output directory.
88
89 Args:
90 fname: Filename to use for new file
91
92 Returns:
93 The full path of the filename, within the output directory
94 """
95 return os.path.join(outdir, fname)
96
97def _FinaliseForTest():
98 """Remove the output directory (for use by tests)"""
99 global outdir
100
101 if outdir:
102 _RemoveOutputDir()
Simon Glass7f47e082019-07-20 12:24:07 -0600103 outdir = None
Simon Glassbe39f052016-07-25 18:59:08 -0600104
105def SetInputDirs(dirname):
106 """Add a list of input directories, where input files are kept.
107
108 Args:
109 dirname: a list of paths to input directories to use for obtaining
110 files needed by binman to place in the image.
111 """
112 global indir
113
114 indir = dirname
115 tout.Debug("Using input directories %s" % indir)
116
Simon Glass5d94cc62020-07-09 18:39:38 -0600117def GetInputFilename(fname, allow_missing=False):
Simon Glassbe39f052016-07-25 18:59:08 -0600118 """Return a filename for use as input.
119
120 Args:
121 fname: Filename to use for new file
Simon Glass5d94cc62020-07-09 18:39:38 -0600122 allow_missing: True if the filename can be missing
Simon Glassbe39f052016-07-25 18:59:08 -0600123
124 Returns:
Simon Glass5d94cc62020-07-09 18:39:38 -0600125 The full path of the filename, within the input directory, or
126 None on error
Simon Glassbe39f052016-07-25 18:59:08 -0600127 """
Simon Glass4affd4b2019-08-24 07:22:54 -0600128 if not indir or fname[:1] == '/':
Simon Glassbe39f052016-07-25 18:59:08 -0600129 return fname
130 for dirname in indir:
131 pathname = os.path.join(dirname, fname)
132 if os.path.exists(pathname):
133 return pathname
134
Simon Glass5d94cc62020-07-09 18:39:38 -0600135 if allow_missing:
136 return None
Simon Glassf2eb0542018-07-17 13:25:45 -0600137 raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
138 (fname, ','.join(indir), os.getcwd()))
Simon Glassbe39f052016-07-25 18:59:08 -0600139
Simon Glassac6328c2018-09-14 04:57:28 -0600140def GetInputFilenameGlob(pattern):
141 """Return a list of filenames for use as input.
142
143 Args:
144 pattern: Filename pattern to search for
145
146 Returns:
147 A list of matching files in all input directories
148 """
149 if not indir:
150 return glob.glob(fname)
151 files = []
152 for dirname in indir:
153 pathname = os.path.join(dirname, pattern)
154 files += glob.glob(pathname)
155 return sorted(files)
156
Simon Glassbe39f052016-07-25 18:59:08 -0600157def Align(pos, align):
158 if align:
159 mask = align - 1
160 pos = (pos + mask) & ~mask
161 return pos
162
163def NotPowerOfTwo(num):
164 return num and (num & (num - 1))
Simon Glass0362cd22018-07-17 13:25:43 -0600165
Simon Glass90c3c4c2019-07-08 13:18:27 -0600166def SetToolPaths(toolpaths):
167 """Set the path to search for tools
168
169 Args:
170 toolpaths: List of paths to search for tools executed by Run()
171 """
172 global tool_search_paths
173
174 tool_search_paths = toolpaths
175
176def PathHasFile(path_spec, fname):
Simon Glass778ab842018-09-14 04:57:25 -0600177 """Check if a given filename is in the PATH
178
179 Args:
Simon Glass90c3c4c2019-07-08 13:18:27 -0600180 path_spec: Value of PATH variable to check
Simon Glass778ab842018-09-14 04:57:25 -0600181 fname: Filename to check
182
183 Returns:
184 True if found, False if not
185 """
Simon Glass90c3c4c2019-07-08 13:18:27 -0600186 for dir in path_spec.split(':'):
Simon Glass778ab842018-09-14 04:57:25 -0600187 if os.path.exists(os.path.join(dir, fname)):
188 return True
189 return False
190
Alper Nebi Yasakcd740d32020-09-06 14:46:06 +0300191def GetHostCompileTool(name):
192 """Get the host-specific version for a compile tool
193
194 This checks the environment variables that specify which version of
195 the tool should be used (e.g. ${HOSTCC}).
196
197 The following table lists the host-specific versions of the tools
198 this function resolves to:
199
200 Compile Tool | Host version
201 --------------+----------------
202 as | ${HOSTAS}
203 ld | ${HOSTLD}
204 cc | ${HOSTCC}
205 cpp | ${HOSTCPP}
206 c++ | ${HOSTCXX}
207 ar | ${HOSTAR}
208 nm | ${HOSTNM}
209 ldr | ${HOSTLDR}
210 strip | ${HOSTSTRIP}
211 objcopy | ${HOSTOBJCOPY}
212 objdump | ${HOSTOBJDUMP}
213 dtc | ${HOSTDTC}
214
215 Args:
216 name: Command name to run
217
218 Returns:
219 host_name: Exact command name to run instead
220 extra_args: List of extra arguments to pass
221 """
222 host_name = None
223 extra_args = []
224 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
225 'objcopy', 'objdump', 'dtc'):
226 host_name, *host_args = env.get('HOST' + name.upper(), '').split(' ')
227 elif name == 'c++':
228 host_name, *host_args = env.get('HOSTCXX', '').split(' ')
229
230 if host_name:
231 return host_name, extra_args
232 return name, []
233
Alper Nebi Yasak5cd321d2020-09-06 14:46:05 +0300234def GetTargetCompileTool(name, cross_compile=None):
235 """Get the target-specific version for a compile tool
236
237 This first checks the environment variables that specify which
238 version of the tool should be used (e.g. ${CC}). If those aren't
239 specified, it checks the CROSS_COMPILE variable as a prefix for the
240 tool with some substitutions (e.g. "${CROSS_COMPILE}gcc" for cc).
241
242 The following table lists the target-specific versions of the tools
243 this function resolves to:
244
245 Compile Tool | First choice | Second choice
246 --------------+----------------+----------------------------
247 as | ${AS} | ${CROSS_COMPILE}as
248 ld | ${LD} | ${CROSS_COMPILE}ld.bfd
249 | | or ${CROSS_COMPILE}ld
250 cc | ${CC} | ${CROSS_COMPILE}gcc
251 cpp | ${CPP} | ${CROSS_COMPILE}gcc -E
252 c++ | ${CXX} | ${CROSS_COMPILE}g++
253 ar | ${AR} | ${CROSS_COMPILE}ar
254 nm | ${NM} | ${CROSS_COMPILE}nm
255 ldr | ${LDR} | ${CROSS_COMPILE}ldr
256 strip | ${STRIP} | ${CROSS_COMPILE}strip
257 objcopy | ${OBJCOPY} | ${CROSS_COMPILE}objcopy
258 objdump | ${OBJDUMP} | ${CROSS_COMPILE}objdump
259 dtc | ${DTC} | (no CROSS_COMPILE version)
260
261 Args:
262 name: Command name to run
263
264 Returns:
265 target_name: Exact command name to run instead
266 extra_args: List of extra arguments to pass
267 """
268 env = dict(os.environ)
269
270 target_name = None
271 extra_args = []
272 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
273 'objcopy', 'objdump', 'dtc'):
274 target_name, *extra_args = env.get(name.upper(), '').split(' ')
275 elif name == 'c++':
276 target_name, *extra_args = env.get('CXX', '').split(' ')
277
278 if target_name:
279 return target_name, extra_args
280
281 if cross_compile is None:
282 cross_compile = env.get('CROSS_COMPILE', '')
283 if not cross_compile:
284 return name, []
285
286 if name in ('as', 'ar', 'nm', 'ldr', 'strip', 'objcopy', 'objdump'):
287 target_name = cross_compile + name
288 elif name == 'ld':
289 try:
290 if Run(cross_compile + 'ld.bfd', '-v'):
291 target_name = cross_compile + 'ld.bfd'
292 except:
293 target_name = cross_compile + 'ld'
294 elif name == 'cc':
295 target_name = cross_compile + 'gcc'
296 elif name == 'cpp':
297 target_name = cross_compile + 'gcc'
298 extra_args = ['-E']
299 elif name == 'c++':
300 target_name = cross_compile + 'g++'
301 else:
302 target_name = name
303 return target_name, extra_args
304
Simon Glasscc311ac2019-10-31 07:42:50 -0600305def Run(name, *args, **kwargs):
Simon Glass90c3c4c2019-07-08 13:18:27 -0600306 """Run a tool with some arguments
307
308 This runs a 'tool', which is a program used by binman to process files and
309 perhaps produce some output. Tools can be located on the PATH or in a
310 search path.
311
312 Args:
313 name: Command name to run
314 args: Arguments to the tool
Alper Nebi Yasakcd740d32020-09-06 14:46:06 +0300315 for_host: True to resolve the command to the version for the host
Alper Nebi Yasak5cd321d2020-09-06 14:46:05 +0300316 for_target: False to run the command as-is, without resolving it
317 to the version for the compile target
Simon Glass90c3c4c2019-07-08 13:18:27 -0600318
319 Returns:
320 CommandResult object
321 """
Simon Glass778ab842018-09-14 04:57:25 -0600322 try:
Simon Glasscc311ac2019-10-31 07:42:50 -0600323 binary = kwargs.get('binary')
Alper Nebi Yasakcd740d32020-09-06 14:46:06 +0300324 for_host = kwargs.get('for_host', False)
325 for_target = kwargs.get('for_target', not for_host)
Simon Glass90c3c4c2019-07-08 13:18:27 -0600326 env = None
327 if tool_search_paths:
328 env = dict(os.environ)
329 env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH']
Alper Nebi Yasak5cd321d2020-09-06 14:46:05 +0300330 if for_target:
331 name, extra_args = GetTargetCompileTool(name)
332 args = tuple(extra_args) + args
Alper Nebi Yasakcd740d32020-09-06 14:46:06 +0300333 elif for_host:
334 name, extra_args = GetHostCompileTool(name)
335 args = tuple(extra_args) + args
Simon Glass6e892242020-11-09 07:45:02 -0700336 name = os.path.expanduser(name) # Expand paths containing ~
Simon Glass9f5051a2019-08-24 07:22:42 -0600337 all_args = (name,) + args
338 result = command.RunPipe([all_args], capture=True, capture_stderr=True,
Simon Glasscc311ac2019-10-31 07:42:50 -0600339 env=env, raise_on_error=False, binary=binary)
Simon Glass9f5051a2019-08-24 07:22:42 -0600340 if result.return_code:
341 raise Exception("Error %d running '%s': %s" %
342 (result.return_code,' '.join(all_args),
343 result.stderr))
344 return result.stdout
Simon Glass778ab842018-09-14 04:57:25 -0600345 except:
Simon Glass90c3c4c2019-07-08 13:18:27 -0600346 if env and not PathHasFile(env['PATH'], name):
347 msg = "Please install tool '%s'" % name
Simon Glass778ab842018-09-14 04:57:25 -0600348 package = packages.get(name)
349 if package:
350 msg += " (e.g. from package '%s')" % package
351 raise ValueError(msg)
352 raise
Simon Glass0362cd22018-07-17 13:25:43 -0600353
354def Filename(fname):
355 """Resolve a file path to an absolute path.
356
357 If fname starts with ##/ and chroot is available, ##/ gets replaced with
358 the chroot path. If chroot is not available, this file name can not be
359 resolved, `None' is returned.
360
361 If fname is not prepended with the above prefix, and is not an existing
362 file, the actual file name is retrieved from the passed in string and the
363 search_paths directories (if any) are searched to for the file. If found -
364 the path to the found file is returned, `None' is returned otherwise.
365
366 Args:
367 fname: a string, the path to resolve.
368
369 Returns:
370 Absolute path to the file or None if not found.
371 """
372 if fname.startswith('##/'):
373 if chroot_path:
374 fname = os.path.join(chroot_path, fname[3:])
375 else:
376 return None
377
378 # Search for a pathname that exists, and return it if found
379 if fname and not os.path.exists(fname):
380 for path in search_paths:
381 pathname = os.path.join(path, os.path.basename(fname))
382 if os.path.exists(pathname):
383 return pathname
384
385 # If not found, just return the standard, unchanged path
386 return fname
387
Simon Glass0696ee62019-05-17 22:00:44 -0600388def ReadFile(fname, binary=True):
Simon Glass0362cd22018-07-17 13:25:43 -0600389 """Read and return the contents of a file.
390
391 Args:
392 fname: path to filename to read, where ## signifiies the chroot.
393
394 Returns:
395 data read from file, as a string.
396 """
Simon Glass0696ee62019-05-17 22:00:44 -0600397 with open(Filename(fname), binary and 'rb' or 'r') as fd:
Simon Glass0362cd22018-07-17 13:25:43 -0600398 data = fd.read()
399 #self._out.Info("Read file '%s' size %d (%#0x)" %
400 #(fname, len(data), len(data)))
401 return data
402
Simon Glass54f1c5b2020-07-05 21:41:50 -0600403def WriteFile(fname, data, binary=True):
Simon Glass0362cd22018-07-17 13:25:43 -0600404 """Write data into a file.
405
406 Args:
407 fname: path to filename to write
408 data: data to write to file, as a string
409 """
410 #self._out.Info("Write file '%s' size %d (%#0x)" %
411 #(fname, len(data), len(data)))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600412 with open(Filename(fname), binary and 'wb' or 'w') as fd:
Simon Glass0362cd22018-07-17 13:25:43 -0600413 fd.write(data)
Simon Glassac0d4952019-05-14 15:53:47 -0600414
415def GetBytes(byte, size):
416 """Get a string of bytes of a given size
417
418 This handles the unfortunate different between Python 2 and Python 2.
419
420 Args:
421 byte: Numeric byte value to use
422 size: Size of bytes/string to return
423
424 Returns:
425 A bytes type with 'byte' repeated 'size' times
426 """
427 if sys.version_info[0] >= 3:
428 data = bytes([byte]) * size
429 else:
430 data = chr(byte) * size
431 return data
Simon Glass8bb7a7a2019-05-14 15:53:50 -0600432
433def ToUnicode(val):
434 """Make sure a value is a unicode string
435
436 This allows some amount of compatibility between Python 2 and Python3. For
437 the former, it returns a unicode object.
438
439 Args:
440 val: string or unicode object
441
442 Returns:
443 unicode version of val
444 """
445 if sys.version_info[0] >= 3:
446 return val
447 return val if isinstance(val, unicode) else val.decode('utf-8')
448
449def FromUnicode(val):
450 """Make sure a value is a non-unicode string
451
452 This allows some amount of compatibility between Python 2 and Python3. For
453 the former, it converts a unicode object to a string.
454
455 Args:
456 val: string or unicode object
457
458 Returns:
459 non-unicode version of val
460 """
461 if sys.version_info[0] >= 3:
462 return val
463 return val if isinstance(val, str) else val.encode('utf-8')
Simon Glassd8f593f2019-05-17 22:00:35 -0600464
465def ToByte(ch):
466 """Convert a character to an ASCII value
467
468 This is useful because in Python 2 bytes is an alias for str, but in
469 Python 3 they are separate types. This function converts the argument to
470 an ASCII value in either case.
471
472 Args:
473 ch: A string (Python 2) or byte (Python 3) value
474
475 Returns:
476 integer ASCII value for ch
477 """
478 return ord(ch) if type(ch) == str else ch
479
480def ToChar(byte):
481 """Convert a byte to a character
482
483 This is useful because in Python 2 bytes is an alias for str, but in
484 Python 3 they are separate types. This function converts an ASCII value to
485 a value with the appropriate type in either case.
486
487 Args:
488 byte: A byte or str value
489 """
490 return chr(byte) if type(byte) != str else byte
Simon Glass1cd40082019-05-17 22:00:36 -0600491
492def ToChars(byte_list):
493 """Convert a list of bytes to a str/bytes type
494
495 Args:
496 byte_list: List of ASCII values representing the string
497
498 Returns:
499 string made by concatenating all the ASCII values
500 """
501 return ''.join([chr(byte) for byte in byte_list])
502
503def ToBytes(string):
504 """Convert a str type into a bytes type
505
506 Args:
Simon Glasscc311ac2019-10-31 07:42:50 -0600507 string: string to convert
Simon Glass1cd40082019-05-17 22:00:36 -0600508
509 Returns:
510 Python 3: A bytes type
511 Python 2: A string type
512 """
513 if sys.version_info[0] >= 3:
514 return string.encode('utf-8')
515 return string
Simon Glassdfd19012019-07-08 13:18:41 -0600516
Simon Glasscc311ac2019-10-31 07:42:50 -0600517def ToString(bval):
518 """Convert a bytes type into a str type
519
520 Args:
521 bval: bytes value to convert
522
523 Returns:
524 Python 3: A bytes type
525 Python 2: A string type
526 """
527 return bval.decode('utf-8')
528
Simon Glass37fdd142019-07-20 12:24:06 -0600529def Compress(indata, algo, with_header=True):
Simon Glassdfd19012019-07-08 13:18:41 -0600530 """Compress some data using a given algorithm
531
532 Note that for lzma this uses an old version of the algorithm, not that
533 provided by xz.
534
535 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
536 directory to be previously set up, by calling PrepareOutputDir().
537
538 Args:
539 indata: Input data to compress
540 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
541
542 Returns:
543 Compressed data
544 """
545 if algo == 'none':
546 return indata
547 fname = GetOutputFilename('%s.comp.tmp' % algo)
548 WriteFile(fname, indata)
549 if algo == 'lz4':
Simon Glasscc311ac2019-10-31 07:42:50 -0600550 data = Run('lz4', '--no-frame-crc', '-c', fname, binary=True)
Simon Glassdfd19012019-07-08 13:18:41 -0600551 # cbfstool uses a very old version of lzma
552 elif algo == 'lzma':
553 outfname = GetOutputFilename('%s.comp.otmp' % algo)
554 Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8')
555 data = ReadFile(outfname)
556 elif algo == 'gzip':
Simon Glasscc311ac2019-10-31 07:42:50 -0600557 data = Run('gzip', '-c', fname, binary=True)
Simon Glassdfd19012019-07-08 13:18:41 -0600558 else:
559 raise ValueError("Unknown algorithm '%s'" % algo)
Simon Glass37fdd142019-07-20 12:24:06 -0600560 if with_header:
561 hdr = struct.pack('<I', len(data))
562 data = hdr + data
Simon Glassdfd19012019-07-08 13:18:41 -0600563 return data
564
Simon Glass37fdd142019-07-20 12:24:06 -0600565def Decompress(indata, algo, with_header=True):
Simon Glassdfd19012019-07-08 13:18:41 -0600566 """Decompress some data using a given algorithm
567
568 Note that for lzma this uses an old version of the algorithm, not that
569 provided by xz.
570
571 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
572 directory to be previously set up, by calling PrepareOutputDir().
573
574 Args:
575 indata: Input data to decompress
576 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
577
578 Returns:
579 Compressed data
580 """
581 if algo == 'none':
582 return indata
Simon Glass37fdd142019-07-20 12:24:06 -0600583 if with_header:
584 data_len = struct.unpack('<I', indata[:4])[0]
585 indata = indata[4:4 + data_len]
Simon Glassdfd19012019-07-08 13:18:41 -0600586 fname = GetOutputFilename('%s.decomp.tmp' % algo)
587 with open(fname, 'wb') as fd:
588 fd.write(indata)
589 if algo == 'lz4':
Simon Glasscc311ac2019-10-31 07:42:50 -0600590 data = Run('lz4', '-dc', fname, binary=True)
Simon Glassdfd19012019-07-08 13:18:41 -0600591 elif algo == 'lzma':
592 outfname = GetOutputFilename('%s.decomp.otmp' % algo)
593 Run('lzma_alone', 'd', fname, outfname)
Simon Glasscc311ac2019-10-31 07:42:50 -0600594 data = ReadFile(outfname, binary=True)
Simon Glassdfd19012019-07-08 13:18:41 -0600595 elif algo == 'gzip':
Simon Glasscc311ac2019-10-31 07:42:50 -0600596 data = Run('gzip', '-cd', fname, binary=True)
Simon Glassdfd19012019-07-08 13:18:41 -0600597 else:
598 raise ValueError("Unknown algorithm '%s'" % algo)
599 return data
Simon Glass578d53e2019-07-08 13:18:51 -0600600
601CMD_CREATE, CMD_DELETE, CMD_ADD, CMD_REPLACE, CMD_EXTRACT = range(5)
602
603IFWITOOL_CMDS = {
604 CMD_CREATE: 'create',
605 CMD_DELETE: 'delete',
606 CMD_ADD: 'add',
607 CMD_REPLACE: 'replace',
608 CMD_EXTRACT: 'extract',
609 }
610
611def RunIfwiTool(ifwi_file, cmd, fname=None, subpart=None, entry_name=None):
612 """Run ifwitool with the given arguments:
613
614 Args:
615 ifwi_file: IFWI file to operation on
616 cmd: Command to execute (CMD_...)
617 fname: Filename of file to add/replace/extract/create (None for
618 CMD_DELETE)
619 subpart: Name of sub-partition to operation on (None for CMD_CREATE)
620 entry_name: Name of directory entry to operate on, or None if none
621 """
622 args = ['ifwitool', ifwi_file]
623 args.append(IFWITOOL_CMDS[cmd])
624 if fname:
625 args += ['-f', fname]
626 if subpart:
627 args += ['-n', subpart]
628 if entry_name:
629 args += ['-d', '-e', entry_name]
630 Run(*args)
Simon Glassb6dff4c2019-07-20 12:23:36 -0600631
632def ToHex(val):
633 """Convert an integer value (or None) to a string
634
635 Returns:
636 hex value, or 'None' if the value is None
637 """
638 return 'None' if val is None else '%#x' % val
639
640def ToHexSize(val):
641 """Return the size of an object in hex
642
643 Returns:
644 hex value of size, or 'None' if the value is None
645 """
646 return 'None' if val is None else '%#x' % len(val)