blob: 877e37cd8da1b25d9899e753012e07e2737fb6b8 [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
8import shutil
Simon Glasseb0f4a42019-07-20 12:24:06 -06009import struct
Simon Glasse6d85ff2019-05-14 15:53:47 -060010import sys
Simon Glass1f1864b2016-07-25 18:59:08 -060011import tempfile
12
Simon Glassbf776672020-04-17 18:09:04 -060013from patman import command
14from patman import tout
Simon Glass1f1864b2016-07-25 18:59:08 -060015
Simon Glassaeffc5e2018-07-17 13:25:43 -060016# Output directly (generally this is temporary)
Simon Glass1f1864b2016-07-25 18:59:08 -060017outdir = None
Simon Glassaeffc5e2018-07-17 13:25:43 -060018
19# True to keep the output directory around after exiting
Simon Glass1f1864b2016-07-25 18:59:08 -060020preserve_outdir = False
21
Simon Glassaeffc5e2018-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 Glassc22b8cf2019-07-08 13:18:27 -060028tool_search_paths = []
29
Simon Glass04187a82018-09-14 04:57:25 -060030# Tools and the packages that contain them, on debian
31packages = {
32 'lz4': 'liblz4-tool',
33 }
Simon Glassaeffc5e2018-07-17 13:25:43 -060034
Simon Glass1fda1822018-10-01 21:12:44 -060035# List of paths to use when looking for an input file
36indir = []
37
Simon Glass1f1864b2016-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 Glass31353302019-07-20 12:24:07 -060084 outdir = None
Simon Glass1f1864b2016-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
Simon Glass10cbd3b2020-12-28 20:34:52 -070097def GetOutputDir():
98 """Return the current output directory
99
100 Returns:
101 str: The output directory
102 """
103 return outdir
104
Simon Glass1f1864b2016-07-25 18:59:08 -0600105def _FinaliseForTest():
106 """Remove the output directory (for use by tests)"""
107 global outdir
108
109 if outdir:
110 _RemoveOutputDir()
Simon Glass31353302019-07-20 12:24:07 -0600111 outdir = None
Simon Glass1f1864b2016-07-25 18:59:08 -0600112
113def SetInputDirs(dirname):
114 """Add a list of input directories, where input files are kept.
115
116 Args:
117 dirname: a list of paths to input directories to use for obtaining
118 files needed by binman to place in the image.
119 """
120 global indir
121
122 indir = dirname
123 tout.Debug("Using input directories %s" % indir)
124
Simon Glass4f9f1052020-07-09 18:39:38 -0600125def GetInputFilename(fname, allow_missing=False):
Simon Glass1f1864b2016-07-25 18:59:08 -0600126 """Return a filename for use as input.
127
128 Args:
129 fname: Filename to use for new file
Simon Glass4f9f1052020-07-09 18:39:38 -0600130 allow_missing: True if the filename can be missing
Simon Glass1f1864b2016-07-25 18:59:08 -0600131
132 Returns:
Simon Glass5187b802021-03-18 20:25:03 +1300133 fname, if indir is None;
134 full path of the filename, within the input directory;
135 None, if file is missing and allow_missing is True
136
137 Raises:
138 ValueError if file is missing and allow_missing is False
Simon Glass1f1864b2016-07-25 18:59:08 -0600139 """
Simon Glassf514d8f2019-08-24 07:22:54 -0600140 if not indir or fname[:1] == '/':
Simon Glass1f1864b2016-07-25 18:59:08 -0600141 return fname
142 for dirname in indir:
143 pathname = os.path.join(dirname, fname)
144 if os.path.exists(pathname):
145 return pathname
146
Simon Glass4f9f1052020-07-09 18:39:38 -0600147 if allow_missing:
148 return None
Simon Glass4f5dea42018-07-17 13:25:45 -0600149 raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
150 (fname, ','.join(indir), os.getcwd()))
Simon Glass1f1864b2016-07-25 18:59:08 -0600151
Simon Glass0a98b282018-09-14 04:57:28 -0600152def GetInputFilenameGlob(pattern):
153 """Return a list of filenames for use as input.
154
155 Args:
156 pattern: Filename pattern to search for
157
158 Returns:
159 A list of matching files in all input directories
160 """
161 if not indir:
162 return glob.glob(fname)
163 files = []
164 for dirname in indir:
165 pathname = os.path.join(dirname, pattern)
166 files += glob.glob(pathname)
167 return sorted(files)
168
Simon Glass1f1864b2016-07-25 18:59:08 -0600169def Align(pos, align):
170 if align:
171 mask = align - 1
172 pos = (pos + mask) & ~mask
173 return pos
174
175def NotPowerOfTwo(num):
176 return num and (num & (num - 1))
Simon Glassaeffc5e2018-07-17 13:25:43 -0600177
Simon Glassc22b8cf2019-07-08 13:18:27 -0600178def SetToolPaths(toolpaths):
179 """Set the path to search for tools
180
181 Args:
182 toolpaths: List of paths to search for tools executed by Run()
183 """
184 global tool_search_paths
185
186 tool_search_paths = toolpaths
187
188def PathHasFile(path_spec, fname):
Simon Glass04187a82018-09-14 04:57:25 -0600189 """Check if a given filename is in the PATH
190
191 Args:
Simon Glassc22b8cf2019-07-08 13:18:27 -0600192 path_spec: Value of PATH variable to check
Simon Glass04187a82018-09-14 04:57:25 -0600193 fname: Filename to check
194
195 Returns:
196 True if found, False if not
197 """
Simon Glassc22b8cf2019-07-08 13:18:27 -0600198 for dir in path_spec.split(':'):
Simon Glass04187a82018-09-14 04:57:25 -0600199 if os.path.exists(os.path.join(dir, fname)):
200 return True
201 return False
202
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300203def GetHostCompileTool(name):
204 """Get the host-specific version for a compile tool
205
206 This checks the environment variables that specify which version of
207 the tool should be used (e.g. ${HOSTCC}).
208
209 The following table lists the host-specific versions of the tools
210 this function resolves to:
211
212 Compile Tool | Host version
213 --------------+----------------
214 as | ${HOSTAS}
215 ld | ${HOSTLD}
216 cc | ${HOSTCC}
217 cpp | ${HOSTCPP}
218 c++ | ${HOSTCXX}
219 ar | ${HOSTAR}
220 nm | ${HOSTNM}
221 ldr | ${HOSTLDR}
222 strip | ${HOSTSTRIP}
223 objcopy | ${HOSTOBJCOPY}
224 objdump | ${HOSTOBJDUMP}
225 dtc | ${HOSTDTC}
226
227 Args:
228 name: Command name to run
229
230 Returns:
231 host_name: Exact command name to run instead
232 extra_args: List of extra arguments to pass
233 """
234 host_name = None
235 extra_args = []
236 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
237 'objcopy', 'objdump', 'dtc'):
238 host_name, *host_args = env.get('HOST' + name.upper(), '').split(' ')
239 elif name == 'c++':
240 host_name, *host_args = env.get('HOSTCXX', '').split(' ')
241
242 if host_name:
243 return host_name, extra_args
244 return name, []
245
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300246def GetTargetCompileTool(name, cross_compile=None):
247 """Get the target-specific version for a compile tool
248
249 This first checks the environment variables that specify which
250 version of the tool should be used (e.g. ${CC}). If those aren't
251 specified, it checks the CROSS_COMPILE variable as a prefix for the
252 tool with some substitutions (e.g. "${CROSS_COMPILE}gcc" for cc).
253
254 The following table lists the target-specific versions of the tools
255 this function resolves to:
256
257 Compile Tool | First choice | Second choice
258 --------------+----------------+----------------------------
259 as | ${AS} | ${CROSS_COMPILE}as
260 ld | ${LD} | ${CROSS_COMPILE}ld.bfd
261 | | or ${CROSS_COMPILE}ld
262 cc | ${CC} | ${CROSS_COMPILE}gcc
263 cpp | ${CPP} | ${CROSS_COMPILE}gcc -E
264 c++ | ${CXX} | ${CROSS_COMPILE}g++
265 ar | ${AR} | ${CROSS_COMPILE}ar
266 nm | ${NM} | ${CROSS_COMPILE}nm
267 ldr | ${LDR} | ${CROSS_COMPILE}ldr
268 strip | ${STRIP} | ${CROSS_COMPILE}strip
269 objcopy | ${OBJCOPY} | ${CROSS_COMPILE}objcopy
270 objdump | ${OBJDUMP} | ${CROSS_COMPILE}objdump
271 dtc | ${DTC} | (no CROSS_COMPILE version)
272
273 Args:
274 name: Command name to run
275
276 Returns:
277 target_name: Exact command name to run instead
278 extra_args: List of extra arguments to pass
279 """
280 env = dict(os.environ)
281
282 target_name = None
283 extra_args = []
284 if name in ('as', 'ld', 'cc', 'cpp', 'ar', 'nm', 'ldr', 'strip',
285 'objcopy', 'objdump', 'dtc'):
286 target_name, *extra_args = env.get(name.upper(), '').split(' ')
287 elif name == 'c++':
288 target_name, *extra_args = env.get('CXX', '').split(' ')
289
290 if target_name:
291 return target_name, extra_args
292
293 if cross_compile is None:
294 cross_compile = env.get('CROSS_COMPILE', '')
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300295
296 if name in ('as', 'ar', 'nm', 'ldr', 'strip', 'objcopy', 'objdump'):
297 target_name = cross_compile + name
298 elif name == 'ld':
299 try:
300 if Run(cross_compile + 'ld.bfd', '-v'):
301 target_name = cross_compile + 'ld.bfd'
302 except:
303 target_name = cross_compile + 'ld'
304 elif name == 'cc':
305 target_name = cross_compile + 'gcc'
306 elif name == 'cpp':
307 target_name = cross_compile + 'gcc'
308 extra_args = ['-E']
309 elif name == 'c++':
310 target_name = cross_compile + 'g++'
311 else:
312 target_name = name
313 return target_name, extra_args
314
Simon Glass3b3e3c02019-10-31 07:42:50 -0600315def Run(name, *args, **kwargs):
Simon Glassc22b8cf2019-07-08 13:18:27 -0600316 """Run a tool with some arguments
317
318 This runs a 'tool', which is a program used by binman to process files and
319 perhaps produce some output. Tools can be located on the PATH or in a
320 search path.
321
322 Args:
323 name: Command name to run
324 args: Arguments to the tool
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300325 for_host: True to resolve the command to the version for the host
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300326 for_target: False to run the command as-is, without resolving it
327 to the version for the compile target
Simon Glassc22b8cf2019-07-08 13:18:27 -0600328
329 Returns:
330 CommandResult object
331 """
Simon Glass04187a82018-09-14 04:57:25 -0600332 try:
Simon Glass3b3e3c02019-10-31 07:42:50 -0600333 binary = kwargs.get('binary')
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300334 for_host = kwargs.get('for_host', False)
335 for_target = kwargs.get('for_target', not for_host)
Simon Glassc22b8cf2019-07-08 13:18:27 -0600336 env = None
337 if tool_search_paths:
338 env = dict(os.environ)
339 env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH']
Alper Nebi Yasak1e4687a2020-09-06 14:46:05 +0300340 if for_target:
341 name, extra_args = GetTargetCompileTool(name)
342 args = tuple(extra_args) + args
Alper Nebi Yasak29cc0912020-09-06 14:46:06 +0300343 elif for_host:
344 name, extra_args = GetHostCompileTool(name)
345 args = tuple(extra_args) + args
Simon Glassf31e83d2020-11-09 07:45:02 -0700346 name = os.path.expanduser(name) # Expand paths containing ~
Simon Glass6eace392019-08-24 07:22:42 -0600347 all_args = (name,) + args
348 result = command.RunPipe([all_args], capture=True, capture_stderr=True,
Simon Glass3b3e3c02019-10-31 07:42:50 -0600349 env=env, raise_on_error=False, binary=binary)
Simon Glass6eace392019-08-24 07:22:42 -0600350 if result.return_code:
351 raise Exception("Error %d running '%s': %s" %
352 (result.return_code,' '.join(all_args),
353 result.stderr))
354 return result.stdout
Simon Glass04187a82018-09-14 04:57:25 -0600355 except:
Simon Glassc22b8cf2019-07-08 13:18:27 -0600356 if env and not PathHasFile(env['PATH'], name):
357 msg = "Please install tool '%s'" % name
Simon Glass04187a82018-09-14 04:57:25 -0600358 package = packages.get(name)
359 if package:
360 msg += " (e.g. from package '%s')" % package
361 raise ValueError(msg)
362 raise
Simon Glassaeffc5e2018-07-17 13:25:43 -0600363
364def Filename(fname):
365 """Resolve a file path to an absolute path.
366
367 If fname starts with ##/ and chroot is available, ##/ gets replaced with
368 the chroot path. If chroot is not available, this file name can not be
369 resolved, `None' is returned.
370
371 If fname is not prepended with the above prefix, and is not an existing
372 file, the actual file name is retrieved from the passed in string and the
373 search_paths directories (if any) are searched to for the file. If found -
374 the path to the found file is returned, `None' is returned otherwise.
375
376 Args:
377 fname: a string, the path to resolve.
378
379 Returns:
380 Absolute path to the file or None if not found.
381 """
382 if fname.startswith('##/'):
383 if chroot_path:
384 fname = os.path.join(chroot_path, fname[3:])
385 else:
386 return None
387
388 # Search for a pathname that exists, and return it if found
389 if fname and not os.path.exists(fname):
390 for path in search_paths:
391 pathname = os.path.join(path, os.path.basename(fname))
392 if os.path.exists(pathname):
393 return pathname
394
395 # If not found, just return the standard, unchanged path
396 return fname
397
Simon Glass3c47e412019-05-17 22:00:44 -0600398def ReadFile(fname, binary=True):
Simon Glassaeffc5e2018-07-17 13:25:43 -0600399 """Read and return the contents of a file.
400
401 Args:
402 fname: path to filename to read, where ## signifiies the chroot.
403
404 Returns:
405 data read from file, as a string.
406 """
Simon Glass3c47e412019-05-17 22:00:44 -0600407 with open(Filename(fname), binary and 'rb' or 'r') as fd:
Simon Glassaeffc5e2018-07-17 13:25:43 -0600408 data = fd.read()
409 #self._out.Info("Read file '%s' size %d (%#0x)" %
410 #(fname, len(data), len(data)))
411 return data
412
Simon Glassfd709862020-07-05 21:41:50 -0600413def WriteFile(fname, data, binary=True):
Simon Glassaeffc5e2018-07-17 13:25:43 -0600414 """Write data into a file.
415
416 Args:
417 fname: path to filename to write
418 data: data to write to file, as a string
419 """
420 #self._out.Info("Write file '%s' size %d (%#0x)" %
421 #(fname, len(data), len(data)))
Simon Glassfd709862020-07-05 21:41:50 -0600422 with open(Filename(fname), binary and 'wb' or 'w') as fd:
Simon Glassaeffc5e2018-07-17 13:25:43 -0600423 fd.write(data)
Simon Glasse6d85ff2019-05-14 15:53:47 -0600424
425def GetBytes(byte, size):
426 """Get a string of bytes of a given size
427
Simon Glasse6d85ff2019-05-14 15:53:47 -0600428 Args:
429 byte: Numeric byte value to use
430 size: Size of bytes/string to return
431
432 Returns:
433 A bytes type with 'byte' repeated 'size' times
434 """
Simon Glassfc0056e2020-11-08 20:36:18 -0700435 return bytes([byte]) * size
Simon Glass2b6ed5e2019-05-17 22:00:35 -0600436
Simon Glassf6b64812019-05-17 22:00:36 -0600437def ToBytes(string):
438 """Convert a str type into a bytes type
439
440 Args:
Simon Glass3b3e3c02019-10-31 07:42:50 -0600441 string: string to convert
Simon Glassf6b64812019-05-17 22:00:36 -0600442
443 Returns:
Simon Glassfc0056e2020-11-08 20:36:18 -0700444 A bytes type
Simon Glassf6b64812019-05-17 22:00:36 -0600445 """
Simon Glassfc0056e2020-11-08 20:36:18 -0700446 return string.encode('utf-8')
Simon Glass07d9e702019-07-08 13:18:41 -0600447
Simon Glass3b3e3c02019-10-31 07:42:50 -0600448def ToString(bval):
449 """Convert a bytes type into a str type
450
451 Args:
452 bval: bytes value to convert
453
454 Returns:
455 Python 3: A bytes type
456 Python 2: A string type
457 """
458 return bval.decode('utf-8')
459
Simon Glasseb0f4a42019-07-20 12:24:06 -0600460def Compress(indata, algo, with_header=True):
Simon Glass07d9e702019-07-08 13:18:41 -0600461 """Compress some data using a given algorithm
462
463 Note that for lzma this uses an old version of the algorithm, not that
464 provided by xz.
465
466 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
467 directory to be previously set up, by calling PrepareOutputDir().
468
Simon Glass650ead12021-07-06 10:36:36 -0600469 Care is taken to use unique temporary files so that this function can be
470 called from multiple threads.
471
Simon Glass07d9e702019-07-08 13:18:41 -0600472 Args:
473 indata: Input data to compress
474 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
475
476 Returns:
477 Compressed data
478 """
479 if algo == 'none':
480 return indata
Simon Glass650ead12021-07-06 10:36:36 -0600481 fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo,
482 dir=outdir).name
Simon Glass07d9e702019-07-08 13:18:41 -0600483 WriteFile(fname, indata)
484 if algo == 'lz4':
Simon Glass6deff872021-01-06 21:35:11 -0700485 data = Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname,
486 binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600487 # cbfstool uses a very old version of lzma
488 elif algo == 'lzma':
Simon Glass650ead12021-07-06 10:36:36 -0600489 outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo,
490 dir=outdir).name
Simon Glass07d9e702019-07-08 13:18:41 -0600491 Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8')
492 data = ReadFile(outfname)
493 elif algo == 'gzip':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600494 data = Run('gzip', '-c', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600495 else:
496 raise ValueError("Unknown algorithm '%s'" % algo)
Simon Glasseb0f4a42019-07-20 12:24:06 -0600497 if with_header:
498 hdr = struct.pack('<I', len(data))
499 data = hdr + data
Simon Glass07d9e702019-07-08 13:18:41 -0600500 return data
501
Simon Glasseb0f4a42019-07-20 12:24:06 -0600502def Decompress(indata, algo, with_header=True):
Simon Glass07d9e702019-07-08 13:18:41 -0600503 """Decompress some data using a given algorithm
504
505 Note that for lzma this uses an old version of the algorithm, not that
506 provided by xz.
507
508 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
509 directory to be previously set up, by calling PrepareOutputDir().
510
511 Args:
512 indata: Input data to decompress
513 algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
514
515 Returns:
516 Compressed data
517 """
518 if algo == 'none':
519 return indata
Simon Glasseb0f4a42019-07-20 12:24:06 -0600520 if with_header:
521 data_len = struct.unpack('<I', indata[:4])[0]
522 indata = indata[4:4 + data_len]
Simon Glass07d9e702019-07-08 13:18:41 -0600523 fname = GetOutputFilename('%s.decomp.tmp' % algo)
524 with open(fname, 'wb') as fd:
525 fd.write(indata)
526 if algo == 'lz4':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600527 data = Run('lz4', '-dc', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600528 elif algo == 'lzma':
529 outfname = GetOutputFilename('%s.decomp.otmp' % algo)
530 Run('lzma_alone', 'd', fname, outfname)
Simon Glass3b3e3c02019-10-31 07:42:50 -0600531 data = ReadFile(outfname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600532 elif algo == 'gzip':
Simon Glass3b3e3c02019-10-31 07:42:50 -0600533 data = Run('gzip', '-cd', fname, binary=True)
Simon Glass07d9e702019-07-08 13:18:41 -0600534 else:
535 raise ValueError("Unknown algorithm '%s'" % algo)
536 return data
Simon Glass1cfdfc02019-07-08 13:18:51 -0600537
538CMD_CREATE, CMD_DELETE, CMD_ADD, CMD_REPLACE, CMD_EXTRACT = range(5)
539
540IFWITOOL_CMDS = {
541 CMD_CREATE: 'create',
542 CMD_DELETE: 'delete',
543 CMD_ADD: 'add',
544 CMD_REPLACE: 'replace',
545 CMD_EXTRACT: 'extract',
546 }
547
548def RunIfwiTool(ifwi_file, cmd, fname=None, subpart=None, entry_name=None):
549 """Run ifwitool with the given arguments:
550
551 Args:
552 ifwi_file: IFWI file to operation on
553 cmd: Command to execute (CMD_...)
554 fname: Filename of file to add/replace/extract/create (None for
555 CMD_DELETE)
556 subpart: Name of sub-partition to operation on (None for CMD_CREATE)
557 entry_name: Name of directory entry to operate on, or None if none
558 """
559 args = ['ifwitool', ifwi_file]
560 args.append(IFWITOOL_CMDS[cmd])
561 if fname:
562 args += ['-f', fname]
563 if subpart:
564 args += ['-n', subpart]
565 if entry_name:
566 args += ['-d', '-e', entry_name]
567 Run(*args)
Simon Glass9f297b02019-07-20 12:23:36 -0600568
569def ToHex(val):
570 """Convert an integer value (or None) to a string
571
572 Returns:
573 hex value, or 'None' if the value is None
574 """
575 return 'None' if val is None else '%#x' % val
576
577def ToHexSize(val):
578 """Return the size of an object in hex
579
580 Returns:
581 hex value of size, or 'None' if the value is None
582 """
583 return 'None' if val is None else '%#x' % len(val)