1*d807f41cSkn /* $OpenBSD: rdboot.c,v 1.9 2023/10/20 19:55:49 kn Exp $ */
23a62b615Svisa
33a62b615Svisa /*
4cf939257Svisa * Copyright (c) 2019-2020 Visa Hankala
53a62b615Svisa *
63a62b615Svisa * Permission to use, copy, modify, and/or distribute this software for any
73a62b615Svisa * purpose with or without fee is hereby granted, provided that the above
83a62b615Svisa * copyright notice and this permission notice appear in all copies.
93a62b615Svisa *
103a62b615Svisa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113a62b615Svisa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123a62b615Svisa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133a62b615Svisa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143a62b615Svisa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153a62b615Svisa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163a62b615Svisa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173a62b615Svisa */
183a62b615Svisa
193a62b615Svisa #include <sys/types.h>
203a62b615Svisa #include <sys/param.h>
213a62b615Svisa #include <sys/ioctl.h>
223a62b615Svisa #include <sys/mount.h>
233a62b615Svisa #include <sys/reboot.h>
243a62b615Svisa #include <sys/select.h>
25cf939257Svisa #include <sys/stat.h>
263a62b615Svisa
273a62b615Svisa #include <err.h>
283a62b615Svisa #include <errno.h>
293a62b615Svisa #include <fcntl.h>
303a62b615Svisa #include <paths.h>
313a62b615Svisa #include <stdio.h>
32cf939257Svisa #include <stdlib.h>
333a62b615Svisa #include <string.h>
343a62b615Svisa #include <termios.h>
353a62b615Svisa #include <unistd.h>
363a62b615Svisa #include <util.h>
373a62b615Svisa
383a62b615Svisa #include <machine/octboot.h>
393a62b615Svisa #include <machine/param.h>
403a62b615Svisa
413a62b615Svisa #include "cmd.h"
423a62b615Svisa #include "disk.h"
433a62b615Svisa
443a62b615Svisa #define DEVRANDOM "/dev/random"
453a62b615Svisa #define BOOTRANDOM "/etc/random.seed"
46b4bcf630Sderaadt #define BOOTRANDOM_MAX 256 /* no point being greater than RC4STATE */
473a62b615Svisa #define KERNEL "/bsd"
483a62b615Svisa
49eb518625Svisa int loadrandom(void);
50*d807f41cSkn void kexec(int);
513a62b615Svisa
523a62b615Svisa struct cmd_state cmd;
533a62b615Svisa int octbootfd = -1;
54*d807f41cSkn const char version[] = "1.4";
553a62b615Svisa
563a62b615Svisa int
main(void)573a62b615Svisa main(void)
583a62b615Svisa {
593a62b615Svisa char rootdev[PATH_MAX];
60*d807f41cSkn int fd, hasboot, isupgrade = 0;
613a62b615Svisa
623a62b615Svisa fd = open(_PATH_CONSOLE, O_RDWR);
633a62b615Svisa login_tty(fd);
643a62b615Svisa
653a62b615Svisa /* Keep stdout unbuffered to mimic ordinary bootblocks. */
663a62b615Svisa setvbuf(stdout, NULL, _IONBF, 0);
673a62b615Svisa
683a62b615Svisa printf(">> OpenBSD/" MACHINE " BOOT %s\n", version);
693a62b615Svisa
703a62b615Svisa octbootfd = open("/dev/octboot", O_WRONLY);
713a62b615Svisa if (octbootfd == -1)
723a62b615Svisa err(1, "cannot open boot control device");
733a62b615Svisa
743a62b615Svisa memset(&cmd, 0, sizeof(cmd));
753a62b615Svisa cmd.boothowto = 0;
763a62b615Svisa cmd.conf = "/etc/boot.conf";
773a62b615Svisa strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
783a62b615Svisa cmd.timeout = 5;
793a62b615Svisa
803a62b615Svisa if (ioctl(octbootfd, OBIOC_GETROOTDEV, rootdev) == -1) {
813a62b615Svisa if (errno != ENOENT)
823a62b615Svisa fprintf(stderr, "cannot get rootdev from kernel: %s\n",
833a62b615Svisa strerror(errno));
843a62b615Svisa } else {
853a62b615Svisa snprintf(cmd.bootdev, sizeof(cmd.bootdev), "%s%sa",
863a62b615Svisa rootdev, isduid(rootdev, OPENDEV_PART) ? "." : "");
873a62b615Svisa }
883a62b615Svisa
893a62b615Svisa disk_init();
903a62b615Svisa
913a62b615Svisa if (upgrade()) {
923a62b615Svisa strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image));
933a62b615Svisa printf("upgrade detected: switching to %s\n", cmd.image);
94*d807f41cSkn isupgrade = 1;
953a62b615Svisa }
963a62b615Svisa
973a62b615Svisa hasboot = read_conf();
983a62b615Svisa
993a62b615Svisa for (;;) {
1003a62b615Svisa if (hasboot <= 0) {
1013a62b615Svisa do {
1023a62b615Svisa printf("boot> ");
1033a62b615Svisa } while (!getcmd());
1043a62b615Svisa }
1053a62b615Svisa
106eb518625Svisa if (loadrandom() == 0)
107eb518625Svisa cmd.boothowto |= RB_GOODRANDOM;
108eb518625Svisa
109*d807f41cSkn kexec(isupgrade);
1103a62b615Svisa
1113a62b615Svisa hasboot = 0;
1123a62b615Svisa strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
1133a62b615Svisa printf("will try %s\n", cmd.image);
1143a62b615Svisa }
1153a62b615Svisa
1163a62b615Svisa return 0;
1173a62b615Svisa }
1183a62b615Svisa
119eb518625Svisa int
loadrandom(void)1203a62b615Svisa loadrandom(void)
1213a62b615Svisa {
1223a62b615Svisa char buf[BOOTRANDOM_MAX];
123eb518625Svisa struct stat sb;
124eb518625Svisa int fd, ret = 0;
1253a62b615Svisa
1263a62b615Svisa /* Read the file from the device specified by the kernel path. */
1273a62b615Svisa if (disk_open(cmd.path) == NULL)
128eb518625Svisa return -1;
1293a62b615Svisa fd = open(BOOTRANDOM, O_RDONLY);
1303a62b615Svisa if (fd == -1) {
1313a62b615Svisa fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM,
1323a62b615Svisa strerror(errno));
1333a62b615Svisa disk_close();
134eb518625Svisa return -1;
1353a62b615Svisa }
136eb518625Svisa if (fstat(fd, &sb) == 0) {
137eb518625Svisa if (sb.st_mode & S_ISTXT) {
138eb518625Svisa printf("NOTE: random seed is being reused.\n");
139eb518625Svisa ret = -1;
140eb518625Svisa }
141eb518625Svisa if (read(fd, buf, sizeof(buf)) != sizeof(buf))
142eb518625Svisa ret = -1;
143eb518625Svisa fchmod(fd, sb.st_mode | S_ISTXT);
144eb518625Svisa } else {
145eb518625Svisa ret = -1;
146eb518625Svisa }
1473a62b615Svisa close(fd);
1483a62b615Svisa disk_close();
1493a62b615Svisa
1503a62b615Svisa /*
1513a62b615Svisa * Push the whole buffer to the entropy pool.
1523a62b615Svisa * The kernel will use the entropy on kexec().
1533a62b615Svisa * It does not matter if some of the buffer content is uninitialized.
1543a62b615Svisa */
1553a62b615Svisa fd = open(DEVRANDOM, O_WRONLY);
1563a62b615Svisa if (fd == -1) {
1573a62b615Svisa fprintf(stderr, "%s: cannot open %s: %s", __func__,
1583a62b615Svisa DEVRANDOM, strerror(errno));
159eb518625Svisa return -1;
1603a62b615Svisa }
1613a62b615Svisa write(fd, buf, sizeof(buf));
1623a62b615Svisa close(fd);
163eb518625Svisa return ret;
1643a62b615Svisa }
1653a62b615Svisa
1663a62b615Svisa void
kexec(int isupgrade)167*d807f41cSkn kexec(int isupgrade)
1683a62b615Svisa {
1693a62b615Svisa struct octboot_kexec_args kargs;
170cf939257Svisa struct stat sb;
171a084a2f8Svisa char boothowtostr[32];
1723a62b615Svisa char rootdev[32];
173cf939257Svisa char *kimg = NULL;
1743a62b615Svisa const char *path;
175cf939257Svisa ssize_t n;
176cf939257Svisa off_t pos;
177cf939257Svisa int argc, fd = -1, ret;
1783a62b615Svisa
1793a62b615Svisa path = disk_open(cmd.path);
1803a62b615Svisa if (path == NULL)
1813a62b615Svisa return;
1823a62b615Svisa
183cf939257Svisa fd = open(path, O_RDONLY);
184cf939257Svisa if (fd == -1)
185cf939257Svisa goto load_failed;
186cf939257Svisa if (fstat(fd, &sb) == -1)
187cf939257Svisa goto load_failed;
188cf939257Svisa if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
189cf939257Svisa errno = ENOEXEC;
190cf939257Svisa goto load_failed;
191cf939257Svisa }
192cf939257Svisa
193*d807f41cSkn /* Prevent re-upgrade: chmod a-x bsd.upgrade */
194*d807f41cSkn if (isupgrade) {
195*d807f41cSkn sb.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
196*d807f41cSkn if (fchmod(fd, sb.st_mode) == -1)
197*d807f41cSkn printf("fchmod a-x %s: failed\n", path);
198*d807f41cSkn }
199*d807f41cSkn
200cf939257Svisa kimg = malloc(sb.st_size);
201cf939257Svisa if (kimg == NULL)
202cf939257Svisa goto load_failed;
203cf939257Svisa
204cf939257Svisa pos = 0;
205cf939257Svisa while (pos < sb.st_size) {
206cf939257Svisa n = read(fd, kimg + pos, sb.st_size - pos);
207cf939257Svisa if (n == -1)
208cf939257Svisa goto load_failed;
209cf939257Svisa pos += n;
210cf939257Svisa }
211cf939257Svisa
212cf939257Svisa close(fd);
213cf939257Svisa disk_close();
214cf939257Svisa
2153a62b615Svisa memset(&kargs, 0, sizeof(kargs));
216cf939257Svisa kargs.kimg = kimg;
217cf939257Svisa kargs.klen = sb.st_size;
2183a62b615Svisa argc = 0;
2193a62b615Svisa if (cmd.boothowto != 0) {
220a084a2f8Svisa snprintf(boothowtostr, sizeof(boothowtostr), "boothowto=%d",
221a084a2f8Svisa cmd.boothowto);
222a084a2f8Svisa kargs.argv[argc++] = boothowtostr;
2233a62b615Svisa }
2243a62b615Svisa if (cmd.hasduid) {
2253a62b615Svisa snprintf(rootdev, sizeof(rootdev),
2263a62b615Svisa "rootdev=%02x%02x%02x%02x%02x%02x%02x%02x",
2273a62b615Svisa cmd.bootduid[0], cmd.bootduid[1],
2283a62b615Svisa cmd.bootduid[2], cmd.bootduid[3],
2293a62b615Svisa cmd.bootduid[4], cmd.bootduid[5],
2303a62b615Svisa cmd.bootduid[6], cmd.bootduid[7]);
2313a62b615Svisa kargs.argv[argc++] = rootdev;
2323a62b615Svisa }
2333a62b615Svisa
2343a62b615Svisa printf("booting %s\n", cmd.path);
2353a62b615Svisa ret = ioctl(octbootfd, OBIOC_KEXEC, &kargs);
2363a62b615Svisa if (ret == -1)
2373a62b615Svisa fprintf(stderr, "failed to execute kernel %s: %s\n",
2383a62b615Svisa cmd.path, strerror(errno));
2393a62b615Svisa else
2403a62b615Svisa fprintf(stderr, "kexec() returned unexpectedly\n");
241cf939257Svisa free(kimg);
242cf939257Svisa return;
2433a62b615Svisa
244cf939257Svisa load_failed:
245cf939257Svisa fprintf(stderr, "failed to load kernel %s: %s\n",
246cf939257Svisa cmd.path, strerror(errno));
247cf939257Svisa if (fd != -1)
248cf939257Svisa close(fd);
2493a62b615Svisa disk_close();
250cf939257Svisa free(kimg);
2513a62b615Svisa }
252