diff options
author | 2020-07-16 19:48:58 +0000 | |
---|---|---|
committer | 2020-07-16 19:48:58 +0000 | |
commit | a79842c90b600f90baeff83b9d7a012a21c20237 (patch) | |
tree | bae80f5ea5ffd092ba27d3e4adcb6a10297a7f7d | |
parent | regen (diff) | |
download | wireguard-openbsd-a79842c90b600f90baeff83b9d7a012a21c20237.tar.xz wireguard-openbsd-a79842c90b600f90baeff83b9d7a012a21c20237.zip |
First stab at a bootloader for OpenBSD/powerpc64.
Based on the same principle (and mostly copied from) the bootloader
written by visa@ for OpenBSD/octeon.
Needed because the petitboot environment provided by the OpenPower
firmware is unsuitable for loading OpenBSD kernels properly.
-rw-r--r-- | sys/arch/powerpc64/stand/Makefile | 7 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/Makefile.inc | 44 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/boot/Makefile | 39 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/Makefile | 19 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/cmd.c | 507 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/cmd.h | 65 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/disk.c | 208 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/disk.h | 21 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/rdboot.c | 245 | ||||
-rw-r--r-- | sys/arch/powerpc64/stand/rdboot/vars.c | 203 |
10 files changed, 1354 insertions, 4 deletions
diff --git a/sys/arch/powerpc64/stand/Makefile b/sys/arch/powerpc64/stand/Makefile index dfe244d4df3..2b496e0cf37 100644 --- a/sys/arch/powerpc64/stand/Makefile +++ b/sys/arch/powerpc64/stand/Makefile @@ -1,6 +1,5 @@ -# dummy -all: +# $OpenBSD: Makefile,v 1.2 2020/07/16 19:48:58 kettenis Exp $ -clean: +SUBDIR+= rdboot boot -includes: +.include <bsd.subdir.mk> diff --git a/sys/arch/powerpc64/stand/Makefile.inc b/sys/arch/powerpc64/stand/Makefile.inc new file mode 100644 index 00000000000..5f00292123a --- /dev/null +++ b/sys/arch/powerpc64/stand/Makefile.inc @@ -0,0 +1,44 @@ +# $OpenBSD: Makefile.inc,v 1.1 2020/07/16 19:48:58 kettenis Exp $ + +BINDIR= /usr/mdec + +STANDALONE?= -D_STANDALONE + +.if ${MACHINE} == "powerpc64" +CPPFLAGS+= ${STANDALONE} +CPPFLAGS+= -I. +CFLAGS+= -fno-stack-protector -Wall +CFLAGS+= -fno-builtin-vprintf -fno-builtin-printf -fno-builtin-putchar +# Silence warnings +CFLAGS+= -fno-builtin-snprintf +CFLAGS+= -fno-builtin-memcpy +CFLAGS+= -fno-builtin-memcmp +CFLAGS+= -fno-builtin-memset +CFLAGS+= -fno-builtin-strncpy +CFLAGS+= -fno-builtin-strncmp +CFLAGS+= -fno-builtin-exit +AS?= as +LD?= ld +.endif + +### Figure out what to use for libsa +LIBSADIR?= ${.CURDIR}/../libsa + +.if exists(${LIBSADIR}/${__objdir}) +LIBSAOBJDIR= ${LIBSADIR}/${__objdir} +.else +LIBSAOBJDIR= ${LIBSADIR} +.endif + +LIBSA= ${LIBSAOBJDIR}/libsa.a + +### Figure out what to use for libz +LIBZDIR?= ${.CURDIR}/../libz + +.if exists(${LIBZDIR}/${__objdir}) +LIBZOBJDIR= ${LIBZDIR}/${__objdir} +.else +LIBZOBJDIR= ${LIBZDIR} +.endif + +LIBZ= ${LIBZOBJDIR}/libz.a diff --git a/sys/arch/powerpc64/stand/boot/Makefile b/sys/arch/powerpc64/stand/boot/Makefile new file mode 100644 index 00000000000..0919efd524f --- /dev/null +++ b/sys/arch/powerpc64/stand/boot/Makefile @@ -0,0 +1,39 @@ +# $OpenBSD: Makefile,v 1.1 2020/07/16 19:48:58 kettenis Exp $ + +NOMAN= +#MAN= boot.8 + +.if ${MACHINE} == "powerpc64" +all: bsd rdboot + +bsd: + cd ${.CURDIR}/../../compile/BOOT && \ + ${MAKE} config && ${MAKE} clean && ${MAKE} + cp -p ${.CURDIR}/../../compile/BOOT/obj/bsd bsd + +rdboot: + cp -p ${.CURDIR}/../rdboot/obj/rdboot rdboot + strip rdboot + +mr.fs: rdboot + rm -rf $@.d + install -d -o root -g wheel $@.d/dev + install -d -o root -g wheel $@.d/mnt + install -d -o root -g wheel $@.d/sbin + install -o ${BINOWN} -g ${BINGRP} -m 555 rdboot $@.d/sbin/init + cd $@.d/dev && sh ${DESTDIR}/dev/MAKEDEV boot + makefs -o disklabel=rdboot,minfree=0,density=1024 $@ $@.d + +boot: bsd mr.fs + cp bsd boot + rdsetroot boot mr.fs + +realinstall: boot + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 boot ${DESTDIR}/usr/mdec + +clean: + rm -f boot bsd mr.fs rdboot + rm -rf mr.fs.d +.endif + +.include <bsd.prog.mk> diff --git a/sys/arch/powerpc64/stand/rdboot/Makefile b/sys/arch/powerpc64/stand/rdboot/Makefile new file mode 100644 index 00000000000..b99ed3689d6 --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1 2020/07/16 19:48:58 kettenis Exp $ + +NOMAN= + +.if ${MACHINE} == "powerpc64" +PROG= rdboot +SRCS= cmd.c disk.c rdboot.c vars.c +LDADD+= -lutil +LDSTATIC+= -static +.else +NOPROG= +.endif + +.PATH: ${S}/lib/libsa +#SRCS+= hexdump.c strtoll.c + +install: + +.include <bsd.prog.mk> diff --git a/sys/arch/powerpc64/stand/rdboot/cmd.c b/sys/arch/powerpc64/stand/rdboot/cmd.c new file mode 100644 index 00000000000..ed93604aae1 --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/cmd.c @@ -0,0 +1,507 @@ +/* $OpenBSD: cmd.c,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 1997-1999 Michael Shalayeff + * All rights reserved. + * + * 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 AUTHOR ``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 REGENTS 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. + */ + +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/select.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "cmd.h" +#include "disk.h" + +static int Xboot(void); +static int Xecho(void); +static int Xhelp(void); +static int Xls(void); +static int Xnop(void); +static int Xreboot(void); +#ifdef MACHINE_CMD +static int Xmachine(void); +extern const struct cmd_table MACHINE_CMD[]; +#endif +extern int Xset(void); + +#ifdef CHECK_SKIP_CONF +extern int CHECK_SKIP_CONF(void); +#endif + +extern const struct cmd_table cmd_set[]; +const struct cmd_table cmd_table[] = { + {"#", CMDT_CMD, Xnop}, /* XXX must be first */ + {"boot", CMDT_CMD, Xboot}, + {"echo", CMDT_CMD, Xecho}, + {"help", CMDT_CMD, Xhelp}, + {"ls", CMDT_CMD, Xls}, +#ifdef MACHINE_CMD + {"machine",CMDT_MDC, Xmachine}, +#endif + {"reboot", CMDT_CMD, Xreboot}, + {"set", CMDT_SET, Xset}, + {NULL, 0}, +}; + +static void ls(const char *, struct stat *); +static int readline(char *, size_t, int); +char *nextword(char *); +static char *whatcmd(const struct cmd_table **ct, char *); +static char *qualify(char *); + +char cmd_buf[CMD_BUFF_SIZE]; + +int +getcmd(void) +{ + cmd.cmd = NULL; + + if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout)) + cmd.cmd = cmd_table; + + return docmd(); +} + +int +read_conf(void) +{ + struct stat sb; + const char *path; + int fd, rc = 0; + +#ifdef CHECK_SKIP_CONF + if (CHECK_SKIP_CONF()) { + printf("boot.conf processing skipped at operator request\n"); + cmd.timeout = 0; + return -1; /* Pretend file wasn't found */ + } +#endif + + path = disk_open(qualify(cmd.conf)); + if (path == NULL) { + fprintf(stderr, "cannot open device for reading %s: %s\n", + cmd.conf, strerror(errno)); + return -1; + } + if ((fd = open(path, O_RDONLY)) == -1) { + if (errno != ENOENT && errno != ENXIO) { + fprintf(stderr, "%s: open(%s): %s\n", __func__, + cmd.path, strerror(errno)); + rc = 0; + } else + rc = -1; + goto out; + } + + (void) fstat(fd, &sb); + if (sb.st_uid || (sb.st_mode & 2)) { + fprintf(stderr, "non-secure %s, will not proceed\n", cmd.path); + rc = -1; + goto out; + } + + do { + char *p = cmd_buf; + + cmd.cmd = NULL; + do { + rc = read(fd, p, 1); + } while (rc > 0 && *p++ != '\n' && + (p-cmd_buf) < sizeof(cmd_buf)); + + if (rc < 0) { /* Error from read() */ + fprintf(stderr, "%s: %s\n", cmd.path, strerror(errno)); + break; + } + + if (rc == 0) { /* eof from read() */ + if (p != cmd_buf) { /* Line w/o trailing \n */ + *p = '\0'; + rc = docmd(); + break; + } + } else { /* rc > 0, read a char */ + p--; /* Get back to last character */ + + if (*p != '\n') { /* Line was too long */ + fprintf(stderr, "%s: line too long\n", + cmd.path); + + /* Don't want to run the truncated command */ + rc = -1; + } + *p = '\0'; + } + } while (rc > 0 && !(rc = docmd())); + +out: + if (fd != -1) + close(fd); + disk_close(); + return rc; +} + +int +docmd(void) +{ + char *p = NULL; + const struct cmd_table *ct = cmd_table, *cs; + + cmd.argc = 1; + if (cmd.cmd == NULL) { + + /* command */ + for (p = cmd_buf; *p == ' ' || *p == '\t'; p++) + ; + if (*p == '#' || *p == '\0') { /* comment or empty string */ +#ifdef DEBUG + printf("rem\n"); +#endif + return 0; + } + ct = cmd_table; + cs = NULL; + cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */ + p = whatcmd(&ct, p); + if (ct == NULL) { + cmd.argc++; + ct = cmd_table; + } else if (ct->cmd_type == CMDT_SET && p != NULL) { + cs = cmd_set; +#ifdef MACHINE_CMD + } else if (ct->cmd_type == CMDT_MDC && p != NULL) { + cs = MACHINE_CMD; +#endif + } + + if (cs != NULL) { + p = whatcmd(&cs, p); + if (cs == NULL) { + printf("%s: syntax error\n", ct->cmd_name); + return 0; + } + ct = cs; + } + cmd.cmd = ct; + } + + cmd.argv[0] = ct->cmd_name; + while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) { + cmd.argv[cmd.argc++] = p; + p = nextword(p); + } + cmd.argv[cmd.argc] = NULL; + + return (*cmd.cmd->cmd_exec)(); +} + +static char * +whatcmd(const struct cmd_table **ct, char *p) +{ + char *q; + int l; + + q = nextword(p); + + for (l = 0; p[l]; l++) + ; + + while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l)) + (*ct)++; + + if ((*ct)->cmd_name == NULL) + *ct = NULL; + + return q; +} + +static int +readline(char *buf, size_t n, int to) +{ + struct termios saved_tio, tio; + struct timeval tv; + fd_set fdset; + char *p; + int timed_out = 0; +#ifdef DEBUG + extern int debug; +#endif + + /* Only do timeout if greater than 0 */ + if (to > 0) { + /* Switch to non-canonical mode for timeout detection. */ + tcgetattr(STDIN_FILENO, &saved_tio); + tio = saved_tio; + tio.c_lflag &= ~(ECHO | ICANON); + tcsetattr(STDIN_FILENO, TCSANOW, &tio); + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); + tv.tv_sec = to; + tv.tv_usec = 0; + if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &tv) == 0) + timed_out = 1; + + /* Restore canonical mode. */ + tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio); + + if (timed_out) { + strlcpy(buf, "boot", 5); + putchar('\n'); + return strlen(buf); + } + } + + /* User has typed something. Turn off timeouts. */ + cmd.timeout = 0; + + if (fgets(buf, n, stdin) == NULL) + return 0; + + /* Strip trailing newline. */ + p = strchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + + return strlen(buf); +} + +/* + * Search for spaces/tabs after the current word. If found, \0 the + * first one. Then pass a pointer to the first character of the + * next word, or NULL if there is no next word. + */ +char * +nextword(char *p) +{ + /* skip blanks */ + while (*p && *p != '\t' && *p != ' ') + p++; + if (*p) { + *p++ = '\0'; + while (*p == '\t' || *p == ' ') + p++; + } + if (*p == '\0') + p = NULL; + return p; +} + +static void +print_help(const struct cmd_table *ct) +{ + for (; ct->cmd_name != NULL; ct++) + printf(" %s", ct->cmd_name); + putchar('\n'); +} + +static int +Xhelp(void) +{ + printf("commands:"); + print_help(cmd_table); +#ifdef MACHINE_CMD + return Xmachine(); +#else + return 0; +#endif +} + +#ifdef MACHINE_CMD +static int +Xmachine(void) +{ + printf("machine:"); + print_help(MACHINE_CMD); + return 0; +} +#endif + +static int +Xecho(void) +{ + int i; + + for (i = 1; i < cmd.argc; i++) + printf("%s ", cmd.argv[i]); + putchar('\n'); + return 0; +} + +static int +Xls(void) +{ + struct stat sb; + const char *path; + DIR *dir; + struct dirent *dent; + int dirfd, oldcwd; + + path = disk_open(qualify(cmd.argv[1] ? cmd.argv[1] : "/.")); + if (path == NULL) + return 0; + + if (stat(path, &sb) < 0) { + printf("stat(%s): %s\n", cmd.path, strerror(errno)); + goto out; + } + + if ((sb.st_mode & S_IFMT) != S_IFDIR) + ls(path, &sb); + else { + oldcwd = open(".", O_RDONLY); + + dirfd = open(path, O_RDONLY); + if (dirfd < 0) { + printf("opendir(%s): %s\n", cmd.path, strerror(errno)); + close(oldcwd); + goto out; + } + if ((dir = fdopendir(dirfd)) < 0) { + printf("opendir(%s): %s\n", cmd.path, strerror(errno)); + close(dirfd); + close(oldcwd); + goto out; + } + fchdir(dirfd); + while ((dent = readdir(dir)) != NULL) { + if (fstatat(dirfd, dent->d_name, &sb, + AT_SYMLINK_NOFOLLOW) < 0) + printf("stat(%s): %s\n", dent->d_name, + strerror(errno)); + else + ls(dent->d_name, &sb); + } + closedir(dir); + + fchdir(oldcwd); + close(oldcwd); + } + +out: + disk_close(); + return 0; +} + +#define lsrwx(mode,s) \ + putchar ((mode) & S_IROTH? 'r' : '-'); \ + putchar ((mode) & S_IWOTH? 'w' : '-'); \ + putchar ((mode) & S_IXOTH? *(s): (s)[1]); + +static void +ls(const char *name, struct stat *sb) +{ + putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]); + lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-")); + lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-")); + lsrwx(sb->st_mode , (sb->st_mode & S_ISTXT? "tT" : "x-")); + + printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid, + (u_long)sb->st_size, name); +} +#undef lsrwx + +int doboot = 1; + +static int +Xnop(void) +{ + if (doboot) { + doboot = 0; + return (Xboot()); + } + + return 0; +} + +static int +Xboot(void) +{ + if (cmd.argc > 1 && cmd.argv[1][0] != '-') { + qualify((cmd.argv[1]? cmd.argv[1]: cmd.image)); + if (bootparse(2)) + return 0; + } else { + if (bootparse(1)) + return 0; + snprintf(cmd.path, sizeof cmd.path, "%s:%s", + cmd.bootdev, cmd.image); + } + + return 1; +} + +/* + * Qualifies the path adding necessary dev + */ + +static char * +qualify(char *name) +{ + char *p; + + for (p = name; *p; p++) + if (*p == ':') + break; + if (*p == ':') + strlcpy(cmd.path, name, sizeof(cmd.path)); + else + snprintf(cmd.path, sizeof cmd.path, "%s:%s", + cmd.bootdev, name); + return cmd.path; +} + +static int +Xreboot(void) +{ + printf("Rebooting...\n"); + reboot(0); + return 0; /* just in case */ +} + +int +upgrade(void) +{ + struct stat sb; + const char *path; + int ret = 0; + + path = disk_open(qualify("/bsd.upgrade")); + if (path == NULL) + return 0; + if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode)) + ret = 1; + disk_close(); + + return ret; +} diff --git a/sys/arch/powerpc64/stand/rdboot/cmd.h b/sys/arch/powerpc64/stand/rdboot/cmd.h new file mode 100644 index 00000000000..1bbe0af07c9 --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/cmd.h @@ -0,0 +1,65 @@ +/* $OpenBSD: cmd.h,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 1997 Michael Shalayeff + * All rights reserved. + * + * 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 AUTHOR ``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 REGENTS 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. + * + */ + +#define CMD_BUFF_SIZE 133 +#define BOOTDEVLEN 1024 + +struct cmd_table { + char *cmd_name; + char cmd_type; +#define CMDT_CMD 0 +#define CMDT_VAR 1 +#define CMDT_SET 2 +#define CMDT_MDC 3 + int (*cmd_exec)(void); +}; + +struct cmd_state { + char bootdev[BOOTDEVLEN]; /* device */ + char image[MAXPATHLEN - 16]; /* image */ + unsigned char bootduid[8]; /* duid of root disk */ + int boothowto; /* howto */ + int hasduid; + char *conf; /* /etc/boot.conf normally */ + int timeout; + + char path[MAXPATHLEN]; /* buffer for pathname compose */ + const struct cmd_table *cmd; + int argc; + char *argv[8]; /* XXX i hope this is enough */ +}; +extern struct cmd_state cmd; + +int getcmd(void); +int read_conf(void); +int bootparse(int); +void boot(dev_t); + +int upgrade(void); +int docmd(void); /* No longer static: needed by regress test */ diff --git a/sys/arch/powerpc64/stand/rdboot/disk.c b/sys/arch/powerpc64/stand/rdboot/disk.c new file mode 100644 index 00000000000..73298dd165f --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/disk.c @@ -0,0 +1,208 @@ +/* $OpenBSD: disk.c,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * 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. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/disklabel.h> +#include <sys/dkio.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <util.h> + +#include "cmd.h" + +int disk_proberoot(const char *); + +int mounted = 0; +int rdroot = -1; /* fd that points to the root of the ramdisk */ + +void +disk_init(void) +{ + char rootdevs[1024]; + char *devname, *disknames, *ptr; + size_t size; + int mib[2]; + + rdroot = open("/", O_RDONLY); + if (rdroot == -1) + err(1, "failed to open root directory fd"); + + if (strlen(cmd.bootdev) != 0) + return; + + mib[0] = CTL_HW; + mib[1] = HW_DISKNAMES; + size = 0; + if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1) { + fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__, + strerror(errno)); + return; + } + disknames = malloc(size); + if (disknames == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + return; + } + if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1) { + fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__, + strerror(errno)); + free(disknames); + return; + } + + printf("probing disks\n"); + rootdevs[0] = '\0'; + ptr = disknames; + while ((devname = strsep(&ptr, ",")) != NULL) { + char *duid; + + duid = strchr(devname, ':'); + if (duid == NULL) + continue; + *duid++ = '\0'; + + /* Disk without a duid cannot be a root device. */ + if (strlen(duid) == 0) + continue; + + if (disk_proberoot(devname)) { + if (strlen(cmd.bootdev) == 0) { + snprintf(cmd.bootdev, sizeof(cmd.bootdev), + "%sa", devname); + } + (void)strlcat(rootdevs, " ", sizeof(rootdevs)); + (void)strlcat(rootdevs, devname, sizeof(rootdevs)); + } + } + if (strlen(rootdevs) != 0) + printf("available root devices:%s\n", rootdevs); + else + printf("no root devices found\n"); +} + +int +disk_proberoot(const char *devname) +{ + static const char *const names[] = { + "bin", "dev", "etc", "home", "mnt", "root", "sbin", "tmp", + "usr", "var", NULL + }; + struct ufs_args ffs_args; + struct stat st; + char path[32]; + int i, is_root = 1; + + snprintf(path, sizeof(path), "/dev/%sa", devname); + memset(&ffs_args, 0, sizeof(ffs_args)); + ffs_args.fspec = path; + if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1) + return 0; + for (i = 0; names[i] != NULL; i++) { + snprintf(path, sizeof(path), "/mnt/%s", names[i]); + if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { + is_root = 0; + break; + } + } + (void)unmount("/mnt", 0); + + return is_root; +} + +const char * +disk_open(const char *path) +{ + struct ufs_args ffs_args; + struct disklabel label; + char devname[32]; + char *devpath; + const char *ptr; + int fd; + + if (mounted) { + fprintf(stderr, "%s: cannot nest\n", __func__); + return NULL; + } + + ptr = strchr(path, ':'); + if (ptr != NULL) { + snprintf(devname, sizeof(devname), "%.*s", + (int)(ptr - path), path); + ptr++; /* skip ':' */ + } else { + strlcpy(devname, cmd.bootdev, sizeof(devname)); + ptr = path; + } + if (strlen(devname) == 0) { + fprintf(stderr, "no device specified\n"); + return NULL; + } + + cmd.hasduid = 0; + fd = opendev(devname, O_RDONLY, OPENDEV_BLCK, &devpath); + if (fd != -1) { + if (ioctl(fd, DIOCGDINFO, &label) != -1) { + memcpy(cmd.bootduid, label.d_uid, 8); + cmd.hasduid = 1; + } + close(fd); + } else { + fprintf(stderr, "failed to open device %s: %s\n", devname, + strerror(errno)); + return NULL; + } + + memset(&ffs_args, 0, sizeof(ffs_args)); + ffs_args.fspec = devpath; + if (mount(MOUNT_FFS, "/mnt", MNT_FORCE | MNT_NOATIME, + &ffs_args) == -1) { + fprintf(stderr, "failed to mount %s: %s\n", devpath, + strerror(errno)); + return NULL; + } + if (chroot("/mnt") == -1) { + fprintf(stderr, "failed to chroot: %s\n", strerror(errno)); + (void)unmount("/mnt", 0); + return NULL; + } + mounted = 1; + + return ptr; +} + +void +disk_close(void) +{ + if (mounted) { + (void)fchdir(rdroot); + (void)chroot("."); + mounted = 0; + (void)unmount("/mnt", 0); + } +} diff --git a/sys/arch/powerpc64/stand/rdboot/disk.h b/sys/arch/powerpc64/stand/rdboot/disk.h new file mode 100644 index 00000000000..4c01e33ca82 --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/disk.h @@ -0,0 +1,21 @@ +/* $OpenBSD: disk.h,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * 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. + */ + +void disk_init(void); +const char *disk_open(const char *); +void disk_close(void); diff --git a/sys/arch/powerpc64/stand/rdboot/rdboot.c b/sys/arch/powerpc64/stand/rdboot/rdboot.c new file mode 100644 index 00000000000..704e8945181 --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/rdboot.c @@ -0,0 +1,245 @@ +/* $OpenBSD: rdboot.c,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 2019-2020 Visa Hankala + * + * 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. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/reboot.h> +#include <sys/select.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <util.h> + +#include <machine/kexec.h> +#include <machine/param.h> + +#include "cmd.h" +#include "disk.h" + +#define DEVRANDOM "/dev/random" +#define BOOTRANDOM "/etc/random.seed" +#define BOOTRANDOM_MAX 256 /* no point being greater than RC4STATE */ +#define KERNEL "/bsd" + +int loadrandom(void); +void kexec(void); + +struct cmd_state cmd; +int kexecfd = -1; +const char version[] = "0.1"; + +int +main(void) +{ + char bootduid[8]; + int fd, hasboot; + + fd = open(_PATH_CONSOLE, O_RDWR); + login_tty(fd); + + /* Keep stdout unbuffered to mimic ordinary bootblocks. */ + setvbuf(stdout, NULL, _IONBF, 0); + + printf(">> OpenBSD/" MACHINE " BOOT %s\n", version); + + kexecfd = open("/dev/kexec", O_WRONLY); + if (kexecfd == -1) + err(1, "cannot open boot control device"); + + memset(&cmd, 0, sizeof(cmd)); + cmd.boothowto = 0; + cmd.conf = "/etc/boot.conf"; + strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); + cmd.timeout = 5; + +#if 0 + if (ioctl(kexecfd, KIOC_GETBOOTDUID, bootduid) == -1) { + if (errno != ENOENT) + fprintf(stderr, "cannot get bootduid from kernel: %s\n", + strerror(errno)); + } else { + snprintf(cmd.bootdev, sizeof(cmd.bootdev), "%s%sa", + rootdev, isduid(rootdev, OPENDEV_PART) ? "." : ""); + } +#endif + + disk_init(); + + if (upgrade()) { + strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image)); + printf("upgrade detected: switching to %s\n", cmd.image); + } + + hasboot = read_conf(); + + for (;;) { + if (hasboot <= 0) { + do { + printf("boot> "); + } while (!getcmd()); + } + + if (loadrandom() == 0) + cmd.boothowto |= RB_GOODRANDOM; + + kexec(); + + hasboot = 0; + strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); + printf("will try %s\n", cmd.image); + } + + return 0; +} + +int +loadrandom(void) +{ + char buf[BOOTRANDOM_MAX]; + struct stat sb; + int fd, ret = 0; + + /* Read the file from the device specified by the kernel path. */ + if (disk_open(cmd.path) == NULL) + return -1; + fd = open(BOOTRANDOM, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM, + strerror(errno)); + disk_close(); + return -1; + } + if (fstat(fd, &sb) == 0) { + if (sb.st_mode & S_ISTXT) { + printf("NOTE: random seed is being reused.\n"); + ret = -1; + } + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + ret = -1; + fchmod(fd, sb.st_mode | S_ISTXT); + } else { + ret = -1; + } + close(fd); + disk_close(); + + /* + * Push the whole buffer to the entropy pool. + * The kernel will use the entropy on kexec(). + * It does not matter if some of the buffer content is uninitialized. + */ + fd = open(DEVRANDOM, O_WRONLY); + if (fd == -1) { + fprintf(stderr, "%s: cannot open %s: %s", __func__, + DEVRANDOM, strerror(errno)); + return -1; + } + write(fd, buf, sizeof(buf)); + close(fd); + return ret; +} + +void +kexec(void) +{ + struct kexec_args kargs; + struct stat sb; + char boothowtostr[32]; + char rootdev[32]; + char *kimg = NULL; + const char *path; + ssize_t n; + off_t pos; + int argc, fd = -1, ret; + + path = disk_open(cmd.path); + if (path == NULL) + return; + + fd = open(path, O_RDONLY); + if (fd == -1) + goto load_failed; + if (fstat(fd, &sb) == -1) + goto load_failed; + if (!S_ISREG(sb.st_mode) || sb.st_size == 0) { + errno = ENOEXEC; + goto load_failed; + } + + kimg = malloc(sb.st_size); + if (kimg == NULL) + goto load_failed; + + pos = 0; + while (pos < sb.st_size) { + n = read(fd, kimg + pos, sb.st_size - pos); + if (n == -1) + goto load_failed; + pos += n; + } + + close(fd); + disk_close(); + + memset(&kargs, 0, sizeof(kargs)); + kargs.kimg = kimg; + kargs.klen = sb.st_size; + argc = 0; + if (cmd.boothowto != 0) { + snprintf(boothowtostr, sizeof(boothowtostr), "boothowto=%d", + cmd.boothowto); + kargs.argv[argc++] = boothowtostr; + } + if (cmd.hasduid) { + snprintf(rootdev, sizeof(rootdev), + "rootdev=%02x%02x%02x%02x%02x%02x%02x%02x", + cmd.bootduid[0], cmd.bootduid[1], + cmd.bootduid[2], cmd.bootduid[3], + cmd.bootduid[4], cmd.bootduid[5], + cmd.bootduid[6], cmd.bootduid[7]); + kargs.argv[argc++] = rootdev; + } + + printf("booting %s\n", cmd.path); + ret = ioctl(kexecfd, KIOC_KEXEC, &kargs); + if (ret == -1) + fprintf(stderr, "failed to execute kernel %s: %s\n", + cmd.path, strerror(errno)); + else + fprintf(stderr, "kexec() returned unexpectedly\n"); + free(kimg); + return; + +load_failed: + fprintf(stderr, "failed to load kernel %s: %s\n", + cmd.path, strerror(errno)); + if (fd != -1) + close(fd); + disk_close(); + free(kimg); +} diff --git a/sys/arch/powerpc64/stand/rdboot/vars.c b/sys/arch/powerpc64/stand/rdboot/vars.c new file mode 100644 index 00000000000..a923e35184a --- /dev/null +++ b/sys/arch/powerpc64/stand/rdboot/vars.c @@ -0,0 +1,203 @@ +/* $OpenBSD: vars.c,v 1.1 2020/07/16 19:48:58 kettenis Exp $ */ + +/* + * Copyright (c) 1998-2000 Michael Shalayeff + * All rights reserved. + * + * 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 AUTHOR ``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 REGENTS 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 MIND, 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. + * + */ + +#include <sys/param.h> +#include <sys/reboot.h> + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "cmd.h" + +extern int debug; +int db_console = -1; + +static int Xdevice(void); +#ifdef DEBUG +static int Xdebug(void); +#endif +static int Xdb_console(void); +static int Ximage(void); +static int Xhowto(void); +static int Xtimeout(void); +int Xset(void); + +const struct cmd_table cmd_set[] = { + {"howto", CMDT_VAR, Xhowto}, +#ifdef DEBUG + {"debug", CMDT_VAR, Xdebug}, +#endif + {"device", CMDT_VAR, Xdevice}, + {"image", CMDT_VAR, Ximage}, + {"timeout",CMDT_VAR, Xtimeout}, + {"db_console", CMDT_VAR, Xdb_console}, + {NULL,0} +}; + +#ifdef DEBUG +static int +Xdebug(void) +{ + if (cmd.argc != 2) + printf( "o%s\n", debug? "n": "ff" ); + else + debug = (cmd.argv[1][0] == '0' || + (cmd.argv[1][0] == 'o' && cmd.argv[1][1] == 'f'))? + 0: 1; + return 0; +} +#endif + +int +Xdb_console(void) +{ + if (cmd.argc != 2) { + switch (db_console) { + case 0: + printf("off\n"); + break; + case 1: + printf("on\n"); + break; + default: + printf("unset\n"); + break; + } + } else { + if (strcmp(cmd.argv[1], "0") == 0 || + strcmp(cmd.argv[1], "off") == 0) + db_console = 0; + else if (strcmp(cmd.argv[1], "1") == 0 || + strcmp(cmd.argv[1], "on") == 0) + db_console = 1; + } + + return (0); +} + +static int +Xtimeout(void) +{ + if (cmd.argc != 2) + printf( "%d\n", cmd.timeout ); + else + cmd.timeout = (int)strtol( cmd.argv[1], (char **)NULL, 0 ); + return 0; +} + +/* called only w/ no arguments */ +int +Xset(void) +{ + const struct cmd_table *ct; + + printf("boot\n"); + for (ct = cmd_set; ct->cmd_name != NULL; ct++) { + printf("%s\t ", ct->cmd_name); + (*ct->cmd_exec)(); + } + return 0; +} + +static int +Xdevice(void) +{ + if (cmd.argc != 2) + printf("%s\n", cmd.bootdev); + else + strlcpy(cmd.bootdev, cmd.argv[1], sizeof(cmd.bootdev)); + return 0; +} + +static int +Ximage(void) +{ + if (cmd.argc != 2) + printf("%s\n", cmd.image); + else + strlcpy(cmd.image, cmd.argv[1], sizeof(cmd.image)); + return 0; +} + +static int +Xhowto(void) +{ + if (cmd.argc == 1) { + if (cmd.boothowto) { + putchar('-'); + if (cmd.boothowto & RB_ASKNAME) + putchar('a'); + if (cmd.boothowto & RB_CONFIG) + putchar('c'); + if (cmd.boothowto & RB_SINGLE) + putchar('s'); + if (cmd.boothowto & RB_KDB) + putchar('d'); + } + putchar('\n'); + } else + bootparse(1); + return 0; +} + +int +bootparse(int i) +{ + char *cp; + int howto = cmd.boothowto; + + for (; i < cmd.argc; i++) { + cp = cmd.argv[i]; + if (*cp == '-') { + while (*++cp) { + switch (*cp) { + case 'a': + howto |= RB_ASKNAME; + break; + case 'c': + howto |= RB_CONFIG; + break; + case 's': + howto |= RB_SINGLE; + break; + case 'd': + howto |= RB_KDB; + break; + default: + printf("howto: bad option: %c\n", *cp); + return 1; + } + } + } + } + cmd.boothowto = howto; + return 0; +} |