blob: 7af4a52a8fd7dbbd392f5f2dbedbc4c4177aa035 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass1f1864b2016-07-25 18:59:08 -06002#
3# Copyright (c) 2016 Google, Inc
4#
Simon Glass1f1864b2016-07-25 18:59:08 -06005
Simon Glass0a98b282018-09-14 04:57:28 -06006import glob
Simon Glass1f1864b2016-07-25 18:59:08 -06007import os
Paul Barker0d60e5d2021-09-08 12:38:02 +01008import shlex
Simon Glass1f1864b2016-07-25 18:59:08 -06009import shutil
Simon Glasseb0f4a42019-07-20 12:24:06 -060010import struct
Simon Glasse6d85ff2019-05-14 15:53:47 -060011import sys
Simon Glass1f1864b2016-07-25 18:59:08 -060012import tempfile
13
Simon Glassbf776672020-04-17 18:09:04 -060014from patman import command
15from patman import tout
Simon Glass1f1864b2016-07-25 18:59:08 -060016
Simon Glassaeffc5e2018-07-17 13:25:43 -060017# Output directly (generally this is temporary)
Simon Glass1f1864b2016-07-25 18:59:08 -060018outdir = None
Simon Glassaeffc5e2018-07-17 13:25:43 -060019
20# True to keep the output directory around after exiting
Simon Glass1f1864b2016-07-25 18:59:08 -060021preserve_outdir = False
22
Simon Glassaeffc5e2018-07-17 13:25:43 -060023# Path to the Chrome OS chroot, if we know it
24chroot_path = None
25
26# Search paths to use for Filename(), used to find files
27search_paths = []
28
Simon Glassc22b8cf2019-07-08 13:18:27 -060029tool_search_paths = []
30
Simon Glass04187a82018-09-14 04:57:25 -060031# Tools and the packages that contain them, on debian
32packages = {
33 'lz4': 'liblz4-tool',
34 }
Simon Glassaeffc5e2018-07-17 13:25:43 -060035
Simon Glass1fda1822018-10-01 21:12:44 -060036# List of paths to use when looking for an input file
37indir = []
38
Simon Glass1f1864b2016-07-25 18:59:08 -060039def PrepareOutputDir(dirname, preserve=False):
40 """Select an output directory, ensuring it exists.
41
42 This either creates a temporary directory or checks that the one supplied
43 by the user is valid. For a temporary directory, it makes a note to
44 remove it later if required.
45
46 Args:
47 dirname: a string, name of the output directory to use to store
48 intermediate and output files. If is None - create a temporary
49 directory.
50 preserve: a Boolean. If outdir above is None and preserve is False, the
51 created temporary directory will be destroyed on exit.
52
53 Raises:
54 OSError: If it cannot create the output directory.
55 """
56 global outdir, preserve_outdir
57
58 preserve_outdir = dirname or preserve
59 if dirname:
60 outdir = dirname
61 if not os.path.isdir(outdir):
62 try:
63 os.makedirs(outdir)
64 except OSError as err:
65 raise CmdError("Cannot make output directory '%s': '%s'" %
66 (outdir, err.strerror))
67 tout.Debug("Using output directory '%s'" % outdir)
68 else:
69 outdir = tempfile.mkdtemp(prefix='binman.')
70 tout.Debug("Using temporary directory '%s'" % outdir)
71
72def _RemoveOutputDir():
73 global outdir
74
75 shutil.rmtree(outdir)
76 tout.Debug("Deleted temporary directory '%s'" % outdir)
77 outdir = None
78
79def FinaliseOutputDir():
80 global outdir, preserve_outdir
81
82 """Tidy up: delete output directory if temporary and not preserved."""
83 if outdir and not preserve_outdir:
84 _RemoveOutputDir()
Simon Glass31353302019-07-20 12:24:07 -060085 outdir = None
Simon Glass1f1864b2016-07-25 18:59:08 -060086
87def GetOutputFilename(fname):
88 """Return a filename within the output directory.
89
90 Args:
91 fname: Filename to use for new file
92
93 Returns:
94 The full path of the filename, within the output directory
95 """
96 return os.path.join(outdir, fname)
97
Simon Glass10cbd3b2020-12-28 20:34:52 -070098def GetOutputDir():
99 """Return the current output directory
100
101 Returns:
102 str: The output directory
103 """
104 return outdir
105
Simon Glass1f1864b2016-07-25 18:59:08 -0600106def _FinaliseForTest():
107 """Remove the output directory (for use by tests)"""
108 global outdir
109
110 if outdir:
111 _RemoveOutputDir()
Simon Glass31353302019-07-20 12:24:07 -0600112 outdir = None
Simon Glass1f1864b2016-07-25 18:59:08 -0600113
114def SetInputDirs(dirname):
115 """Add a list of input directories, where input files are kept.
116
117 Args:
118 dirname: a list of paths to input directories to use for obtaining
119 files needed by binman to place in the image.
120 """
121 global indir
122
123 indir = dirname
124 tout.Debug("Using input directories %s" % indir)
125
Simon Glass4f9f1052020-07-09 18:39:38 -0600126def GetInputFilename(fname, allow_missing=False):
Simon Glass1f1864b2016-07-25 18:59:08 -0600127 """Return a filename for use as input.
128
129 Args:
130 fname: Filename to use for new file
Simon Glass4f9f1052020-07-09 18:39:38 -0600131 allow_missing: True if the filename can be missing
Simon Glass1f1864b2016-07-25 18:59:08 -0600132
133 Returns:
Simon Glass5187b802021-03-18 20:25:03 +1300134 fname, if indir is None;
135 full path of the filename, within the input directory;
136 None, if file is missing and allow_missing is True
137
138 Raises:
139 ValueError if file is missing and allow_missing is False
Simon Glass1f1864b2016-07-25 18:59:08 -0600140 """
Simon Glassf514d8f2019-08-24 07:22:54 -0600141 if not indir or fname[:1] == '/':
Simon Glass1f1864b2016-07-25 18:59:08 -0600142 return fname
143 for dirname in indir:
144 pathname = os.path.join(dirname, fname)
145 if os.path.exists(pathname):
146 return pathname
147
Simon Glass4f9f1052020-07-09 18:39:38 -0600148 if allow_missing:
149 return None
Simon Glass4f5dea42018-07-17 13:25:45 -0600150 raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
151 (fname, ','.join(indir), os.getcwd()))
Simon Glass1f1864b2016-07-25 18:59:08 -0600152
Simon Glass0a98b282018-09-14 04:57:28 -0600153def GetInputFilenameGlob(pattern):
154 """Return a list of filenames for use as input.
155
156 Args:
157 pattern: Filename pattern to search for
158
159 Returns:
160 A list of matching files in all input directories
161 """
162 if not indir:
163 return glob.glob(fname)
164 files = []
165 for dirname in indir:
166 pathname = os.path.join(dirname, pattern)
167 files += glob.glob(pathname)
168 return sorted(files)
169
Simon Glass1f1864b2016-07-25 18:59:08 -0600170def Align(pos, align):
171 if align:
172 mask = align - 1
173 pos = (pos + mask) & ~mask
174 return pos
175
176def NotPowerOfTwo(num):
177 return num and (num & (num - 1))
Simon Glassaeffc5e2018-07-17 13:25:43 -0600178
Simon Glassc22b8cf2019-07-08 13:18:27 -0600179def SetToolPaths(toolpaths):
180 """Set the path to search for tools
181
182 Args:
183 toolpaths: List of paths to search for tools executed by Run()
184 """
185 global tool_search_paths
186
187 tool_search_paths = toolpaths
188
189def PathHasFile(path_spec, fname):
Simon Glass04187a82018-09-14 04:57:25 -0600190 """Check if a given filename is in the PATH
191
192 Args:
Simon Glassc22b8cf2019-07-08 13:18:27 -0600193 path_spec: Value of PATH variable to check
Simon Glass04187a82018-09-14 04:57:25 -0600194 fname: Filename to check
195
196 Returns:
197 True if found, False if not
198 """
Simon Glassc22b8cf2019-07-08 13:18:27 -0600199 for dir in path_spec.split(':'):
Simon Glass04187a82018-09-14 04:57:25 -0600200 if os.path.exists(os.path.join(dir, fname)):
201 return True
202 return False
203
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300204def GetHostCompileTool(name):
205 """Get the host-specific version for a compile tool
206
207 This checks the environment variables that specify which version of
208 the tool should be used (e.g. ${HOSTCC}).
209
210 The following table lists the host-specific versions of the tools
211 this function resolves to:
212
213 Compile Tool | Host version
214 --------------+----------------
215 as | ${HOSTAS}
216 ld | ${HOSTLD}
217 cc | ${HOSTCC}
218 cpp | ${HOSTCPP}
219 c++ | ${HOSTCXX}
220 ar | ${HOSTAR}
221 nm | ${HOSTNM}
222 ldr | ${HOSTLDR}
223 strip | ${HOSTSTRIP}
224 objcopy | ${HOSTOBJCOPY}
225 objdump | ${HOSTOBJDUMP}
226 dtc | ${HOSTDTC}
227
228 Args:
229 name: Command name to run
230
231 Returns:
232 host_name: Exact command name to run instead
233 extra_args: List of extra arguments to pass
234 """
235 host_name = None
236 extra_args = []
237 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
238 'objcopy', 'objdump', 'dtc'):
239 host_name, *host_args = env.get('HOST' + name.upper(), '').split(' ')
240 elif name == 'c++':
241 host_name, *host_args = env.get('HOSTCXX', '').split(' ')
242
243 if host_name:
244 return host_name, extra_args
245 return name, []
246
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300247def GetTargetCompileTool(name, cross_compile=None):
248 """Get the target-specific version for a compile tool
249
250 This first checks the environment variables that specify which
251 version of the tool should be used (e.g. ${CC}). If those aren't
252 specified, it checks the CROSS_COMPILE variable as a prefix for the
253 tool with some substitutions (e.g. "${CROSS_COMPILE}gcc" for cc).
254
255 The following table lists the target-specific versions of the tools
256 this function resolves to:
257
258 Compile Tool | First choice | Second choice
259 --------------+----------------+----------------------------
260 as | ${AS} | ${CROSS_COMPILE}as
261 ld | ${LD} | ${CROSS_COMPILE}ld.bfd
262 | | or ${CROSS_COMPILE}ld
263 cc | ${CC} | ${CROSS_COMPILE}gcc
264 cpp | ${CPP} | ${CROSS_COMPILE}gcc -E
265 c++ | ${CXX} | ${CROSS_COMPILE}g++
266 ar | ${AR} | ${CROSS_COMPILE}ar
267 nm | ${NM} | ${CROSS_COMPILE}nm
268 ldr | ${LDR} | ${CROSS_COMPILE}ldr
269 strip | ${STRIP} | ${CROSS_COMPILE}strip
270 objcopy | ${OBJCOPY} | ${CROSS_COMPILE}objcopy
271 objdump | ${OBJDUMP} | ${CROSS_COMPILE}objdump
272 dtc | ${DTC} | (no CROSS_COMPILE version)
273
274 Args:
275 name: Command name to run
276
277 Returns:
278 target_name: Exact command name to run instead
279 extra_args: List of extra arguments to pass
280 """
281 env = dict(os.environ)
282
283 target_name = None
284 extra_args = []
285 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
286 'objcopy', 'objdump', 'dtc'):
287 target_name, *extra_args = env.get(name.upper(), '').split(' ')
288 elif name == 'c++':
289 target_name, *extra_args = env.get('CXX', '').split(' ')
290
291 if target_name:
292 return target_name, extra_args
293
294 if cross_compile is None:
295 cross_compile = env.get('CROSS_COMPILE', '')
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300296
297 if name in ('as', 'ar', 'nm', 'ldr', 'strip', 'objcopy', 'objdump'):
298 target_name = cross_compile + name
299 elif name == 'ld':
300 try:
301 if Run(cross_compile + 'ld.bfd', '-v'):
302 target_name = cross_compile + 'ld.bfd'
303 except:
304 target_name = cross_compile + 'ld'
305 elif name == 'cc':
306 target_name = cross_compile + 'gcc'
307 elif name == 'cpp':
308 target_name = cross_compile + 'gcc'
309 extra_args = ['-E']
310 elif name == 'c++':
311 target_name = cross_compile + 'g++'
312 else:
313 target_name = name
314 return target_name, extra_args
315
Simon Glassade53272022-01-09 20:13:40 -0700316def get_env_with_path():
317 """Get an updated environment with the PATH variable set correctly
318
319 If there are any search paths set, these need to come first in the PATH so
320 that these override any other version of the tools.
321
322 Returns:
323 dict: New environment with PATH updated, or None if there are not search
324 paths
325 """
326 if tool_search_paths:
327 env = dict(os.environ)
328 env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH']
329 return env
330
331def run_result(name, *args, **kwargs):
332 """Run a tool with some arguments
333
334 This runs a 'tool', which is a program used by binman to process files and
335 perhaps produce some output. Tools can be located on the PATH or in a
336 search path.
337
338 Args:
339 name: Command name to run
340 args: Arguments to the tool
341 for_host: True to resolve the command to the version for the host
342 for_target: False to run the command as-is, without resolving it
343 to the version for the compile target
344 raise_on_error: Raise an error if the command fails (True by default)
345
346 Returns:
347 CommandResult object
348 """
349 try:
350 binary = kwargs.get('binary')
351 for_host = kwargs.get('for_host', False)
352 for_target = kwargs.get('for_target', not for_host)
353 raise_on_error = kwargs.get('raise_on_error', True)
354 env = get_env_with_path()
355 if for_target:
356 name, extra_args = GetTargetCompileTool(name)
357 args = tuple(extra_args) + args
358 elif for_host:
359 name, extra_args = GetHostCompileTool(name)
360 args = tuple(extra_args) + args
361 name = os.path.expanduser(name) # Expand paths containing ~
362 all_args = (name,) + args
363 result = command.RunPipe([all_args], capture=True, capture_stderr=True,
364 env=env, raise_on_error=False, binary=binary)
365 if result.return_code:
366 if raise_on_error:
367 raise ValueError("Error %d running '%s': %s" %
368 (result.return_code,' '.join(all_args),
369 result.stderr or result.stdout))
370 return result
371 except ValueError:
372 if env and not PathHasFile(env['PATH'], name):
373 msg = "Please install tool '%s'" % name
374 package = packages.get(name)
375 if package:
376 msg += " (e.g. from package '%s')" % package
377 raise ValueError(msg)
378 raise
379
Simon Glass3b3e3c02019-10-31 07:42:50 -0600380def Run(name, *args, **kwargs):
Simon Glassc22b8cf2019-07-08 13:18:27 -0600381 """Run a tool with some arguments
382
383 This runs a 'tool', which is a program used by binman to process files and
384 perhaps produce some output. Tools can be located on the PATH or in a
385 search path.
386
387 Args:
388 name: Command name to run
389 args: Arguments to the tool
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300390 for_host: True to resolve the command to the version for the host
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300391 for_target: False to run the command as-is, without resolving it
392 to the version for the compile target
Simon Glassc22b8cf2019-07-08 13:18:27 -0600393
394 Returns:
395 CommandResult object
396 """
Simon Glassade53272022-01-09 20:13:40 -0700397 result = run_result(name, *args, **kwargs)
398 if result is not None:
Simon Glass6eace392019-08-24 07:22:42 -0600399 return result.stdout
Simon Glassaeffc5e2018-07-17 13:25:43 -0600400
401def Filename(fname):
402 """Resolve a file path to an absolute path.
403
404 If fname starts with ##/ and chroot is available, ##/ gets replaced with
405 the chroot path. If chroot is not available, this file name can not be
406 resolved, `None' is returned.
407
408 If fname is not prepended with the above prefix, and is not an existing
409 file, the actual file name is retrieved from the passed in string and the
410 search_paths directories (if any) are searched to for the file. If found -
411 the path to the found file is returned, `None' is returned otherwise.
412
413 Args:
414 fname: a string, the path to resolve.
415
416 Returns:
417 Absolute path to the file or None if not found.
418 """
419 if fname.startswith('##/'):
420 if chroot_path:
421 fname = os.path.join(chroot_path, fname[3:])
422 else:
423 return None
424
425 # Search for a pathname that exists, and return it if found
426 if fname and not os.path.exists(fname):
427 for path in search_paths:
428 pathname = os.path.join(path, os.path.basename(fname))
429 if os.path.exists(pathname):
430 return pathname
431
432 # If not found, just return the standard, unchanged path
433 return fname
434
Simon Glass3c47e412019-05-17 22:00:44 -0600435def ReadFile(fname, binary=True):
Simon Glassaeffc5e2018-07-17 13:25:43 -0600436 """Read and return the contents of a file.
437
438 Args:
439 fname: path to filename to read, where ## signifiies the chroot.
440
441 Returns:
442 data read from file, as a string.
443 """
Simon Glass3c47e412019-05-17 22:00:44 -0600444 with open(Filename(fname), binary and 'rb' or 'r') as fd:
Simon Glassaeffc5e2018-07-17 13:25:43 -0600445 data = fd.read()
446 #self._out.Info("Read file '%s' size %d (%#0x)" %
447 #(fname, len(data), len(data)))
448 return data
449
Simon Glassfd709862020-07-05 21:41:50 -0600450def WriteFile(fname, data, binary=True):
Simon Glassaeffc5e2018-07-17 13:25:43 -0600451 """Write data into a file.
452
453 Args:
454 fname: path to filename to write
455 data: data to write to file, as a string
456 """
457 #self._out.Info("Write file '%s' size %d (%#0x)" %
458 #(fname, len(data), len(data)))
Simon Glassfd709862020-07-05 21:41:50 -0600459 with open(Filename(fname), binary and 'wb' or 'w') as fd:
Simon Glassaeffc5e2018-07-17 13:25:43 -0600460 fd.write(data)
Simon Glasse6d85ff2019-05-14 15:53:47 -0600461
462def GetBytes(byte, size):
463 """Get a string of bytes of a given size
464
Simon Glasse6d85ff2019-05-14 15:53:47 -0600465 Args:
466 byte: Numeric byte value to use
467 size: Size of bytes/string to return
468
469 Returns:
470 A bytes type with 'byte' repeated 'size' times
471 """
Simon Glassfc0056e2020-11-08 20:36:18 -0700472 return bytes([byte]) * size
Simon Glass2b6ed5e2019-05-17 22:00:35 -0600473
Simon Glassf6b64812019-05-17 22:00:36 -0600474def ToBytes(string):
475 """Convert a str type into a bytes type
476
477 Args:
Simon Glass3b3e3c02019-10-31 07:42:50 -0600478 string: string to convert
Simon Glassf6b64812019-05-17 22:00:36 -0600479
480 Returns:
Simon Glassfc0056e2020-11-08 20:36:18 -0700481 A bytes type
Simon Glassf6b64812019-05-17 22:00:36 -0600482 """
Simon Glassfc0056e2020-11-08 20:36:18 -0700483 return string.encode('utf-8')
Simon Glass07d9e702019-07-08 13:18:41 -0600484
Simon Glass3b3e3c02019-10-31 07:42:50 -0600485def ToString(bval):
486 """Convert a bytes type into a str type
487
488 Args:
489 bval: bytes value to convert
490
491 Returns:
492 Python 3: A bytes type
493 Python 2: A string type
494 """
495 return bval.decode('utf-8')
496
Simon Glasseb0f4a42019-07-20 12:24:06 -0600497def Compress(indata, algo, with_header=True):
Simon Glass07d9e702019-07-08 13:18:41 -0600498 """Compress some data using a given algorithm
499
500 Note that for lzma this uses an old version of the algorithm, not that
501 provided by xz.
502
503 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
504 directory to be previously set up, by calling PrepareOutputDir().
505
Simon Glass650ead12021-07-06 10:36:36 -0600506 Care is taken to use unique temporary files so that this function can be
507 called from multiple threads.
508
Simon Glass07d9e702019-07-08 13:18:41 -0600509 Args:
510 indata: Input data to compress
511 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
512
513 Returns:
514 Compressed data
515 """
516 if algo == 'none':
517 return indata
Simon Glass650ead12021-07-06 10:36:36 -0600518 fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo,
519 dir=outdir).name
Simon Glass07d9e702019-07-08 13:18:41 -0600520 WriteFile(fname, indata)
521 if algo == 'lz4':
Simon Glass6deff872021-01-06 21:35:11 -0700522 data = Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname,
523 binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600524 # cbfstool uses a very old version of lzma
525 elif algo == 'lzma':
Simon Glass650ead12021-07-06 10:36:36 -0600526 outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo,
527 dir=outdir).name
Simon Glass07d9e702019-07-08 13:18:41 -0600528 Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8')
529 data = ReadFile(outfname)
530 elif algo == 'gzip':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600531 data = Run('gzip', '-c', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600532 else:
533 raise ValueError("Unknown algorithm '%s'" % algo)
Simon Glasseb0f4a42019-07-20 12:24:06 -0600534 if with_header:
535 hdr = struct.pack('<I', len(data))
536 data = hdr + data
Simon Glass07d9e702019-07-08 13:18:41 -0600537 return data
538
Simon Glasseb0f4a42019-07-20 12:24:06 -0600539def Decompress(indata, algo, with_header=True):
Simon Glass07d9e702019-07-08 13:18:41 -0600540 """Decompress some data using a given algorithm
541
542 Note that for lzma this uses an old version of the algorithm, not that
543 provided by xz.
544
545 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
546 directory to be previously set up, by calling PrepareOutputDir().
547
548 Args:
549 indata: Input data to decompress
550 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
551
552 Returns:
553 Compressed data
554 """
555 if algo == 'none':
556 return indata
Simon Glasseb0f4a42019-07-20 12:24:06 -0600557 if with_header:
558 data_len = struct.unpack('<I', indata[:4])[0]
559 indata = indata[4:4 + data_len]
Simon Glass07d9e702019-07-08 13:18:41 -0600560 fname = GetOutputFilename('%s.decomp.tmp' % algo)
561 with open(fname, 'wb') as fd:
562 fd.write(indata)
563 if algo == 'lz4':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600564 data = Run('lz4', '-dc', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600565 elif algo == 'lzma':
566 outfname = GetOutputFilename('%s.decomp.otmp' % algo)
567 Run('lzma_alone', 'd', fname, outfname)
Simon Glass3b3e3c02019-10-31 07:42:50 -0600568 data = ReadFile(outfname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600569 elif algo == 'gzip':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600570 data = Run('gzip', '-cd', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600571 else:
572 raise ValueError("Unknown algorithm '%s'" % algo)
573 return data
Simon Glass1cfdfc02019-07-08 13:18:51 -0600574
575CMD_CREATE, CMD_DELETE, CMD_ADD, CMD_REPLACE, CMD_EXTRACT = range(5)
576
577IFWITOOL_CMDS = {
578 CMD_CREATE: 'create',
579 CMD_DELETE: 'delete',
580 CMD_ADD: 'add',
581 CMD_REPLACE: 'replace',
582 CMD_EXTRACT: 'extract',
583 }
584
585def RunIfwiTool(ifwi_file, cmd, fname=None, subpart=None, entry_name=None):
586 """Run ifwitool with the given arguments:
587
588 Args:
589 ifwi_file: IFWI file to operation on
590 cmd: Command to execute (CMD_...)
591 fname: Filename of file to add/replace/extract/create (None for
592 CMD_DELETE)
593 subpart: Name of sub-partition to operation on (None for CMD_CREATE)
594 entry_name: Name of directory entry to operate on, or None if none
595 """
596 args = ['ifwitool', ifwi_file]
597 args.append(IFWITOOL_CMDS[cmd])
598 if fname:
599 args += ['-f', fname]
600 if subpart:
601 args += ['-n', subpart]
602 if entry_name:
603 args += ['-d', '-e', entry_name]
604 Run(*args)
Simon Glass9f297b02019-07-20 12:23:36 -0600605
606def ToHex(val):
607 """Convert an integer value (or None) to a string
608
609 Returns:
610 hex value, or 'None' if the value is None
611 """
612 return 'None' if val is None else '%#x' % val
613
614def ToHexSize(val):
615 """Return the size of an object in hex
616
617 Returns:
618 hex value of size, or 'None' if the value is None
619 """
620 return 'None' if val is None else '%#x' % len(val)
Paul Barker5fe50f92021-09-08 12:38:01 +0100621
622def PrintFullHelp(fname):
623 """Print the full help message for a tool using an appropriate pager.
624
625 Args:
626 fname: Path to a file containing the full help message
627 """
Paul Barker0d60e5d2021-09-08 12:38:02 +0100628 pager = shlex.split(os.getenv('PAGER', ''))
Paul Barker5fe50f92021-09-08 12:38:01 +0100629 if not pager:
Paul Barker0d60e5d2021-09-08 12:38:02 +0100630 lesspath = shutil.which('less')
631 pager = [lesspath] if lesspath else None
Paul Barker5fe50f92021-09-08 12:38:01 +0100632 if not pager:
Paul Barker0d60e5d2021-09-08 12:38:02 +0100633 pager = ['more']
634 command.Run(*pager, fname)