summaryrefslogtreecommitdiff
path: root/usr.sbin/installboot
diff options
context:
space:
mode:
authorthorpej <thorpej@NetBSD.org>2019-05-07 05:02:41 +0000
committerthorpej <thorpej@NetBSD.org>2019-05-07 05:02:41 +0000
commit3ca18896e1f3416437fa3a0cf6cd9bdf44f68dbd (patch)
treed118731ac1485b8595f6889a1309ca3134c17a2c /usr.sbin/installboot
parent6dd44c41b93ea31a18ab6960bedb9786e85c2224 (diff)
Add installboot(8) support for evbarm (and, in the future, other evb*)
boards that use u-boot. A known board database lists boards and their respective u-boot packages. u-boot packages are discovered at run-time (in /usr/pkg/share/u-boot, by default). These packages contain board database overlays that describe u-boot installation procedure that's specific for that board. Support this as a native tool and as a host tool. The native tool will attempt to determine the running board type using OpenFirmware calls. Host tool and native tool alike may also specify a board type directly using the "-o board=xxx option" or have installboot(8) determine the board type from a device tree blob using "-o dtb=/path/to/board.dtb". A "-o media=xxx" option is provided for boards that have different u-boot binaries and/or installation procedures for different media types (e.g. SDMMC, eMMC, or USB). This is trivial to extend to other evb* platforms that use u-boot, even if they don't use FDT for autoconfiguration.
Diffstat (limited to 'usr.sbin/installboot')
-rw-r--r--usr.sbin/installboot/Makefile45
-rw-r--r--usr.sbin/installboot/arch/evbarm.c119
-rw-r--r--usr.sbin/installboot/evboards.c1655
-rw-r--r--usr.sbin/installboot/evboards.h65
-rw-r--r--usr.sbin/installboot/installboot.8120
-rw-r--r--usr.sbin/installboot/installboot.c56
-rw-r--r--usr.sbin/installboot/installboot.h22
-rw-r--r--usr.sbin/installboot/machines.c6
8 files changed, 2066 insertions, 22 deletions
diff --git a/usr.sbin/installboot/Makefile b/usr.sbin/installboot/Makefile
index 7df16909395..1791c796db1 100644
--- a/usr.sbin/installboot/Makefile
+++ b/usr.sbin/installboot/Makefile
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.51 2017/01/11 12:19:44 joerg Exp $
+# $NetBSD: Makefile,v 1.52 2019/05/07 05:02:42 thorpej Exp $
#
.include <bsd.own.mk>
@@ -11,9 +11,17 @@ ARCH_XLAT= amd64-i386.c news68k-news.c newsmips-news.c
ARCH_XLAT+= sun2-sun68k.c sun3-sun68k.c
.if !defined(SMALLPROG) && !defined(ARCH_FILES)
-ARCH_FILES= alpha.c amiga.c emips.c ews4800mips.c hp300.c hppa.c i386.c
-ARCH_FILES+= landisk.c macppc.c news.c next68k.c pmax.c
-ARCH_FILES+= sparc.c sparc64.c sun68k.c vax.c x68k.c
+ARCH_FILES= alpha.c amiga.c
+ARCH_FILES+= emips.c evbarm.c ews4800mips.c
+ARCH_FILES+= hp300.c hppa.c
+ARCH_FILES+= i386.c
+ARCH_FILES+= landisk.c
+ARCH_FILES+= macppc.c
+ARCH_FILES+= news.c next68k.c
+ARCH_FILES+= pmax.c
+ARCH_FILES+= sparc.c sparc64.c sun68k.c
+ARCH_FILES+= vax.c
+ARCH_FILES+= x68k.c
.else
ARCH_FILES?= ${ARCH_XLAT:M${MACHINE}-*:S/${MACHINE}-//}
.if empty(ARCH_FILES)
@@ -23,8 +31,30 @@ ARCH_FILES= ${MACHINE}.c
SRCS+=${ARCH_FILES}
+.if !empty(ARCH_FILES:C/(evbarm)/evboard/:Mevboard.c)
+SRCS+=evboards.c
+.endif
+
+.if !empty(ARCH_FILES:C/(evbarm)/fdt/:Mfdt.c)
+FDTDIR= ${.CURDIR}/../../sys/external/bsd/libfdt/dist
+.PATH: ${FDTDIR}
+CPPFLAGS+= -DSUPPORT_FDT -I${FDTDIR}
+SRCS+=fdt.c fdt_ro.c fdt_strerror.c
+# XXX libfdt has some sign-comparison issues
+COPTS.fdt.c+= -Wno-error=sign-compare
+COPTS.fdt_ro.c+= -Wno-error=sign-compare
+COPTS.fdt_strerror.c+= -Wno-error=sign-compare
+.endif
+
+
+.if !defined(HOSTPROGNAME)
+.if !empty(ARCH_FILES:C/(evbarm)/ofw/:Mofw.c)
+CPPFLAGS+= -DSUPPORT_OPENFIRMWARE
+.endif
+.endif
+
.if empty(ARCH_FILES:C/(macppc|news|sparc|sun68k|x68k)/stg2/:Mstg2.c)
-CPPFLAGS += -DNO_STAGE2
+CPPFLAGS+= -DNO_STAGE2
.else
SRCS+= bbinfo.c
@@ -47,6 +77,11 @@ CPPFLAGS+= -I${.CURDIR} -I.
COPTS.${f}.c+= -Wno-pointer-sign
.endfor
+.if !empty(SRCS:Mevboards.c)
+LDADD+= -lprop
+DPADD+= ${LIBPROP}
+.endif
+
LDADD+= -lutil
DPADD+= ${LIBUTIL}
.endif
diff --git a/usr.sbin/installboot/arch/evbarm.c b/usr.sbin/installboot/arch/evbarm.c
new file mode 100644
index 00000000000..977cfb76209
--- /dev/null
+++ b/usr.sbin/installboot/arch/evbarm.c
@@ -0,0 +1,119 @@
+/* $NetBSD: evbarm.c,v 1.1 2019/05/07 05:02:42 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if !defined(__lint)
+__RCSID("$NetBSD: evbarm.c,v 1.1 2019/05/07 05:02:42 thorpej Exp $");
+#endif /* !__lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "installboot.h"
+#include "evboards.h"
+
+static int evbarm_setboot(ib_params *);
+static int evbarm_clearboot(ib_params *);
+static int evbarm_editboot(ib_params *);
+static void evbarm_usage(ib_params *);
+
+struct ib_mach ib_mach_evbarm = {
+ .name = "evbarm",
+ .setboot = evbarm_setboot,
+ .clearboot = evbarm_clearboot,
+ .editboot = evbarm_editboot,
+ .usage = evbarm_usage,
+ .valid_flags = IB_BOARD | IB_DTB | IB_MEDIA,
+ .mach_flags = MF_UBOOT,
+};
+
+static int
+evbarm_setboot(ib_params *params)
+{
+ evb_board board;
+ int rv = 0;
+
+ if (!evb_db_load(params)) {
+ warnx("Unable to load board db.");
+ return 0;
+ }
+
+ board = evb_db_get_board(params);
+ if (board == NULL)
+ goto out;
+
+ rv = evb_uboot_setboot(params, board);
+
+ out:
+ if (params->mach_data) {
+ prop_object_release(params->mach_data);
+ params->mach_data = NULL;
+ }
+ return rv;
+}
+
+static int
+evbarm_clearboot(ib_params *params)
+{
+
+ return no_clearboot(params);
+}
+
+static int
+evbarm_editboot(ib_params *params)
+{
+
+ return no_editboot(params);
+}
+
+static void
+evbarm_usage(ib_params *params)
+{
+
+ if (!evb_db_load(params)) {
+ warnx("Unable to load board db.");
+ return;
+ }
+
+ fprintf(stderr, "Known boards (for -o board=...) are:\n");
+ evb_db_list_boards(params, stderr);
+
+ if (params->mach_data) {
+ prop_object_release(params->mach_data);
+ params->mach_data = NULL;
+ }
+}
diff --git a/usr.sbin/installboot/evboards.c b/usr.sbin/installboot/evboards.c
new file mode 100644
index 00000000000..3c166496f42
--- /dev/null
+++ b/usr.sbin/installboot/evboards.c
@@ -0,0 +1,1655 @@
+/* $NetBSD: evboards.c,v 1.1 2019/05/07 05:02:42 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if !defined(__lint)
+__RCSID("$NetBSD: evboards.c,v 1.1 2019/05/07 05:02:42 thorpej Exp $");
+#endif /* !__lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef SUPPORT_FDT
+#include "libfdt.h"
+#endif
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/utsname.h>
+
+#ifdef SUPPORT_OPENFIRMWARE
+#include <sys/ioctl.h>
+#include <dev/ofw/openfirmio.h>
+#endif
+
+#endif /* ! HAVE_NBTOOL_CONFIG_H */
+
+#include "installboot.h"
+#include "evboards.h"
+
+/*
+ * The board database is implemented as a property list. The base
+ * system provides a set of known boards, keyed by their "compatible"
+ * device tree property.
+ *
+ * The database provided by the base system is meant to help guide
+ * the user as to which u-boot package needs to be installed on the
+ * system in order to write the boot loader to the boot media. The
+ * base board plist is specific to the $MACHINE (e.g. "evbarm"), and
+ * is installed along with the build tools, e.g.:
+ *
+ * (native location)
+ * /usr/sbin/installboot
+ * /usr/share/installboot/evbarm/boards.plist
+ * /usr/share/installboot/evbmips/boards.plist
+ *
+ * (example cross host tool location)
+ * /usr/local/xnbsd/bin/nbinstallboot
+ * /usr/local/xnbsd/share/installboot/evbarm/boards.plist
+ * /usr/local/xnbsd/share/installboot/evbmips/boards.plist
+ *
+ * The schema of the base board plist is as follows:
+ *
+ * <plist>
+ * <dict>
+ * <!--
+ * -- Key: string matching a "compatible" DT property.
+ * -- Value: dictionary representing a board object.
+ * -- (required)
+ * -->
+ * <key>example,example-board</key>
+ * <dict>
+ * <!--
+ * -- Key: "description".
+ * -- Value: string containing the board description.
+ * -- (required)
+ * -->
+ * <key>description</key>
+ * <string>Example Co. Example Board</string>
+ *
+ * <!--
+ * -- Key: "u-boot-pkg".
+ * -- Value: string representing the board-specific
+ * -- portion of the u-boot package name.
+ * -- In this example, the package's full name
+ * -- is "u-boot-exampleboard". This is used
+ * -- to recommend to the user which u-boot
+ * -- package to install. If not present, then
+ * -- no package recommendation will be made.
+ * -- (optional)
+ * -->
+ * <key>u-boot-pkg</key>
+ * <string>exampleboard</string>
+ * </dict>
+ * </dict>
+ * </plist>
+ *
+ * Individual u-boot packages install their own overlay property list
+ * files that installboot(8) then scans for. These overlay files are
+ * named "installboot.plist", and are installed alongside the u-boot
+ * binaries by the individual u-boot packages, for example:
+ *
+ * /usr/pkg/share/u-boot/exampleboard/installboot.plist
+ * /usr/pkg/share/u-boot/exampleboard/u-boot-with-spl.bin
+ *
+ * installboot(8) scans a set of directories looking for "installboot.plist"
+ * overlay files one directory deep. For example:
+ *
+ * /usr/pkg/share/u-boot/
+ * exampleboard/installboot.plist
+ * superarmdeluxe/installboot.plist
+ * dummy/
+ *
+ * In this example, "/usr/pkg/share/u-boot" is scanned, it would identify
+ * "exampleboard" and "superarmdeluxe" as directories containing overlays
+ * and load them.
+ *
+ * The default path scanned for u-boot packages is:
+ *
+ * /usr/pkg/share/u-boot
+ *
+ * This can be overridden with the INSTALLBOOT_UBOOT_PATHS environment
+ * variable, which contains a colon-sparated list of directories, e.g.:
+ *
+ * /usr/pkg/share/u-boot:/home/jmcneill/hackityhack/u-boot
+ *
+ * The scan only consults the top-level children of the specified directory.
+ *
+ * Each overlay includes complete board objects that entirely replace
+ * the system-provided board objects in memory. Some of the keys in
+ * overlay board objects are computed at run-time and should not appear
+ * in the plists loaded from the file system.
+ *
+ * The schema of the overlay board plists are as follows:
+ *
+ * <plist>
+ * <dict>
+ * <!--
+ * -- Key: string matching a "compatible" DT property.
+ * -- Value: dictionary representing a board object.
+ * -- (required)
+ * -->
+ * <key>example,example-board</key>
+ * <dict>
+ * <!--
+ * -- Key: "description".
+ * -- Value: string containing the board description.
+ * -- (required)
+ * -->
+ * <key>description</key>
+ * <string>Example Co. Example Board</string>
+ *
+ * <!--
+ * -- Key: "u-boot-install".
+ * -- (and variants; see discussion below)
+ * -- "u-boot-install-emmc", etc.).
+ * -- Value: Array of u-boot installation step objects,
+ * -- as described below.
+ * -- (required)
+ * --
+ * -- At least one of these objects is required. If the
+ * -- board uses a single set of steps for all boot media
+ * -- types, then it should provide just "u-boot-install".
+ * -- Otherwise, it whould provide one or more objects
+ * -- with names reflecting the media type, e.g.:
+ * --
+ * -- "u-boot-install-sdmmc" (for SD cards)
+ * -- "u-boot-install-emmc" (for eMMC modules)
+ * -- "u-boot-install-usb" (for USB block storage)
+ * --
+ * -- These installation steps will be selectable using
+ * -- the "media=..." option to installboot(8).
+ * -->
+ * <key>u-boot-install</key>
+ * <array>
+ * <!-- see installation object discussion below. -->
+ * </array>
+ *
+ * <!--
+ * -- Key: "runtime-u-boot-path"
+ * -- Value: A string representing the path to the u-boot
+ * -- binary files needed to install the boot loader.
+ * -- This value is computed at run-time and is the
+ * -- same directory in which the instalboot.plist
+ * -- file for that u-boot package is located.
+ * -- This key/value pair should never be included
+ * -- in an installboot.plist file, and including it
+ * -- will cause the overlay to be rejected.
+ * -- (computed at run-time)
+ * -->
+ * <key>runtime-u-boot-path</key>
+ * <string>/usr/pkg/share/u-boot/exampleboard</string>
+ * </dict>
+ * </dict>
+ * </plist>
+ *
+ * The installation objects provide a description of the steps needed
+ * to install u-boot on the boot media. Each installation object it
+ * itself an array of step object.
+ *
+ * A basic installation object has a single step that instructs
+ * installboot(8) to write a file to a specific offset onto the
+ * boot media.
+ *
+ * <key>u-boot-install</key>
+ * <!-- installation object -->
+ * <array>
+ * <!-- step object -->
+ * <dict>
+ * <!--
+ * -- Key: "file-name".
+ * -- Value: a string naming the file to be
+ * -- written to the media.
+ * -- (required)
+ * -->
+ * <key>file-name</key>
+ * <string>u-boot-with-spl.bin</string>
+ *
+ * <!--
+ * -- Key: "image-offset".
+ * -- Value: an integer specifying the offset
+ * -- into the output image or device
+ * -- where to write the file. Defaults
+ * -- to 0 if not specified.
+ * -- (optional)
+ * -->
+ * <key>image-offset</key>
+ * <integer>8192</integer>
+ * </dict>
+ * </array>
+ *
+ * Some installations require multiple steps with special handling.
+ *
+ * <key>u-boot-install</key>
+ * <array>
+ * <--
+ * -- Step 1: Write the initial portion of the boot
+ * -- loader onto the media. The loader has a "hole"
+ * -- to leave room for the MBR partition table. Take
+ * -- care not to scribble over the table.
+ * -->
+ * <dict>
+ * <key>file-name</key>
+ * <string>u-boot-img.bin</string>
+ *
+ * <!--
+ * -- Key: "file-size".
+ * -- Value: an integer specifying the amount of
+ * -- data from the file to be written to the
+ * -- output. Defaults to "to end of file" if
+ * -- not specified.
+ * -- (optional)
+ * -->
+ * <!-- Stop short of the MBR partition table. -->
+ * <key>file-size</key>
+ * <integer>442</integer>
+ *
+ * <!--
+ * -- Key: "preserve".
+ * -- Value: a boolean indicating that any partial
+ * -- output block should preserve any pre-
+ * -- existing contents of that block for
+ * -- the portion of the of the block not
+ * -- overwritten by the input file.
+ * -- (read-modify-write)
+ * -- (optional)
+ * -->
+ * <!-- Preserve the MBR partition table. -->
+ * <key>preserve</key>
+ * <true/>
+ * </dict>
+ * <--
+ * -- Step 2: Write the rest of the loader after the
+ * -- MBR partition table.
+ * -->
+ * <dict>
+ * <key>file-name</key>
+ * <string>u-boot-img.bin</string>
+ *
+ * <!--
+ * -- Key: "file-offset".
+ * -- Value: an integer specifying the offset into
+ * -- the input file from where to start
+ * -- copying to the output.
+ * -- (optional)
+ * -->
+ * <key>file-offset</key>
+ * <integer>512</integer>
+ *
+ * <!-- ...just after the MBR partition talble. -->
+ * <key>image-offset</key>
+ * <integer>512</integer>
+ * </dict>
+ * </array>
+ */
+
+/*
+ * make_path --
+ * Build a path into the given buffer with the specified
+ * format. Returns NULL if the path won't fit.
+ */
+static const char *
+make_path(char *buf, size_t bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(buf, bufsize, fmt, ap);
+ va_end(ap);
+
+ if (ret < 0 || (size_t)ret >= bufsize)
+ return NULL;
+
+ return buf;
+}
+
+#ifndef EVBOARDS_PLIST_BASE
+#define EVBOARDS_PLIST_BASE "/usr"
+#endif
+
+static const char evb_db_base_location[] =
+ EVBOARDS_PLIST_BASE "/share/installboot";
+
+#ifndef DEFAULT_UBOOT_PKG_PATH
+#define DEFAULT_UBOOT_PKG_PATH "/usr/pkg/share/u-boot"
+#endif
+
+#ifndef UBOOT_PATHS_ENV_VAR
+#define UBOOT_PATHS_ENV_VAR "INSTALLBOOT_UBOOT_PATHS"
+#endif
+
+static const char evb_uboot_pkg_path[] = DEFAULT_UBOOT_PKG_PATH;
+
+/*
+ * evb_db_base_path --
+ * Returns the path to the base board db file.
+ */
+static const char *
+evb_db_base_path(ib_params *params, char *buf, size_t bufsize)
+{
+
+ return make_path(buf, bufsize, "%s/%s/boards.plist",
+ evb_db_base_location, params->machine->name);
+}
+
+/*
+ * evb_uboot_pkg_paths --
+ * Returns an array of u-boot package paths to scan for
+ * installboot.plist files.
+ *
+ * Number of array elements, not including the NULL terminator,
+ * is returned in *countp.
+ *
+ * The working buffer is returned in *bufp so that the caller
+ * can free it.
+ */
+static char **
+evb_uboot_pkg_paths(ib_params *params, int *countp, void **bufp)
+{
+ char **ret_array = NULL;
+ char *buf = NULL;
+ const char *pathspec;
+ int i, count;
+ char *cp, *startcp;;
+
+ pathspec = getenv(UBOOT_PATHS_ENV_VAR);
+ if (pathspec == NULL)
+ pathspec = evb_uboot_pkg_path;
+
+ if (strlen(pathspec) == 0)
+ goto out;
+
+ /* Count the path elements. */
+ for (cp = __UNCONST(pathspec), count = 0;;) {
+ count++;
+ cp = strchr(cp, ':');
+ if (cp == NULL)
+ break;
+ cp++;
+ }
+
+ buf = malloc((sizeof(char *) * (count + 1)) +
+ strlen(pathspec) + 1);
+ if (buf == NULL)
+ goto out;
+
+ /*
+ * Because we want to follow the usual "paths are listed in priority
+ * order" semantics, we reverse the order of the paths when we put
+ * them into the array we feed to fts. This is because we always
+ * overwrite existing entries as we find them, thus the last board
+ * object found one a given key is the one that will be used.
+ */
+
+ ret_array = (char **)buf;
+ startcp = buf + (sizeof(char *) * (count + 1));
+ /* this is a safe strcpy(); don't replace it. */
+ strcpy(startcp, pathspec);
+
+ cp = strrchr(startcp, ':');
+ if (cp == NULL)
+ cp = startcp;
+
+ for (i = 0;;) {
+ if (*cp == ':') {
+ ret_array[i++] = cp+1;
+ *cp-- = '\0';
+ } else
+ ret_array[i++] = cp;
+ if (cp == startcp)
+ break;
+ cp = strrchr(cp, ':');
+ if (cp == NULL)
+ cp = startcp;
+ }
+ assert(i == count);
+ ret_array[i] = NULL;
+
+ out:
+ if (ret_array == NULL) {
+ if (buf != NULL)
+ free(buf);
+ } else {
+ if (countp != NULL)
+ *countp = count;
+ if (bufp != NULL)
+ *bufp = buf;
+ }
+ return ret_array;
+}
+
+static const char step_file_name_key[] = "file-name";
+static const char step_file_offset_key[] = "file-offset";
+static const char step_file_size_key[] = "file-size";
+static const char step_image_offset_key[] = "image-offset";
+static const char step_preserve_key[] = "preserve";
+
+static bool
+validate_ubstep_object(evb_ubstep obj)
+{
+ /*
+ * evb_ubstep is a dictionary with the following keys:
+ *
+ * "file-name" (string) (required)
+ * "file-offset" (number) (optional)
+ * "file-size" (number) (optional)
+ * "image-offset" (number) (optional)
+ * "preserve" (bool) (optional)
+ */
+ if (prop_object_type(obj) != PROP_TYPE_DICTIONARY)
+ return false;
+
+ prop_object_t v;
+
+ v = prop_dictionary_get(obj, step_file_name_key);
+ if (v == NULL ||
+ prop_object_type(v) != PROP_TYPE_STRING)
+ return false;
+
+ v = prop_dictionary_get(obj, step_file_offset_key);
+ if (v != NULL &&
+ prop_object_type(v) != PROP_TYPE_NUMBER)
+ return false;
+
+ v = prop_dictionary_get(obj, step_file_size_key);
+ if (v != NULL &&
+ prop_object_type(v) != PROP_TYPE_NUMBER)
+ return false;
+
+ v = prop_dictionary_get(obj, step_image_offset_key);
+ if (v != NULL &&
+ prop_object_type(v) != PROP_TYPE_NUMBER)
+ return false;
+
+ v = prop_dictionary_get(obj, step_preserve_key);
+ if (v != NULL &&
+ prop_object_type(v) != PROP_TYPE_BOOL)
+ return false;
+
+ return true;
+}
+
+static bool
+validate_ubinstall_object(evb_ubinstall obj)
+{
+ /*
+ * evb_ubinstall is an array with one or more evb_ubstep
+ * objects.
+ *
+ * (evb_ubsteps is just a convenience type for iterating
+ * over the steps.)
+ */
+ if (prop_object_type(obj) != PROP_TYPE_ARRAY)
+ return false;
+ if (prop_array_count(obj) < 1)
+ return false;
+
+ prop_object_t v;
+ prop_object_iterator_t iter = prop_array_iterator(obj);
+
+ while ((v = prop_object_iterator_next(iter)) != NULL) {
+ if (!validate_ubstep_object(v))
+ break;
+ }
+
+ prop_object_iterator_release(iter);
+ return v == NULL;
+}
+
+static const char board_description_key[] = "description";
+static const char board_u_boot_pkg_key[] = "u-boot-pkg";
+static const char board_u_boot_path_key[] = "runtime-u-boot-path";
+static const char board_u_boot_install_key[] = "u-boot-install";
+
+static bool
+validate_board_object(evb_board obj, bool is_overlay)
+{
+ /*
+ * evb_board is a dictionary with the following keys:
+ *
+ * "description" (string) (required)
+ * "u-boot-pkg" (string) (optional, base only)
+ * "runtime-u-boot-path" (string) (required, overlay only)
+ *
+ * With special consideration for these keys:
+ *
+ * Either this key and no other "u-boot-install*" keys:
+ * "u-boot-install" (string) (required, overlay only)
+ *
+ * Or one or more keys of the following pattern:
+ * "u-boot-install-*" (string) (required, overlay only)
+ */
+ bool has_default_install = false;
+ bool has_media_install = false;
+
+ if (prop_object_type(obj) != PROP_TYPE_DICTIONARY)
+ return false;
+
+ prop_object_t v;
+
+ v = prop_dictionary_get(obj, board_description_key);
+ if (v == NULL ||
+ prop_object_type(v) != PROP_TYPE_STRING)
+ return false;
+
+ v = prop_dictionary_get(obj, board_u_boot_pkg_key);
+ if (v != NULL &&
+ (is_overlay || prop_object_type(v) != PROP_TYPE_STRING))
+ return false;
+
+ /*
+ * "runtime-u-boot-path" is added to an overlay after we've
+ * validated the board object, so simply make sure it's not
+ * present.
+ */
+ v = prop_dictionary_get(obj, board_u_boot_path_key);
+ if (v != NULL)
+ return false;
+
+ prop_object_iterator_t iter = prop_dictionary_iterator(obj);
+ prop_dictionary_keysym_t key;
+ while ((key = prop_object_iterator_next(iter)) != NULL) {
+ const char *cp = prop_dictionary_keysym_cstring_nocopy(key);
+ if (strcmp(cp, board_u_boot_install_key) == 0) {
+ has_default_install = true;
+ } else if (strncmp(cp, board_u_boot_install_key,
+ sizeof(board_u_boot_install_key) - 1) == 0 &&
+ cp[sizeof(board_u_boot_install_key) - 1] == '-') {
+ has_media_install = true;
+ } else {
+ continue;
+ }
+ v = prop_dictionary_get_keysym(obj, key);
+ assert(v != NULL);
+ if (!is_overlay || !validate_ubinstall_object(v))
+ break;
+ }
+ prop_object_iterator_release(iter);
+ if (key != NULL)
+ return false;
+
+ /*
+ * Overlays must have only a default install key OR one or more
+ * media install keys.
+ */
+ if (is_overlay)
+ return has_default_install ^ has_media_install;
+
+ /*
+ * Base board objects must have neither.
+ */
+ return (has_default_install | has_media_install) == false;
+}
+
+/*
+ * evb_db_load_overlay --
+ * Load boards from an overlay file into the db.
+ */
+static void
+evb_db_load_overlay(ib_params *params, const char *path,
+ const char *runtime_uboot_path)
+{
+ prop_dictionary_t overlay;
+ struct stat sb;
+
+ if (params->flags & IB_VERBOSE)
+ printf("Loading '%s'.\n", path);
+
+ if (stat(path, &sb) < 0) {
+ warn("'%s'", path);
+ return;
+ } else {
+ overlay = prop_dictionary_internalize_from_file(path);
+ if (overlay == NULL) {
+ warnx("unable to parse overlay '%s'", path);
+ return;
+ }
+ }
+
+ /*
+ * Validate all of the board objects and add them to the board
+ * db, replacing any existing entries as we go.
+ */
+ prop_object_iterator_t iter = prop_dictionary_iterator(overlay);
+ prop_dictionary_keysym_t key;
+ prop_dictionary_t board;
+ while ((key = prop_object_iterator_next(iter)) != NULL) {
+ board = prop_dictionary_get_keysym(overlay, key);
+ assert(board != NULL);
+ if (!validate_board_object(board, true)) {
+ warnx("invalid board object in '%s': '%s'", path,
+ prop_dictionary_keysym_cstring_nocopy(key));
+ continue;
+ }
+
+ /* Add "runtime-u-boot-path". */
+ prop_string_t string =
+ prop_string_create_cstring(runtime_uboot_path);
+ assert(string != NULL);
+ prop_dictionary_set(board, board_u_boot_path_key, string);
+ prop_object_release(string);
+
+ /* Insert into board db. */
+ prop_dictionary_set_keysym(params->mach_data, key, board);
+ }
+ prop_object_iterator_release(iter);
+ prop_object_release(overlay);
+}
+
+/*
+ * evb_db_load_overlays --
+ * Load the overlays from the search path.
+ */
+static void
+evb_db_load_overlays(ib_params *params)
+{
+ char overlay_pathbuf[PATH_MAX+1];
+ const char *overlay_path;
+ char **paths;
+ void *pathsbuf = NULL;
+ FTS *fts;
+ FTSENT *chp, *p;
+ struct stat sb;
+
+ paths = evb_uboot_pkg_paths(params, NULL, &pathsbuf);
+ if (paths == NULL) {
+ warnx("No u-boot search path?");
+ return;
+ }
+
+ fts = fts_open(paths, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL ||
+ (chp = fts_children(fts, 0)) == NULL) {
+ warn("Unable to search u-boot path");
+ if (fts != NULL)
+ fts_close(fts);
+ return;
+ }
+
+ chp = fts_children(fts, 0);
+
+ while ((p = fts_read(fts)) != NULL) {
+ if (p->fts_info != FTS_D)
+ continue;
+ overlay_path = make_path(overlay_pathbuf,
+ sizeof(overlay_pathbuf), "%s/installboot.plist",
+ p->fts_path);
+ if (overlay_path == NULL)
+ continue;
+ if (stat(overlay_path, &sb) < 0)
+ continue;
+ evb_db_load_overlay(params, overlay_path, p->fts_path);
+ }
+
+ fts_close(fts);
+
+ /*
+ * If the user specifed a stage1 loader, then consult it last
+ * for a possible u-boot package location.
+ */
+ if (params->stage1 != NULL) {
+ overlay_path = make_path(overlay_pathbuf,
+ sizeof(overlay_pathbuf), "%s/installboot.plist",
+ params->stage1);
+ if (overlay_path != NULL) {
+ if (stat(overlay_path, &sb) == 0) {
+ evb_db_load_overlay(params, overlay_path,
+ params->stage1);
+ }
+ }
+ }
+}
+
+/*
+ * evb_db_load_base --
+ * Load the base board db.
+ */
+static bool
+evb_db_load_base(ib_params *params)
+{
+ char buf[PATH_MAX+1];
+ const char *path;
+ prop_dictionary_t board_db;
+ struct stat sb;
+
+ path = evb_db_base_path(params, buf, sizeof(buf));
+ if (path == NULL)
+ return false;
+
+ if (params->flags & IB_VERBOSE)
+ printf("Loading '%s'.\n", path);
+
+ if (stat(path, &sb) < 0) {
+ if (errno != ENOENT) {
+ warn("'%s'", path);
+ return false;
+ }
+ board_db = prop_dictionary_create();
+ assert(board_db != NULL);
+ } else {
+ board_db = prop_dictionary_internalize_from_file(path);
+ if (board_db == NULL) {
+ warnx("unable to parse board db '%s'", path);
+ return false;
+ }
+ }
+
+ if (prop_dictionary_count(board_db) == 0) {
+ /*
+ * Oh well, maybe we'll load some overlays.
+ */
+ goto done;
+ }
+
+ /*
+ * Validate all of the board objects and remove any bad ones.
+ */
+ prop_array_t all_board_keys = prop_dictionary_all_keys(board_db);
+ prop_object_iterator_t iter = prop_array_iterator(all_board_keys);
+ prop_dictionary_keysym_t key;
+ prop_dictionary_t board;
+ while ((key = prop_object_iterator_next(iter)) != NULL) {
+ board = prop_dictionary_get_keysym(board_db, key);
+ assert(board != NULL);
+ if (!validate_board_object(board, false)) {
+ warnx("invalid board object in '%s': '%s'", path,
+ prop_dictionary_keysym_cstring_nocopy(key));
+ prop_dictionary_remove_keysym(board_db, key);
+ }
+ }
+ prop_object_iterator_release(iter);
+ prop_object_release(all_board_keys);
+
+ done:
+ params->mach_data = board_db;
+ return true;
+}
+
+/*
+ * evb_db_load --
+ * Load the board database.
+ */
+bool
+evb_db_load(ib_params *params)
+{
+ if (!evb_db_load_base(params))
+ return false;
+ evb_db_load_overlays(params);
+
+ return true;
+}
+
+#if !HAVE_NBTOOL_CONFIG_H
+/*
+ * Native board name guessing methods.
+ */
+
+#ifdef SUPPORT_OPENFIRMWARE
+static int
+ofw_fd(void)
+{
+ static const char openfirm_path[] = "/dev/openfirm";
+
+ return open(openfirm_path, O_RDONLY);
+}
+
+static int
+OF_finddevice(const char *name)
+{
+ struct ofiocdesc ofio = {
+ .of_name = __UNCONST(name),
+ .of_namelen = strlen(name),
+ };
+ int fd = ofw_fd();
+
+ if (fd == -1)
+ return -1;
+
+ if (ioctl(fd, OFIOCFINDDEVICE, &ofio) < 0) {
+ if (errno != ENOENT)
+ warn("OFIOCFINDDEVICE('%s')", name);
+ ofio.of_nodeid = -1;
+ }
+ (void) close(fd);
+
+ return ofio.of_nodeid;
+}
+
+static int
+OF_getprop(int phandle, const char *prop, void *buf, size_t buflen)
+{
+ struct ofiocdesc ofio = {
+ .of_nodeid = phandle,
+ .of_name = __UNCONST(prop),
+ .of_namelen = strlen(prop),
+ .of_buf = buf,
+ .of_buflen = buflen,
+ };
+ int fd = ofw_fd();
+
+ if (fd == -1)
+ return -1;
+
+ int save_errno = 0;
+
+ if (ioctl(fd, OFIOCGET, &ofio) < 0) {
+ save_errno = errno;
+ if (errno != ENOMEM && errno != ENOENT) {
+ save_errno = errno;
+ warn("OFIOCGET('%s')", prop);
+ }
+ ofio.of_buflen = -1;
+ }
+ (void) close(fd);
+ errno = save_errno;
+
+ return ofio.of_buflen;
+}
+
+static void *
+ofw_getprop(int phandle, const char *prop, int *lenp)
+{
+ size_t buflen = 32;
+ void *buf = NULL;
+ int len;
+
+ for (;;) {
+ void *newbuf = realloc(buf, buflen);
+ if (newbuf == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = newbuf;
+ switch (len = OF_getprop(phandle, prop, buf, buflen)) {
+ case -1:
+ if (errno != ENOMEM) {
+ free(buf);
+ return NULL;
+ }
+ buflen *= 2;
+ break;
+
+ default:
+ if (lenp)
+ *lenp = len;
+ return buf;
+ }
+ }
+}
+
+static evb_board
+evb_db_get_board_from_ofw(ib_params *params, const char **board_namep)
+{
+ int phandle;
+ int compatible_len = 0;
+ char *compatible_buf;
+ const char *sp, *nsp;
+ evb_board board;
+
+ phandle = OF_finddevice("/");
+ if (phandle == -1) {
+ /* No OpenFirmware available. */
+ return NULL;
+ }
+
+ compatible_buf = ofw_getprop(phandle, "compatible", &compatible_len);
+
+ /*
+ * We just leak compatible_buf on success. Not a big deal since
+ * we are not a long-running process.
+ */
+
+ sp = compatible_buf;
+ while (compatible_len &&
+ (nsp = memchr(sp, 0, compatible_len)) != NULL) {
+ if (params->flags & IB_VERBOSE)
+ printf("Checking OFW compatible string '%s'.\n", sp);
+ board = prop_dictionary_get(params->mach_data, sp);
+ if (board != NULL) {
+ if (board_namep)
+ *board_namep = sp;
+ return board;
+ }
+ nsp++; /* skip over NUL */
+ compatible_len -= (nsp - sp);
+ sp = nsp;
+ }
+
+ free(compatible_buf);
+ return NULL;
+}
+#endif /* SUPPORT_OPENFIRMWARE */
+
+#endif /* ! HAVE_NBTOOL_CONFIG_H */
+
+/*
+ * Host-tool and native board name guessing methods.
+ */
+
+#ifdef SUPPORT_FDT
+static void *
+load_dtb(ib_params *params)
+{
+ struct stat sb;
+ void *buf;
+ int fd;
+
+ if (stat(params->dtb, &sb) < 0) {
+ warn("%s", params->dtb);
+ return NULL;
+ }
+
+ buf = malloc((size_t)sb.st_size);
+ assert(buf != NULL);
+
+ if ((fd = open(params->dtb, O_RDONLY)) < 0) {
+ warn("%s", params->dtb);
+ free(buf);
+ return NULL;
+ }
+
+ if (read(fd, buf, (size_t)sb.st_size) != (ssize_t)sb.st_size) {
+ warn("read '%s'", params->dtb);
+ free(buf);
+ buf = NULL;
+ }
+ (void) close(fd);
+
+ return buf;
+}
+
+static evb_board
+evb_db_get_board_from_dtb(ib_params *params, const char **board_namep)
+{
+ evb_board board = NULL;
+ void *fdt = NULL;
+ int error;
+
+ fdt = load_dtb(params);
+ if (fdt == NULL)
+ return NULL;
+
+ error = fdt_check_header(fdt);
+ if (error) {
+ warnx("%s: %s", params->dtb, fdt_strerror(error));
+ goto bad;
+ }
+
+ const int system_root = fdt_path_offset(fdt, "/");
+ if (system_root < 0) {
+ warnx("%s: unable to find node '/'", params->dtb);
+ goto bad;
+ }
+
+ const int system_ncompat = fdt_stringlist_count(fdt, system_root,
+ "compatible");
+ if (system_ncompat <= 0) {
+ warnx("%s: no 'compatible' property on node '/'", params->dtb);
+ goto bad;
+ }
+
+ const char *compatible;
+ int si;
+ for (si = 0; si < system_ncompat; si++) {
+ compatible = fdt_stringlist_get(fdt, system_root,
+ "compatible", si, NULL);
+ if (compatible == NULL)
+ continue;
+ if (params->flags & IB_VERBOSE)
+ printf("Checking FDT compatible string '%s'.\n",
+ compatible);
+ board = prop_dictionary_get(params->mach_data, compatible);
+ if (board != NULL) {
+ /*
+ * We just leak compatible on success. Not a big
+ * deal since we are not a long-running process.
+ */
+ if (board_namep) {
+ *board_namep = strdup(compatible);
+ assert(*board_namep != NULL);
+ }
+ free(fdt);
+ return board;
+ }
+ }
+
+ bad:
+ if (fdt != NULL)
+ free(fdt);
+ return NULL;
+}
+#endif /* SUPPORT_FDT */
+
+/*
+ * evb_db_get_board --
+ * Return the specified board object from the database.
+ */
+evb_board
+evb_db_get_board(ib_params *params)
+{
+ const char *board_name = NULL;
+ evb_board board = NULL;
+
+#if !HAVE_NBTOOL_CONFIG_H
+ /*
+ * If we're not a host tool, determine if we're running "natively".
+ */
+ bool is_native = false;
+ struct utsname utsname;
+
+ if (uname(&utsname) < 0) {
+ warn("uname");
+ } else if (strcmp(utsname.machine, params->machine->name) == 0) {
+ is_native = true;
+ }
+#endif /* ! HAVE_NBTOOL_CONFIG_H */
+
+ /*
+ * Logic for determing board type that can be shared by host-tool
+ * and native builds goes here.
+ */
+
+ /*
+ * Command-line argument trumps all.
+ */
+ if (params->flags & IB_BOARD) {
+ board_name = params->board;
+ }
+
+#ifdef SUPPORT_FDT
+ if (board_name == NULL && (params->flags & IB_DTB)) {
+ board = evb_db_get_board_from_dtb(params, &board_name);
+ if ((params->flags & IB_VERBOSE) && board != NULL)
+ printf("Found board '%s' from DTB data.\n", board_name);
+#if !HAVE_NBTOOL_CONFIG_H
+ /*
+ * If the user specified a DTB, then regardless of the
+ * outcome, this is like specifying the board directly,
+ * so native checks should be skipped.
+ */
+ is_native = false;
+#endif /* ! HAVE_NBTOOL_CONFIG_H */
+ }
+#endif /* SUPPORT_FDT */
+
+#if !HAVE_NBTOOL_CONFIG_H
+ /*
+ * Non-host-tool logic for determining the board type goes here.
+ */
+
+#ifdef SUPPORT_OPENFIRMWARE
+ if (board_name == NULL && is_native) {
+ board = evb_db_get_board_from_ofw(params, &board_name);
+ if ((params->flags & IB_VERBOSE) && board != NULL)
+ printf("Found board '%s' from OFW data.\n", board_name);
+ }
+#endif /* SUPPORT_OPENFIRMWARE */
+
+ /* Ensure is_native is consumed. */
+ if (is_native == false)
+ is_native = false;
+
+#endif /* ! HAVE_NBTOOL_CONFIG_H */
+
+ /*
+ * If all else fails, we can always rely on the user, right?
+ */
+ if (board_name == NULL) {
+ if (!(params->flags & IB_BOARD)) {
+ warnx("Must specify board=...");
+ return NULL;
+ }
+ board_name = params->board;
+ }
+
+ assert(board_name != NULL);
+
+ if (board == NULL)
+ board = prop_dictionary_get(params->mach_data, board_name);
+ if (board == NULL)
+ warnx("Unknown board '%s'", board_name);
+
+ /* Ensure params->board is always valid. */
+ params->board = board_name;
+
+ if (params->flags & IB_VERBOSE) {
+ printf("Board: %s\n", evb_board_get_description(params, board));
+ }
+
+ return board;
+}
+
+/*
+ * evb_db_list_boards --
+ * Print the list of known boards to the specified output stream.
+ */
+void
+evb_db_list_boards(ib_params *params, FILE *out)
+{
+ prop_object_iterator_t iter;
+ prop_dictionary_keysym_t key;
+ evb_board board;
+ const char *uboot_pkg;
+ const char *uboot_path;
+
+ /*
+ * By default, we only list boards that we have a u-boot
+ * package installed for, or if we know which package you
+ * need to install. You get the full monty in verbose mode.
+ */
+
+ iter = prop_dictionary_iterator(params->mach_data);
+ while ((key = prop_object_iterator_next(iter)) != NULL) {
+ board = prop_dictionary_get_keysym(params->mach_data, key);
+ assert(board != NULL);
+ uboot_pkg = evb_board_get_uboot_pkg(params, board);
+ uboot_path = evb_board_get_uboot_path(params, board);
+
+ if (uboot_pkg == NULL && uboot_path == NULL &&
+ !(params->flags & IB_VERBOSE))
+ continue;
+
+ fprintf(out, "%-30s %s\n",
+ prop_dictionary_keysym_cstring_nocopy(key),
+ evb_board_get_description(params, board));
+
+ if ((params->flags & IB_VERBOSE) && uboot_path) {
+ fprintf(out, "\t(u-boot package found at %s)\n",
+ uboot_path);
+ } else if ((params->flags & IB_VERBOSE) && uboot_pkg) {
+ fprintf(out,
+ "\t(install the sysutils/u-boot-%s package)\n",
+ uboot_pkg);
+ }
+ }
+ prop_object_iterator_release(iter);
+}
+
+/*
+ * evb_board_get_description --
+ * Return the description for the specified board.
+ */
+const char *
+evb_board_get_description(ib_params *params, evb_board board)
+{
+ prop_string_t string;
+
+ string = prop_dictionary_get(board, board_description_key);
+ return prop_string_cstring_nocopy(string);
+}
+
+/*
+ * evb_board_get_uboot_pkg --
+ * Return the u-boot package name for the specified board.
+ */
+const char *
+evb_board_get_uboot_pkg(ib_params *params, evb_board board)
+{
+ prop_string_t string;
+
+ string = prop_dictionary_get(board, board_u_boot_pkg_key);
+ if (string == NULL)
+ return NULL;
+ return prop_string_cstring_nocopy(string);
+}
+
+/*
+ * evb_board_get_uboot_path --
+ * Return the u-boot installed package path for the specified board.
+ */
+const char *
+evb_board_get_uboot_path(ib_params *params, evb_board board)
+{
+ prop_string_t string;
+
+ string = prop_dictionary_get(board, board_u_boot_path_key);
+ if (string == NULL)
+ return NULL;
+ return prop_string_cstring_nocopy(string);
+}
+
+/*
+ * evb_board_get_uboot_install --
+ * Return the u-boot install object for the specified board,
+ * corresponding to the media specified by the user.
+ */
+evb_ubinstall
+evb_board_get_uboot_install(ib_params *params, evb_board board)
+{
+ evb_ubinstall install;
+
+ install = prop_dictionary_get(board, board_u_boot_install_key);
+
+ if (!(params->flags & IB_MEDIA)) {
+ if (install == NULL) {
+ warnx("Must specify media=... for board '%s'",
+ params->board);
+ goto list_media;
+ }
+ return install;
+ }
+
+ /* media=... was specified by the user. */
+
+ if (install) {
+ warnx("media=... is not a valid option for board '%s'",
+ params->board);
+ return NULL;
+ }
+
+ char install_key[128];
+ int n = snprintf(install_key, sizeof(install_key), "%s-%s",
+ board_u_boot_install_key, params->media);
+ if (n < 0 || (size_t)n >= sizeof(install_key))
+ goto invalid_media;;
+ install = prop_dictionary_get(board, install_key);
+ if (install != NULL)
+ return install;
+ invalid_media:
+ warnx("invalid media specification: '%s'", params->media);
+ list_media:
+ fprintf(stderr, "Valid media types:");
+ prop_array_t array = evb_board_copy_uboot_media(params, board);
+ assert(array != NULL);
+ prop_object_iterator_t iter = prop_array_iterator(array);
+ prop_string_t string;
+ while ((string = prop_object_iterator_next(iter)) != NULL)
+ fprintf(stderr, " %s", prop_string_cstring_nocopy(string));
+ fprintf(stderr, "\n");
+ prop_object_iterator_release(iter);
+ prop_object_release(array);
+
+ return NULL;
+}
+
+/*
+ * evb_board_copy_uboot_media --
+ * Return the valid media types for the given board as an array
+ * of strings.
+ *
+ * Follows the create rule; caller is responsible for releasing
+ * the array.
+ */
+prop_array_t
+evb_board_copy_uboot_media(ib_params *params, evb_board board)
+{
+ prop_array_t array = prop_array_create();
+ prop_object_iterator_t iter = prop_dictionary_iterator(board);
+ prop_string_t string;
+ prop_dictionary_keysym_t key;
+ const char *cp;
+
+ assert(array != NULL);
+ assert(iter != NULL);
+
+ while ((key = prop_object_iterator_next(iter)) != NULL) {
+ cp = prop_dictionary_keysym_cstring_nocopy(key);
+ if (strcmp(cp, board_u_boot_install_key) == 0 ||
+ strncmp(cp, board_u_boot_install_key,
+ sizeof(board_u_boot_install_key) - 1) != 0)
+ continue;
+ string = prop_string_create_cstring(strrchr(cp, '-')+1);
+ assert(string != NULL);
+ prop_array_add(array, string);
+ prop_object_release(string);
+ }
+ prop_object_iterator_release(iter);
+ return array;
+}
+
+/*
+ * evb_ubinstall_get_steps --
+ * Get the install steps for a given install object.
+ */
+evb_ubsteps
+evb_ubinstall_get_steps(ib_params *params, evb_ubinstall install)
+{
+ return prop_array_iterator(install);
+}
+
+/*
+ * evb_ubsteps_next_step --
+ * Return the next step in the install object.
+ *
+ * N.B. The iterator is released upon termination.
+ */
+evb_ubstep
+evb_ubsteps_next_step(ib_params *params, evb_ubsteps steps)
+{
+ prop_dictionary_t step = prop_object_iterator_next(steps);
+
+ /* If we are out of steps, release the iterator. */
+ if (step == NULL)
+ prop_object_iterator_release(steps);
+
+ return step;
+}
+
+/*
+ * evb_ubstep_get_file_name --
+ * Returns the input file name for the step.
+ */
+const char *
+evb_ubstep_get_file_name(ib_params *params, evb_ubstep step)
+{
+ prop_string_t string = prop_dictionary_get(step, step_file_name_key);
+ return prop_string_cstring_nocopy(string);
+}
+
+/*
+ * evb_ubstep_get_file_offset --
+ * Returns the input file offset for the step.
+ */
+uint64_t
+evb_ubstep_get_file_offset(ib_params *params, evb_ubstep step)
+{
+ prop_number_t number = prop_dictionary_get(step, step_file_offset_key);
+ if (number != NULL)
+ return prop_number_unsigned_integer_value(number);
+ return 0;
+}
+
+/*
+ * evb_ubstep_get_file_size --
+ * Returns the size of the input file to copy for this step, or
+ * zero if the remainder of the file should be copied.
+ */
+uint64_t
+evb_ubstep_get_file_size(ib_params *params, evb_ubstep step)
+{
+ prop_number_t number = prop_dictionary_get(step, step_file_size_key);
+ if (number != NULL)
+ return prop_number_unsigned_integer_value(number);
+ return 0;
+}
+
+/*
+ * evb_ubstep_get_image_offset --
+ * Returns the offset into the destination image / device to
+ * copy the input file.
+ */
+uint64_t
+evb_ubstep_get_image_offset(ib_params *params, evb_ubstep step)
+{
+ prop_number_t number = prop_dictionary_get(step, step_image_offset_key);
+ if (number != NULL)
+ return prop_number_unsigned_integer_value(number);
+ return 0;
+}
+
+/*
+ * evb_ubstep_preserves_partial_block --
+ * Returns true if the step preserves a partial block.
+ */
+bool
+evb_ubstep_preserves_partial_block(ib_params *params, evb_ubstep step)
+{
+ prop_bool_t val = prop_dictionary_get(step, step_preserve_key);
+ if (val != NULL)
+ return prop_bool_true(val);
+ return false;
+}
+
+/*
+ * evb_uboot_file_path --
+ * Build a file path from the u-boot base path in the board object
+ * and the file name in the step object.
+ */
+static const char *
+evb_uboot_file_path(ib_params *params, evb_board board, evb_ubstep step,
+ char *buf, size_t bufsize)
+{
+ const char *base_path = evb_board_get_uboot_path(params, board);
+ const char *file_name = evb_ubstep_get_file_name(params, step);
+
+ if (base_path == NULL || file_name == NULL)
+ return NULL;
+
+ return make_path(buf, bufsize, "%s/%s", base_path, file_name);
+}
+
+/*
+ * evb_uboot_do_step --
+ * Given a evb_ubstep, do the deed.
+ */
+static int
+evb_uboot_do_step(ib_params *params, const char *uboot_file, evb_ubstep step)
+{
+ struct stat sb;
+ int ifd = -1;
+ char *blockbuf;
+ size_t thisblock;
+ off_t curoffset;
+ off_t remaining;
+ bool rv = false;
+
+ uint64_t file_size = evb_ubstep_get_file_size(params, step);
+ uint64_t file_offset = evb_ubstep_get_file_offset(params, step);
+ uint64_t image_offset = evb_ubstep_get_image_offset(params, step);
+
+ blockbuf = malloc(params->sectorsize);
+ if (blockbuf == NULL)
+ goto out;
+
+ ifd = open(uboot_file, O_RDONLY);
+ if (ifd < 0) {
+ warn("open '%s'", uboot_file);
+ goto out;
+ }
+ if (fstat(ifd, &sb) < 0) {
+ warn("fstat '%s'", uboot_file);
+ goto out;
+ }
+
+ if (file_size)
+ remaining = (off_t)file_size;
+ else
+ remaining = sb.st_size - (off_t)file_offset;
+
+ if (params->flags & IB_VERBOSE) {
+ if (file_offset) {
+ printf("Writing '%s' -- %lld @ %" PRIu64 " ==> %" PRIu64 "\n",
+ evb_ubstep_get_file_name(params, step),
+ (long long)remaining, file_offset, image_offset);
+ } else {
+ printf("Writing '%s' -- %lld ==> %" PRIu64 "\n",
+ evb_ubstep_get_file_name(params, step),
+ (long long)remaining, image_offset);
+ }
+ }
+
+ if (lseek(ifd, (off_t)file_offset, SEEK_SET) < 0) {
+ warn("lseek '%s' @ %" PRIu64, uboot_file,
+ file_offset);
+ goto out;
+ }
+
+ for (curoffset = (off_t)image_offset; remaining > 0;
+ remaining -= thisblock, curoffset += params->sectorsize) {
+ thisblock = params->sectorsize;
+ if ((off_t)thisblock > remaining)
+ thisblock = (size_t)remaining;
+ if ((thisblock % params->sectorsize) != 0) {
+ memset(blockbuf, 0, params->sectorsize);
+ if (evb_ubstep_preserves_partial_block(params, step)) {
+ if (params->flags & IB_VERBOSE) {
+ printf("(Reading '%s' -- %u @ %lld)\n",
+ params->filesystem,
+ params->sectorsize,
+ (long long)curoffset);
+ }
+ if (pread(params->fsfd, blockbuf,
+ params->sectorsize, curoffset) < 0) {
+ warn("pread '%s'", params->filesystem);
+ goto out;
+ }
+ }
+ }
+ if (read(ifd, blockbuf, thisblock) != (ssize_t)thisblock) {
+ warn("read '%s'", uboot_file);
+ goto out;
+ }
+ if (!(params->flags & IB_NOWRITE) &&
+ pwrite(params->fsfd, blockbuf, params->sectorsize,
+ curoffset) != (ssize_t)params->sectorsize) {
+ warn("pwrite '%s'", params->filesystem);
+ goto out;
+ }
+ }
+
+ /* Success! */
+ rv = true;
+
+ out:
+ if (ifd != -1 && close(ifd) == -1)
+ warn("close '%s'", uboot_file);
+ if (blockbuf)
+ free(blockbuf);
+ return rv;
+}
+
+int
+evb_uboot_setboot(ib_params *params, evb_board board)
+{
+ char uboot_filebuf[PATH_MAX+1];
+ const char *uboot_file;
+ struct stat sb;
+ off_t max_offset = 0;
+
+ /*
+ * If we don't have a u-boot path for this board, it means
+ * that a u-boot package wasn't found. Prompt the user to
+ * install it.
+ */
+ if (evb_board_get_uboot_path(params, board) == NULL) {
+ warnx("No u-boot package found for board '%s'",
+ params->board);
+ uboot_file = evb_board_get_uboot_pkg(params, board);
+ if (uboot_file != NULL)
+ warnx("Please install the sysutils/u-boot-%s package.",
+ uboot_file);
+ return 0;
+ }
+
+ evb_ubinstall install = evb_board_get_uboot_install(params, board);
+ evb_ubsteps steps;
+ evb_ubstep step;
+
+ if (install == NULL)
+ return 0;
+
+ /*
+ * First, make sure the files are all there. While we're
+ * at it, calculate the largest byte offset that we will
+ * be writing.
+ */
+ steps = evb_ubinstall_get_steps(params, install);
+ while ((step = evb_ubsteps_next_step(params, steps)) != NULL) {
+ uint64_t file_offset = evb_ubstep_get_file_offset(params, step);
+ uint64_t file_size = evb_ubstep_get_file_size(params, step);
+ uint64_t image_offset =
+ evb_ubstep_get_image_offset(params, step);
+ uboot_file = evb_uboot_file_path(params, board, step,
+ uboot_filebuf, sizeof(uboot_filebuf));
+ if (uboot_file == NULL)
+ return 0;
+ if (stat(uboot_file, &sb) < 0) {
+ warn("%s", uboot_file);
+ return 0;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ warnx("%s: %s", uboot_file, strerror(EFTYPE));
+ return 0;
+ }
+ off_t this_max;
+ if (file_size)
+ this_max = file_size;
+ else
+ this_max = sb.st_size - file_offset;
+ this_max += image_offset;
+ if (max_offset < this_max)
+ max_offset = this_max;
+ }
+
+ /*
+ * Ok, we've verified that all of the files are there, and now
+ * max_offset points to the first byte that's available for a
+ * partition containing a file system.
+ */
+
+ off_t rounded_max_offset = (off_t)(max_offset / params->sectorsize) *
+ params->sectorsize;
+ if (rounded_max_offset != max_offset)
+ rounded_max_offset += params->sectorsize;
+
+ if (params->flags & IB_VERBOSE) {
+ printf("Max u-boot offset (rounded): %lld (%lld)\n",
+ (long long)max_offset, (long long)rounded_max_offset);
+ printf("First free block available for file systems: "
+ "%lld (0x%llx)\n",
+ (long long)rounded_max_offset / params->sectorsize,
+ (long long)rounded_max_offset / params->sectorsize);
+ }
+
+ /* XXX Check MBR table for overlapping partitions. */
+
+ /*
+ * Now write each binary component to the appropriate location
+ * on disk.
+ */
+ steps = evb_ubinstall_get_steps(params, install);
+ while ((step = evb_ubsteps_next_step(params, steps)) != NULL) {
+ uboot_file = evb_uboot_file_path(params, board, step,
+ uboot_filebuf, sizeof(uboot_filebuf));
+ if (uboot_file == NULL)
+ return 0;
+ if (!evb_uboot_do_step(params, uboot_file, step))
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/usr.sbin/installboot/evboards.h b/usr.sbin/installboot/evboards.h
new file mode 100644
index 00000000000..90569e560a6
--- /dev/null
+++ b/usr.sbin/installboot/evboards.h
@@ -0,0 +1,65 @@
+/* $NetBSD: evboards.h,v 1.1 2019/05/07 05:02:42 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2019 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef installboot_evboards_h_included
+#define installboot_evboards_h_included
+
+#include <stdio.h>
+#include <prop/proplib.h>
+
+typedef prop_dictionary_t evb_board;
+typedef prop_array_t evb_ubinstall;
+typedef prop_object_iterator_t evb_ubsteps;
+typedef prop_dictionary_t evb_ubstep;
+
+bool evb_db_load(ib_params *);
+evb_board evb_db_get_board(ib_params *);
+void evb_db_list_boards(ib_params *, FILE *);
+
+const char * evb_board_get_description(ib_params *, evb_board);
+const char * evb_board_get_uboot_pkg(ib_params *, evb_board);
+const char * evb_board_get_uboot_path(ib_params *, evb_board);
+evb_ubinstall evb_board_get_uboot_install(ib_params *, evb_board);
+prop_array_t evb_board_copy_uboot_media(ib_params *, evb_board);
+
+evb_ubsteps evb_ubinstall_get_steps(ib_params *, evb_ubinstall);
+
+evb_ubstep evb_ubsteps_next_step(ib_params *, evb_ubsteps);
+
+const char * evb_ubstep_get_file_name(ib_params *, evb_ubstep);
+uint64_t evb_ubstep_get_file_offset(ib_params *, evb_ubstep);
+uint64_t evb_ubstep_get_file_size(ib_params *, evb_ubstep);
+uint64_t evb_ubstep_get_image_offset(ib_params *, evb_ubstep);
+bool evb_ubstep_preserves_partial_block(ib_params *, evb_ubstep);
+
+int evb_uboot_setboot(ib_params *, evb_board);
+
+#endif /* installboot_evboards_h_included */
diff --git a/usr.sbin/installboot/installboot.8 b/usr.sbin/installboot/installboot.8
index e0936030660..94a1e36286c 100644
--- a/usr.sbin/installboot/installboot.8
+++ b/usr.sbin/installboot/installboot.8
@@ -1,6 +1,6 @@
-.\" $NetBSD: installboot.8,v 1.95 2017/08/15 21:21:18 wiz Exp $
+.\" $NetBSD: installboot.8,v 1.96 2019/05/07 05:02:42 thorpej Exp $
.\"
-.\" Copyright (c) 2002-2017 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2002-2019 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd March 14, 2017
+.Dd May 5, 2019
.Dt INSTALLBOOT 8
.Os
.Sh NAME
@@ -132,6 +132,28 @@ other platforms:
.Sy next68k .
.El
.Pp
+Some platforms, typically embedded system platforms, are umbrella platforms
+that support many different individual board types, each with their own
+boot loader binary and installation procedure requirements.
+On these platforms, it may be necessary to provide specific board type
+information to
+.Nm .
+Information about known boards and their requirements is loaded from a
+database at run-time.
+Sometimes these platforms also require the use of 3rd-party boot loader
+software, such as
+.Sy U-boot .
+To support these platforms,
+.Nm
+scans known locations for these 3rd-party boot loader packages for
+database overlays that contain additional board-specific boot loader
+installation information in a file called
+.Sq installboot.plist .
+.Pp
+The following platforms have this requirement and utilize this database
+overlay feature:
+.Sy evbarm .
+.Pp
The options and arguments recognized by
.Nm
are as follows:
@@ -202,6 +224,7 @@ The following machines are currently supported by
.Sy alpha ,
.Sy amd64 ,
.Sy amiga ,
+.Sy evbarm ,
.Sy ews4800mips ,
.Sy hp300 ,
.Sy hppa ,
@@ -250,6 +273,15 @@ to the end of
.Ar filesystem ,
which must be a regular file in this case.
.
+.It Sy board=<board name>
+.Sy [ evbarm ]
+Specify the board type used to determine the correct boot loader image
+and installation procedure.
+If omitted,
+.Nm
+will attempt to guess the board type based on system information if run
+natively.
+.
.It Sy bootconf
.Sy [ amd64 ,
.Sy i386 ]
@@ -267,6 +299,18 @@ Modify the default boot command line.
Set the console device, <console name> must be one of:
pc, com0, com1, com2, com3, com0kbd, com1kbd, com2kbd, com3kbd or auto.
.
+.It Sy dtb=<path to dtb file>
+.Sy [ evbarm ]
+Attempt to determine the board type from information in the device tree
+blob file at <path to dtb file>.
+If both
+.Sy board
+and
+.Sy dtb
+optios are specified,
+.Sy board
+takes precendence.
+.
.It Sy ioaddr=<ioaddr>
.Sy [ amd64 ,
.Sy i386 ]
@@ -285,6 +329,16 @@ would swap the lowercase letters
and
.Sq z .
.
+.It Sy media=<media type>
+.Sy [ evbarm ]
+Some boards require a different boot loader binary and/or installation
+procedure depending on what type of media will be used to boot the system.
+For such boards, this option is required, and omitting it will display a
+usage message that lists the valid media types for the board.
+For boards that do not require special media handling, this option is
+not allowed.
+Common values: sdmmc, emmc, usb.
+.
.It Sy modules
.Sy [ amd64 ,
.Sy i386 ]
@@ -418,6 +472,21 @@ uses the following environment variables:
.
.Bl -tag -width "MACHINE"
.
+.It Ev INSTALLBOOT_UBOOT_PATHS
+A colon-separated list of search paths to scan for
+.Sy U-boot
+packages with
+.Nm installboot
+installation overlays.
+If multiple overlays are found, overlays from paths closer to the front
+of the list take precensence.
+If not specified, the default path is
+.Pa /usr/pkg/share/u-boot .
+This environment variable is only used on platforms that support
+using
+.Sy U-boot :
+.Sy evbarm .
+.
.It Ev MACHINE
Default value for
.Ar machine ,
@@ -525,6 +594,30 @@ is not found.
.
.El
.
+.Ss Nx Ns /evbarm files
+The
+.Nx Ns /evbarm
+platform covers a wide variety of board types, many of which use
+.Sy U-boot .
+Running
+.Nm
+with no options will display a list of known boards.
+Using the verbose option will also display information about which
+.Sy U-boot
+package needs to be installed to support that board, and if the required
+.Sy U-boot
+package is installed, the path at which it is located.
+.Bl -tag -width /usr/pkg/share/u-boot
+.It Pa /usr/pkg/share/u-boot
+The default location scanned for
+.Sy U-boot
+packages with installation overlays.
+.It Pa /usr/share/installboot/evbarm/boards.plist
+Base board database, used to provide information about which
+.Sy U-boot
+package is required for a given board.
+.El
+.
.Ss Nx Ns /evbmips files
.
The
@@ -649,6 +742,27 @@ Modify the command line to change the default from "netbsd -ASn2" to
"netbsd -S":
.Dl Ic installboot -m amiga -o command="netbsd -S" /dev/rsd0a /usr/mdec/bootxx_ffs
.
+.Ss Nx Ns /evbarm
+Install the
+.Sy U-boot
+boot loader for a Pinebook into an image that will be written to
+an SDMMC card:
+.Dl Ic installboot -m evbarm -o board=pine64,pinebook arm64.img
+.Pp
+Install / update the
+.Sy U-boot
+boot loader for the current running system on the eMMC device
+.Sq ld0
+and display verbose information about the procedure:
+.Dl Ic installboot -v /dev/rld0c
+.Pp
+Install a specific
+.Sy U-boot
+package for a BeagleBone Black into an image that will be written
+to an SDMMC card:
+.Dl Ic installboot -m evbarm -o board=ti,am335x-bone-black armv7.img \
+ /path/to/experimental/u-boot/package
+.
.Ss Nx Ns /ews4800mips
Install the System V Boot File System primary bootstrap on to disk
.Sq sd0 ,
diff --git a/usr.sbin/installboot/installboot.c b/usr.sbin/installboot/installboot.c
index 33a45334442..cae5042df49 100644
--- a/usr.sbin/installboot/installboot.c
+++ b/usr.sbin/installboot/installboot.c
@@ -1,4 +1,4 @@
-/* $NetBSD: installboot.c,v 1.39 2015/07/25 10:37:22 mlelstv Exp $ */
+/* $NetBSD: installboot.c,v 1.40 2019/05/07 05:02:42 thorpej Exp $ */
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
#include <sys/cdefs.h>
#if !defined(__lint)
-__RCSID("$NetBSD: installboot.c,v 1.39 2015/07/25 10:37:22 mlelstv Exp $");
+__RCSID("$NetBSD: installboot.c,v 1.40 2019/05/07 05:02:42 thorpej Exp $");
#endif /* !__lint */
#include <sys/param.h>
@@ -56,6 +56,7 @@ __RCSID("$NetBSD: installboot.c,v 1.39 2015/07/25 10:37:22 mlelstv Exp $");
#endif
#include "installboot.h"
+#include "evboards.h"
static void getmachine(ib_params *, const char *, const char *);
static void getfstype(ib_params *, const char *, const char *);
@@ -92,6 +93,9 @@ const struct option {
{ "timeout", IB_TIMEOUT, OPT_INT, OFFSET(timeout) },
{ "modules", IB_MODULES, OPT_BOOL, 0 },
{ "bootconf", IB_BOOTCONF, OPT_BOOL, 0 },
+ { "board", IB_BOARD, OPT_STRING, OFFSET(board) },
+ { "dtb", IB_DTB, OPT_STRING, OFFSET(dtb) },
+ { "media", IB_MEDIA, OPT_WORD, OFFSET(media) },
{ .name = NULL },
};
#undef OFFSET
@@ -280,16 +284,32 @@ main(int argc, char *argv[])
}
}
+ assert(params->machine != NULL);
+
if (argc >= 2) {
if ((params->s1fd = open(argv[1], O_RDONLY, 0600)) == -1)
err(1, "Opening primary bootstrap `%s'", argv[1]);
if (fstat(params->s1fd, &params->s1stat) == -1)
err(1, "Examining primary bootstrap `%s'", argv[1]);
- if (!S_ISREG(params->s1stat.st_mode))
- errx(1, "`%s' must be a regular file", argv[1]);
+ if (!S_ISREG(params->s1stat.st_mode)) {
+ /*
+ * If the platform uses u-boot, then the stage1
+ * spec might be the directory where the u-boot
+ * binaries for the system are located.
+ */
+ if (params->machine->mach_flags & MF_UBOOT) {
+ if (!S_ISDIR(params->s1stat.st_mode)) {
+ errx(1, "`%s' must be a regular file "
+ "or a directory", argv[1]);
+ }
+ (void) close(params->s1fd);
+ params->s1fd = -1;
+ } else {
+ errx(1, "`%s' must be a regular file", argv[1]);
+ }
+ }
params->stage1 = argv[1];
}
- assert(params->machine != NULL);
if (params->flags & IB_VERBOSE) {
printf("File system: %s\n", params->filesystem);
@@ -299,9 +319,11 @@ main(int argc, char *argv[])
params->fstype->name, params->fstype->blocksize,
params->fstype->needswap);
if (!(params->flags & IB_EDIT))
- printf("Primary bootstrap: %s\n",
+ printf("Primary bootstrap: %s%s\n",
(params->flags & IB_CLEAR) ? "(to be cleared)"
- : params->stage1 ? params->stage1 : "(none)" );
+ : params->stage1 ? params->stage1 : "(none)",
+ S_ISDIR(params->s1stat.st_mode) ? " (directory)"
+ : "");
if (params->stage2 != NULL)
printf("Secondary bootstrap: %s\n", params->stage2);
}
@@ -313,9 +335,18 @@ main(int argc, char *argv[])
op = "Clear";
rv = params->machine->clearboot(params);
} else {
- if (argc < 2)
- errx(EXIT_FAILURE, "Please specify the primary "
- "bootstrap file");
+ if (argc < 2) {
+ /*
+ * If the platform uses u-boot, then the stage1 spec is
+ * optional iff they specified a board (because we can
+ * infer a default location for u-boot binaries if the
+ * board type is given).
+ */
+ if (!(params->machine->mach_flags & MF_UBOOT)) {
+ errx(EXIT_FAILURE,
+ "Please specify the primary bootstrap file");
+ }
+ }
op = "Set";
rv = params->machine->setboot(params);
}
@@ -332,7 +363,7 @@ main(int argc, char *argv[])
}
if (close(params->fsfd) == -1)
err(1, "Closing file system `%s'", params->filesystem);
- if (argc == 2)
+ if (params->s1fd != -1)
if (close(params->s1fd) == -1)
err(1, "Closing primary bootstrap `%s'",
params->stage1);
@@ -585,5 +616,8 @@ usage(void)
machine_usage();
fstype_usage();
options_usage();
+ if (installboot_params.machine != NULL &&
+ installboot_params.machine->usage != NULL)
+ installboot_params.machine->usage(&installboot_params);
exit(1);
}
diff --git a/usr.sbin/installboot/installboot.h b/usr.sbin/installboot/installboot.h
index 680edb2710f..e3d15198137 100644
--- a/usr.sbin/installboot/installboot.h
+++ b/usr.sbin/installboot/installboot.h
@@ -1,4 +1,4 @@
-/* $NetBSD: installboot.h,v 1.39 2014/02/24 07:23:44 skrll Exp $ */
+/* $NetBSD: installboot.h,v 1.40 2019/05/07 05:02:42 thorpej Exp $ */
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -67,8 +67,19 @@ typedef enum {
IB_CONSADDR = 1<<20, /* i386 console io address */
IB_MODULES = 1<<21, /* i386: load modules */
IB_BOOTCONF = 1<<22, /* i386: read boot.conf */
+
+ IB_BOARD = 1<<23, /* evb*: board specification */
+ IB_DTB= 1<<24, /* evb*: device tree blob */
+
+ /* IB_MEDIA is required for some evb*, but not all. */
+ IB_MEDIA = 1<<25, /* evb*: boot media type */
} ib_flags;
+typedef enum {
+
+ MF_UBOOT = 1<<0, /* platform (maybe) uses u-boot */
+} m_flags;
+
typedef struct {
ib_flags flags; /* flags (see above) */
struct ib_mach *machine; /* machine details (see below) */
@@ -91,6 +102,12 @@ typedef struct {
const char *password; /* boot password */
int timeout; /* interactive boot timeout */
const char *keymap; /* keyboard translations */
+ const char *board; /* board specification */
+ const char *dtb; /* dtb specification */
+ const char *media; /* boot media type */
+
+ /* temporary working data */
+ void *mach_data; /* platform-specific data */
} ib_params;
typedef struct {
@@ -103,7 +120,9 @@ struct ib_mach {
int (*setboot) (ib_params *);
int (*clearboot) (ib_params *);
int (*editboot) (ib_params *);
+ void (*usage) (ib_params *);
ib_flags valid_flags;
+ m_flags mach_flags;
};
struct ib_fs {
@@ -165,6 +184,7 @@ int ext2fs_findstage2(ib_params *, uint32_t *, ib_block *);
extern struct ib_mach ib_mach_alpha;
extern struct ib_mach ib_mach_amd64;
extern struct ib_mach ib_mach_amiga;
+extern struct ib_mach ib_mach_evbarm;
extern struct ib_mach ib_mach_ews4800mips;
extern struct ib_mach ib_mach_hp300;
extern struct ib_mach ib_mach_hppa;
diff --git a/usr.sbin/installboot/machines.c b/usr.sbin/installboot/machines.c
index 357652b83b7..7c3f4c3440d 100644
--- a/usr.sbin/installboot/machines.c
+++ b/usr.sbin/installboot/machines.c
@@ -1,4 +1,4 @@
-/* $NetBSD: machines.c,v 1.39 2014/02/24 07:23:44 skrll Exp $ */
+/* $NetBSD: machines.c,v 1.40 2019/05/07 05:02:42 thorpej Exp $ */
/*-
* Copyright (c) 2002-2005 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
#include <sys/cdefs.h>
#if !defined(__lint)
-__RCSID("$NetBSD: machines.c,v 1.39 2014/02/24 07:23:44 skrll Exp $");
+__RCSID("$NetBSD: machines.c,v 1.40 2019/05/07 05:02:42 thorpej Exp $");
#endif /* !__lint */
#include <sys/types.h>
@@ -51,6 +51,7 @@ struct ib_mach
ib_mach_amd64,
ib_mach_amiga,
ib_mach_emips,
+ ib_mach_evbarm,
ib_mach_ews4800mips,
ib_mach_hp300,
ib_mach_hppa,
@@ -73,6 +74,7 @@ struct ib_mach * const machines[] = {
&ib_mach_amd64,
&ib_mach_amiga,
&ib_mach_emips,
+ &ib_mach_evbarm,
&ib_mach_ews4800mips,
&ib_mach_hp300,
&ib_mach_hppa,