summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2019-05-30 20:37:50 +1000
committerChris Johns <chrisj@rtems.org>2019-05-30 20:37:50 +1000
commite5716fdf3fdf883b918a06fd149015a932411265 (patch)
treef8e19f9b4c9a32232ad039b2ed60e95592536931
parente2be6a99192dcfaa6ebf548c7c78e6071aa53167 (diff)
misc/boot-image: Add a tool to create boot images.
-rwxr-xr-xmisc/rtems-boot-image42
-rw-r--r--misc/tools/boot.py670
-rwxr-xr-xmisc/tools/cmd-boot-image.py44
3 files changed, 756 insertions, 0 deletions
diff --git a/misc/rtems-boot-image b/misc/rtems-boot-image
new file mode 100755
index 0000000..aa23b2e
--- /dev/null
+++ b/misc/rtems-boot-image
@@ -0,0 +1,42 @@
+#! /bin/sh
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2019 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+set -e
+base=$(dirname $(dirname $0))
+cmd=misc/tools/cmd-boot-image.py
+PYTHON_WRAPPER=rtemstoolkit/python-wrapper.sh
+if test -f ${base}/${PYTHON_WRAPPER}; then
+ PYTHON_CMD=${base}/${cmd}
+ . ${base}/${PYTHON_WRAPPER}
+elif test -f ${base}/share/rtems/${PYTHON_WRAPPER}; then
+ PYTHON_CMD=${base}/share/rtems/${cmd}
+ . ${base}/share/rtems/${PYTHON_WRAPPER}
+fi
+echo "error: RTEMS Toolkit python wrapper not found, please report"
diff --git a/misc/tools/boot.py b/misc/tools/boot.py
new file mode 100644
index 0000000..3fad3c4
--- /dev/null
+++ b/misc/tools/boot.py
@@ -0,0 +1,670 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2019 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# This code builds a bootloader image for an SD card for a range of
+# boards on a range of hosts.
+#
+
+from __future__ import print_function
+
+import argparse
+import copy
+import datetime
+import os
+import sys
+
+from rtemstoolkit import check
+from rtemstoolkit import error
+from rtemstoolkit import execute
+from rtemstoolkit import host
+from rtemstoolkit import log
+from rtemstoolkit import path
+from rtemstoolkit import version
+
+class settings(object):
+ def __init__(self, bootloader = 'none', base = None):
+ self.build = 'build'
+ self.bootloader = bootloader
+ self.base = base
+ self.image_size = '64m'
+ self.part_type = 'MBR'
+ self.fs_format = 'fat16'
+ self.fs_size = '63m'
+ self.fs_alignment = '1m'
+ self.first_stage = None
+ self.second_stage = None
+ self.output = None
+ self.kernel = None
+ self.net_server_ip = None
+ self.net_ip = None
+ self.files = []
+ self.config = None
+ self.clean = True
+
+ def config_find(self, key, raw = False):
+ if key not in self.config:
+ raise error.general('config item not found: %s' % (key))
+ item = self.config[key]
+ if not raw:
+ if item == 'y':
+ item = True
+ elif item == 'n':
+ item = False
+ elif item[0] == '"' and item[-1] == '"':
+ item = item[1:-1]
+ return item
+
+ def bootloader_file(self, name):
+ return path.join(self.base, name)
+
+ def log(self):
+ log.output('Settings')
+ log.output(' Image: {0}'.format(self.output))
+ log.output(' Bootloader: {0}'.format(self.bootloader))
+ log.output(' Base: {0}'.format(self.base))
+ log.output(' Image Size: {0}'.format(self.image_size))
+ log.output(' Part Type: {0}'.format(self.part_type))
+ log.output(' FS Format: {0}'.format(self.fs_format))
+ log.output(' FS Size: {0}'.format(self.fs_size))
+ log.output(' FS Align: {0}'.format(self.fs_alignment))
+ log.output(' First stage: {0}'.format(self.first_stage))
+ log.output(' Second stage: {0}'.format(self.second_stage))
+ log.output(' Kernel: {0}'.format(self.kernel))
+ log.output(' Net Server IP: {0}'.format(self.net_server_ip))
+ log.output(' Net IP: {0}'.format(self.net_ip))
+ log.output(' Files: {0}'.format(len(self.files)))
+ for f in self.files:
+ log.output(' : {0}'.format(f))
+ if self.config is not None:
+ log.output(' Config: {0}'.format(len(self.config)))
+ for c in self.config:
+ log.output(' : {0} = {1}'.format(c, self.config[c]))
+
+ def install_configuration(self, image, dst):
+ pass
+
+class uboot_settings(settings):
+
+ boards = {
+ 'arm-ti-am335x-am335x_evm' : {
+ 'SPL' : 'MLO',
+ 'IMG' : 'u-boot.img',
+ 'RTEMS-LOADADDR' : '0x82000000'
+ },
+ 'arm-xilinx-zynq-zynq-common' : {
+ 'SPL' : 'spl/boot.bin',
+ 'IMG' : 'u-boot.img',
+ 'RTEMS-LOADADDR' : '0x02000000'
+ },
+ }
+
+ templates = {
+ 'rtems-kernel': [
+ 'loadaddr=@RTEMS-LOADADDR@',
+ 'bootfile=@KERNE@'
+ ]
+ }
+
+ def __init__(self, uboot):
+ super(uboot_settings, self).__init__('u-boot', uboot)
+ self._config_load(self.bootloader_file('.config'))
+ if self.board_name() not in self.boards:
+ raise error.general('board not found: %s' %(self.board_name()))
+ log.output('Board: %s' % (self.board_name()))
+ board = self.boards[self.board_name()]
+ self.first_stage = self.bootloader_file(board['SPL'])
+ self.second_stage = self.bootloader_file(board['IMG'])
+
+ def _config_load(self, config):
+ if not path.exists(config):
+ raise error.general('could not find u-boot\'s config: %s' %(config))
+ with open(config, 'r') as f:
+ items = f.readlines()
+ self.config_path = config
+ items = [l[:l.find('#')] for l in items]
+ items = [l for l in items if len(l) > 0 or len(l) > 0 and l[0] != os.linesep]
+ self.config = {}
+ for c in items:
+ cs = c.split('=')
+ self.config[cs[0].strip()] = cs[1].strip()
+
+ def board_name(self):
+ return '%s-%s-%s-%s' % (self.config_find('CONFIG_SYS_ARCH'),
+ self.config_find('CONFIG_SYS_VENDOR'),
+ self.config_find('CONFIG_SYS_BOARD'),
+ self.config_find('CONFIG_SYS_CONFIG_NAME'))
+
+ def install_configuration(self, image, dst):
+ pass
+
+class image(object):
+
+ def __init__(self, settings):
+ self.settings = settings
+ self.detach_images = []
+ self.unmount_paths = []
+ self.remove_paths = []
+
+ def build(self):
+ #
+ # Cleanup if any goes wrong.
+ #
+ try:
+ #
+ # Ge the absolute paths to fixed locations.
+ #
+ output = path.abspath(self.settings.output)
+ build = path.abspath(self.settings.build)
+ mountpoint = path.join(build, 'mnt')
+
+ #
+ # Create any paths we need. They are removed when this object is
+ # deleted.
+ #
+ self.create_path(build)
+ self.create_path(mountpoint)
+
+ #
+ # Create the blank image file. This is attached as a device,
+ # partitioned, formatted and the files written to it.
+ #
+ log.notice('Create image: %s' % (self.settings.output))
+ img_size, img_units = self.si_size_units(self.settings.image_size)
+ self.command('dd if=/dev/zero of=%s bs=1%s count=%d' % (output,
+ img_units,
+ img_size))
+
+ #
+ # Attach the image so it is a device.
+ #
+ log.notice('Attach image to device: %s' % (self.settings.output))
+ device = self.image_attach(output)
+
+ #
+ # Partition the image.
+ #
+ log.notice('Partition device: %s as %s' % (device, self.settings.part_type))
+ self.partition(device,
+ self.settings.part_type,
+ self.settings.fs_format,
+ self.settings.fs_size,
+ self.settings.fs_alignment)
+
+ part = self.device_partition(device, 1)
+
+ #
+ # Format the first partition.
+ #
+ log.notice('Format: %s as %s' % (part, self.settings.fs_format))
+ self.format_partition(part, self.settings.fs_format)
+
+ #
+ # Mount the file system.
+ #
+ log.notice('Mount: %s' % (part))
+ self.mount(self.settings.fs_format, part, mountpoint)
+
+ #
+ # Install the first stage and second stage boot loaders.
+ #
+ self.install(self.settings.first_stage, mountpoint)
+ self.install(self.settings.second_stage, mountpoint)
+
+ #
+ # Install a kernel if present
+ #
+ if self.settings.kernel is not None:
+ self.install(self.settings.kernel, mountpoint)
+ #
+ # Install the bootloader configuration.
+ #
+ self.settings.install_configuration(self, mountpoint)
+
+ #
+ # Install any user files if present.
+ #
+ for f in self.settings.files:
+ self.install(f, mountpoint)
+
+ #
+ # Done.
+ #
+ log.notice('Finished')
+ finally:
+ self.cleanup()
+
+ def install(self, src, dst):
+ src_base = path.basename(src)
+ log.notice('Install: %s' % (src_base))
+ asrc = path.abspath(src)
+ adst = path.join(path.abspath(dst), src_base)
+ log.output('Copy : %s -> %s' % (asrc, adst))
+ path.copy(asrc, adst)
+
+ def image_attach(self, path_):
+ device = self.host_image_attach(path_)
+ self.detach_images += [device]
+ return device
+
+ def image_detach(self, device):
+ if device in self.detach_images:
+ self.detach_images.remove(device)
+ self.host_image_detach(device)
+
+ def partition(self, device, ptype, pformat, psize, palign):
+ self.host_partition(device, ptype, pformat, psize, palign)
+
+ def format_partition(self, device, pformat):
+ self.host_format_partition(device, pformat)
+
+ def device_partition(self, device, pindex):
+ return self.host_device_partition(device, pindex)
+
+ def mount(self, pformat, device, path_):
+ if path_ not in self.unmount_paths:
+ self.host_mount(pformat, device, path_)
+ self.unmount_paths += [path_]
+
+ def unmount(self, path_):
+ if path_ in self.unmount_paths:
+ self.host_unmount(path_)
+ self.unmount_paths.remove(path_)
+
+ def cleanup(self):
+ log.notice('Cleanup')
+ for m in self.unmount_paths:
+ log.output('unmount: %s' % (m))
+ self.unmount(m)
+ for i in self.detach_images:
+ log.output('detach: %s' % (i))
+ self.image_detach(i)
+ for r in self.remove_paths:
+ log.output('remove: %s' % (r))
+ path.removeall(r)
+
+ def get_exes(self):
+ return ['dd',
+ 'mkimage']
+
+ def check_exes(self):
+ ok = True
+ for exe in self.get_exes():
+ log.output('check exe: %s' % (exe))
+ if not check.check_exe(None, exe):
+ log.notice('host executable not found: %s' % (exe))
+ ok = False
+ if not ok:
+ raise error.general('missing commands; please fix.')
+
+ def si_parse_size(self, size):
+ orig = size
+ units = 1
+ suffix = ''
+ if size[-1].isalpha():
+ suffix = size[-1]
+ if suffix not in ['k', 'm', 'g']:
+ raise error.general('invalid SI units (k, m, g): %s: %s' % (suffix,
+ size))
+ units = {'k': 1024, 'm': 1024 * 1024, 'g': 1024 * 1024 * 1024 }[suffix]
+ size = size[:-1]
+ if not size.isdecimal():
+ raise error.general('invalid size: %s' % (orig))
+ size = int(size)
+ return size, suffix, size * units
+
+ def si_size(self, size):
+ si_s, si_u, size = self.si_parse_size(size)
+ return size
+
+ def si_size_units(self, size):
+ si_s, si_u, size = self.si_parse_size(size)
+ return si_s, si_u
+
+ def create_path(self, where, recreate = True, cleanup = True):
+ if path.exists(where):
+ log.output('remove: %s' % (where))
+ path.removeall(where)
+ try:
+ log.output('make: %s' % (where))
+ path.mkdir(where)
+ except:
+ raise error.general('cannot create build path: %s' % (where))
+ if not path.isreadable(where):
+ raise error.general('build path is not readable: %s' % (where))
+ if not path.iswritable(where):
+ raise error.general('build path is not writeable: %s' % (where))
+ if cleanup and self.settings.clean:
+ self.remove_paths += [where]
+
+ def command(self, cmd):
+ e = execute.capture_execution()
+ build = path.abspath(self.settings.build)
+ log.output('>> cwd: %s' % (build))
+ log.output('> %s' % (cmd))
+ exit_code, proc, output = e.shell(cmd, cwd = path.host(build))
+ log.output(['> ' + l for l in output.split(os.linesep)])
+ log.output('> exit: %d' % (exit_code))
+ if exit_code != 0:
+ raise error.general('executing failure: (exit:%d) %s' % (exit_code, cmd))
+ return output
+
+class freebsd_image(image):
+ def __init__(self, settings):
+ super(freebsd_image, self).__init__(settings)
+
+ def get_exes(self):
+ exes = super(freebsd_image, self).get_exes()
+ return exes + ['mdconfig',
+ 'gpart',
+ 'newfs_msdos',
+ 'mount',
+ 'umount']
+
+ def host_image_attach(self, path_):
+ return self.command('sudo mdconfig -f %s' % (path_))
+
+ def host_image_detach(self, device):
+ self.command('sudo mdconfig -d -u %s' % (device))
+
+ def host_partition(self, device, ptype, pformat, psize, palign):
+ types = { 'MBR': 'MBR' }
+ formats = { 'fat16': 'fat16',
+ 'fat32': 'fat32' }
+ if ptype not in types:
+ raise error.general('unknown type of partitioning: %s' % (ptype))
+ if pformat not in formats:
+ raise error.general('unknown format: %s' % (pformat))
+ self.command('sudo gpart create -s %s %s' % (types[ptype], device))
+ self.command('sudo gpart add -s %s -t %s -a %s %s' % (psize,
+ formats[pformat],
+ palign,
+ device))
+ self.command('sudo gpart set -a active -i 1 %s' % (device))
+
+ def host_format_partition(self, device, pformat):
+ formats = { 'fat16': ('newfs_msdos', '16'),
+ 'fat32': ('newfs_msdos', '32') }
+ if pformat not in formats:
+ raise error.general('unknown format: %s' % (pformat))
+ self.command('sudo %s -F %s %s' % (formats[pformat][0],
+ formats[pformat][1],
+ device))
+
+ def host_device_partition(self, device, pindex):
+ return '/dev/%ss%d' % (device, pindex)
+
+ def host_mount(self, pformat, device, path_):
+ formats = { 'fat16': 'msdos',
+ 'fat32': 'msdos' }
+ if pformat not in formats:
+ raise error.general('unknown format: %s' % (pformat))
+ self.command('sudo mount -t %s %s %s' % (formats[pformat], device, path_))
+
+ def host_unmount(self, path_):
+ self.command('sudo umount %s' % (path_))
+
+class linux_image(image):
+ def __init__(self, settings):
+ super(linux_image, self).__init__(settings)
+
+ def host_image_attach(self, path_):
+ pass
+
+ def host_image_detach(self, device):
+ pass
+
+ def host_partition(self, device, ptype, pformat, psize, palign):
+ pass
+
+ def host_format_partition(self, device, pformat):
+ pass
+
+ def host_device_partition(self, device, pindex):
+ pass
+
+ def host_mount(self, pformat, device, path_):
+ pass
+
+ def host_unmount(self, path_):
+ pass
+
+class darwin_image(image):
+ def __init__(self, settings):
+ super(darwin_image, self).__init__(settings)
+
+ def host_image_attach(self, path_):
+ pass
+
+ def host_image_detach(self, device):
+ pass
+
+ def host_partition(self, device, ptype, pformat, psize, palign):
+ pass
+
+ def host_format_partition(self, device, pformat):
+ pass
+
+ def host_device_partition(self, device, pindex):
+ pass
+
+ def host_mount(self, pformat, device, path_):
+ pass
+
+ def host_unmount(self, path_):
+ pass
+
+builders = {
+ 'freebsd': freebsd_image,
+ 'linux ' : linux_image,
+ 'darwin' : darwin_image
+}
+
+def load_log(logfile):
+ log.default = log.log(streams = [logfile])
+
+def log_default():
+ return 'rtems-log-uboot-image-%s.txt' % \
+ (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
+
+class valid_dir(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string = None):
+ if type(values) is not list:
+ values = [values]
+ for value in values:
+ if not path.isdir(value):
+ raise argparse.ArgumentError(self,
+ 'is not a valid directory: %s' % (value))
+ if not path.isreadable(value):
+ raise argparse.ArgumentError(self, 'is not readable: %s' % (value))
+ if not path.iswritable(value):
+ raise argparse.ArgumentError(self, 'is not writeable: %s' % (value))
+ setattr(namespace, self.dest, value)
+
+class valid_file(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string = None):
+ current = getattr(namespace, self.dest)
+ if not isinstance(current, list) and current is not None:
+ raise argparse.ArgumentError(self,
+ ' already provided: %s, have %s' % (value,
+ current))
+ if not path.isfile(value):
+ raise argparse.ArgumentError(self, 'is not a valid file: %s' % (value))
+ if not path.isreadable(value):
+ raise argparse.ArgumentError(self, 'is not readable: %s' % (value))
+ if current is not None:
+ value = current + [value]
+ setattr(namespace, self.dest, value)
+
+class valid_format(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string = None):
+ current = getattr(namespace, self.dest)
+ if not isinstance(current, list) and current is not None:
+ raise argparse.ArgumentError(self,
+ ' already provided: %s, have %s' % (value,
+ current))
+ if value not in ['fat16', 'fat32']:
+ raise argparse.ArgumentError(self, ' invalid format: %s' % (value))
+ setattr(namespace, self.dest, value)
+
+class valid_si(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string = None):
+ current = getattr(namespace, self.dest)
+ if current is not None:
+ raise argparse.ArgumentError(self,
+ ' already provided: %s, have %s' % (value,
+ current))
+ units = len(value)
+ if value[-1].isalpha():
+ if value[-1] not in ['k', 'm', 'g']:
+ raise argparse.ArgumentError(self,
+ 'invalid SI (k, m, g): %s' % (value[-1]))
+ units = -1
+ if not value[:units].isdecimal():
+ raise argparse.ArgumentError(self, 'invalid SI size: %s' % (value))
+ setattr(namespace, self.dest, value)
+
+class valid_ip(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string = None):
+ current = getattr(namespace, self.dest)
+ if current is not None:
+ raise argparse.ArgumentError(self,
+ ' already provided: %s, have %s' % (value,
+ current))
+ setattr(namespace, self.dest, value)
+
+def run(args = sys.argv, command_path = None):
+ ec = 0
+ notice = None
+ builder = None
+ try:
+ description = 'RTEMS U-Boot Image builder creates a U-Boot image for'
+ description += 'for booting.'
+
+ argsp = argparse.ArgumentParser(prog = 'rtems-uboot-image',
+ description = description)
+ argsp.add_argument('-l', '--log',
+ help = 'Log file (default: %(default)s.',
+ type = str, default = log_default())
+ argsp.add_argument('-v', '--trace',
+ help = 'Enable trace logging for debugging.',
+ action = 'store_true')
+ argsp.add_argument('-s', '--image-size',
+ help = 'Image size in mega-bytes (default: %(default)s).',
+ type = str, action = valid_si, default = '64m')
+ argsp.add_argument('-F', '--fs-format',
+ help = 'Root file system format (default: %(default)s).',
+ type = str, action = valid_format, default = 'fat16')
+ argsp.add_argument('-S', '--fs-size',
+ help = 'Root file system size in SI units ' + \
+ '(default: %(default)s).',
+ type = str, action = valid_si, default = '63m')
+ argsp.add_argument('-A', '--fs-align',
+ help = 'Root file system alignment in SI units ' + \
+ '(default: %(default)s).',
+ type = str, action = valid_si, default = '63m')
+ argsp.add_argument('-k', '--kernel',
+ help = 'Install the kernel (default: %(default)r).',
+ type = str, action = valid_file, default = None)
+ argsp.add_argument('-f', '--file',
+ help = 'Install the file (default: None).',
+ type = str, action = valid_file, default = [])
+ argsp.add_argument('-N', '--net-boot',
+ help = 'Configure a network boot using TFTP ' + \
+ '(default: %(default)r).',
+ action = 'store_true')
+ argsp.add_argument('-B', '--net-boot-server',
+ help = 'Network boot server IP address ' + \
+ '(default: %(default)r).',
+ type = str, action = valid_ip, default = None)
+ argsp.add_argument('-I', '--net-boot-ip',
+ help = 'Network boot IP address (default: %(default)r).',
+ type = str, action = valid_ip, default = None)
+ argsp.add_argument('-U', '--custom-uenv',
+ help = 'Install the custom uEnv.txt file ' + \
+ '(default: %(default)r).',
+ type = str, action = valid_file, default = None)
+ argsp.add_argument('-b', '--build',
+ help = 'Path to a directory to build the image in' + \
+ ' (default: %(default)s).',
+ action = valid_dir, default = 'build')
+ argsp.add_argument('-o', '--output',
+ help = 'Image output file name',
+ type = str, required = True)
+ argsp.add_argument('uboot',
+ help = 'The path to a built u-boot.',
+ nargs = 1, type = str, action = valid_dir)
+
+ argopts = argsp.parse_args(args[1:])
+
+ load_log(argopts.log)
+ log.notice('RTEMS Tools - Boot Image, %s' % (version.string()))
+ log.output(log.info(args))
+ log.tracing = argopts.trace
+
+ host.load()
+
+ log.output('Platform: %s' % (host.name))
+
+ config = uboot_settings(argopts.uboot)
+
+ config.output = argopts.output
+ config.net_server_ip = argopts.net_boot_server
+ config.net_ip = argopts.net_boot_ip
+ config.build = argopts.build
+ config.image_size = argopts.image_size
+ config.fs_format = argopts.fs_format
+ config.fs_size = argopts.fs_size
+ config.fs_align = argopts.fs_align
+ config.kernel = argopts.kernel
+ config.files = argopts.file
+ config.uenv_txt = argopts.custom_uenv
+
+ config.log()
+
+ if host.name not in builders:
+ raise error.general('no builder; platform not supported: %s' % (host.name))
+
+ builder = builders[host.name](config)
+ builder.check_exes()
+ builder.build()
+
+ except error.general as gerr:
+ notice = str(gerr)
+ ec = 1
+ except error.internal as ierr:
+ notice = str(ierr)
+ ec = 1
+ except error.exit as eerr:
+ pass
+ except KeyboardInterrupt:
+ notice = 'abort: user terminated'
+ ec = 1
+ except:
+ raise
+ notice = 'abort: unknown error'
+ ec = 1
+ if builder is not None:
+ del builder
+ if notice is not None:
+ log.stderr(notice)
+ sys.exit(ec)
+
+if __name__ == "__main__":
+ run()
diff --git a/misc/tools/cmd-boot-image.py b/misc/tools/cmd-boot-image.py
new file mode 100755
index 0000000..f17a91e
--- /dev/null
+++ b/misc/tools/cmd-boot-image.py
@@ -0,0 +1,44 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2019 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+from __future__ import print_function
+
+import sys, os
+
+base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+rtems = os.path.dirname(base)
+sys.path = [rtems] + sys.path
+
+try:
+ import boot
+ boot.run(sys.argv[1:], command_path = base)
+except ImportError:
+ print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ sys.exit(1)