1*3f0bb8e9Skn /* $OpenBSD: rdboot.c,v 1.4 2023/10/20 19:58:16 kn Exp $ */
2a79842c9Skettenis
3a79842c9Skettenis /*
4a79842c9Skettenis * Copyright (c) 2019-2020 Visa Hankala
5a79842c9Skettenis *
6a79842c9Skettenis * Permission to use, copy, modify, and/or distribute this software for any
7a79842c9Skettenis * purpose with or without fee is hereby granted, provided that the above
8a79842c9Skettenis * copyright notice and this permission notice appear in all copies.
9a79842c9Skettenis *
10a79842c9Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a79842c9Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a79842c9Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a79842c9Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a79842c9Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a79842c9Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a79842c9Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a79842c9Skettenis */
18a79842c9Skettenis
19a79842c9Skettenis #include <sys/types.h>
20a79842c9Skettenis #include <sys/param.h>
21a79842c9Skettenis #include <sys/ioctl.h>
22a79842c9Skettenis #include <sys/mount.h>
23a79842c9Skettenis #include <sys/reboot.h>
24a79842c9Skettenis #include <sys/select.h>
25a79842c9Skettenis #include <sys/stat.h>
26a79842c9Skettenis
27a79842c9Skettenis #include <err.h>
28a79842c9Skettenis #include <errno.h>
29a79842c9Skettenis #include <fcntl.h>
30a79842c9Skettenis #include <paths.h>
31a79842c9Skettenis #include <stdio.h>
32a79842c9Skettenis #include <stdlib.h>
33a79842c9Skettenis #include <string.h>
34a79842c9Skettenis #include <termios.h>
35a79842c9Skettenis #include <unistd.h>
36a79842c9Skettenis #include <util.h>
37a79842c9Skettenis
38a79842c9Skettenis #include <machine/kexec.h>
39a79842c9Skettenis #include <machine/param.h>
40a79842c9Skettenis
41a79842c9Skettenis #include "cmd.h"
42a79842c9Skettenis #include "disk.h"
43a79842c9Skettenis
44a79842c9Skettenis #define DEVRANDOM "/dev/random"
45a79842c9Skettenis #define BOOTRANDOM "/etc/random.seed"
46a79842c9Skettenis #define BOOTRANDOM_MAX 256 /* no point being greater than RC4STATE */
47a79842c9Skettenis #define KERNEL "/bsd"
48a79842c9Skettenis
49a79842c9Skettenis int loadrandom(void);
50*3f0bb8e9Skn void kexec(int);
51a79842c9Skettenis
52a79842c9Skettenis struct cmd_state cmd;
53a79842c9Skettenis int kexecfd = -1;
54*3f0bb8e9Skn const char version[] = "0.3";
55a79842c9Skettenis
56a79842c9Skettenis int
main(void)57a79842c9Skettenis main(void)
58a79842c9Skettenis {
5924add1c0Skettenis u_char bootduid[8];
60*3f0bb8e9Skn int fd, hasboot, isupgrade = 0;
61a79842c9Skettenis
62a79842c9Skettenis fd = open(_PATH_CONSOLE, O_RDWR);
63a79842c9Skettenis login_tty(fd);
64a79842c9Skettenis
65a79842c9Skettenis /* Keep stdout unbuffered to mimic ordinary bootblocks. */
66a79842c9Skettenis setvbuf(stdout, NULL, _IONBF, 0);
67a79842c9Skettenis
68a79842c9Skettenis printf(">> OpenBSD/" MACHINE " BOOT %s\n", version);
69a79842c9Skettenis
70a79842c9Skettenis kexecfd = open("/dev/kexec", O_WRONLY);
71a79842c9Skettenis if (kexecfd == -1)
72a79842c9Skettenis err(1, "cannot open boot control device");
73a79842c9Skettenis
74a79842c9Skettenis memset(&cmd, 0, sizeof(cmd));
75a79842c9Skettenis cmd.boothowto = 0;
76a79842c9Skettenis cmd.conf = "/etc/boot.conf";
77a79842c9Skettenis strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
78a79842c9Skettenis cmd.timeout = 5;
79a79842c9Skettenis
80a79842c9Skettenis if (ioctl(kexecfd, KIOC_GETBOOTDUID, bootduid) == -1) {
81a79842c9Skettenis fprintf(stderr, "cannot get bootduid from kernel: %s\n",
82a79842c9Skettenis strerror(errno));
83a79842c9Skettenis } else {
8424add1c0Skettenis memcpy(cmd.bootduid, bootduid, sizeof(cmd.bootduid));
85a79842c9Skettenis }
86a79842c9Skettenis
87a79842c9Skettenis disk_init();
88a79842c9Skettenis
89a79842c9Skettenis if (upgrade()) {
90a79842c9Skettenis strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image));
91a79842c9Skettenis printf("upgrade detected: switching to %s\n", cmd.image);
92*3f0bb8e9Skn isupgrade = 1;
93a79842c9Skettenis }
94a79842c9Skettenis
95a79842c9Skettenis hasboot = read_conf();
96a79842c9Skettenis
97a79842c9Skettenis for (;;) {
98a79842c9Skettenis if (hasboot <= 0) {
99a79842c9Skettenis do {
100a79842c9Skettenis printf("boot> ");
101a79842c9Skettenis } while (!getcmd());
102a79842c9Skettenis }
103a79842c9Skettenis
104a79842c9Skettenis if (loadrandom() == 0)
105a79842c9Skettenis cmd.boothowto |= RB_GOODRANDOM;
106a79842c9Skettenis
107*3f0bb8e9Skn kexec(isupgrade);
108a79842c9Skettenis
109a79842c9Skettenis hasboot = 0;
110a79842c9Skettenis strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
111a79842c9Skettenis printf("will try %s\n", cmd.image);
112a79842c9Skettenis }
113a79842c9Skettenis
114a79842c9Skettenis return 0;
115a79842c9Skettenis }
116a79842c9Skettenis
117a79842c9Skettenis int
loadrandom(void)118a79842c9Skettenis loadrandom(void)
119a79842c9Skettenis {
120a79842c9Skettenis char buf[BOOTRANDOM_MAX];
121a79842c9Skettenis struct stat sb;
122a79842c9Skettenis int fd, ret = 0;
123a79842c9Skettenis
124a79842c9Skettenis /* Read the file from the device specified by the kernel path. */
125a79842c9Skettenis if (disk_open(cmd.path) == NULL)
126a79842c9Skettenis return -1;
127a79842c9Skettenis fd = open(BOOTRANDOM, O_RDONLY);
128a79842c9Skettenis if (fd == -1) {
129a79842c9Skettenis fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM,
130a79842c9Skettenis strerror(errno));
131a79842c9Skettenis disk_close();
132a79842c9Skettenis return -1;
133a79842c9Skettenis }
134a79842c9Skettenis if (fstat(fd, &sb) == 0) {
135a79842c9Skettenis if (sb.st_mode & S_ISTXT) {
136a79842c9Skettenis printf("NOTE: random seed is being reused.\n");
137a79842c9Skettenis ret = -1;
138a79842c9Skettenis }
139a79842c9Skettenis if (read(fd, buf, sizeof(buf)) != sizeof(buf))
140a79842c9Skettenis ret = -1;
141a79842c9Skettenis fchmod(fd, sb.st_mode | S_ISTXT);
142a79842c9Skettenis } else {
143a79842c9Skettenis ret = -1;
144a79842c9Skettenis }
145a79842c9Skettenis close(fd);
146a79842c9Skettenis disk_close();
147a79842c9Skettenis
148a79842c9Skettenis /*
149a79842c9Skettenis * Push the whole buffer to the entropy pool.
150a79842c9Skettenis * The kernel will use the entropy on kexec().
151a79842c9Skettenis * It does not matter if some of the buffer content is uninitialized.
152a79842c9Skettenis */
153a79842c9Skettenis fd = open(DEVRANDOM, O_WRONLY);
154a79842c9Skettenis if (fd == -1) {
155a79842c9Skettenis fprintf(stderr, "%s: cannot open %s: %s", __func__,
156a79842c9Skettenis DEVRANDOM, strerror(errno));
157a79842c9Skettenis return -1;
158a79842c9Skettenis }
159a79842c9Skettenis write(fd, buf, sizeof(buf));
160a79842c9Skettenis close(fd);
161a79842c9Skettenis return ret;
162a79842c9Skettenis }
163a79842c9Skettenis
164a79842c9Skettenis void
kexec(int isupgrade)165*3f0bb8e9Skn kexec(int isupgrade)
166a79842c9Skettenis {
167a79842c9Skettenis struct kexec_args kargs;
168a79842c9Skettenis struct stat sb;
169a79842c9Skettenis char *kimg = NULL;
170a79842c9Skettenis const char *path;
171a79842c9Skettenis ssize_t n;
172a79842c9Skettenis off_t pos;
17324add1c0Skettenis int fd = -1, ret;
174a79842c9Skettenis
175a79842c9Skettenis path = disk_open(cmd.path);
176a79842c9Skettenis if (path == NULL)
177a79842c9Skettenis return;
178a79842c9Skettenis
179a79842c9Skettenis fd = open(path, O_RDONLY);
180a79842c9Skettenis if (fd == -1)
181a79842c9Skettenis goto load_failed;
182a79842c9Skettenis if (fstat(fd, &sb) == -1)
183a79842c9Skettenis goto load_failed;
184a79842c9Skettenis if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
185a79842c9Skettenis errno = ENOEXEC;
186a79842c9Skettenis goto load_failed;
187a79842c9Skettenis }
188a79842c9Skettenis
189*3f0bb8e9Skn /* Prevent re-upgrade: chmod a-x bsd.upgrade */
190*3f0bb8e9Skn if (isupgrade) {
191*3f0bb8e9Skn sb.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
192*3f0bb8e9Skn if (fchmod(fd, sb.st_mode) == -1)
193*3f0bb8e9Skn printf("fchmod a-x %s: failed\n", path);
194*3f0bb8e9Skn }
195*3f0bb8e9Skn
196a79842c9Skettenis kimg = malloc(sb.st_size);
197a79842c9Skettenis if (kimg == NULL)
198a79842c9Skettenis goto load_failed;
199a79842c9Skettenis
200a79842c9Skettenis pos = 0;
201a79842c9Skettenis while (pos < sb.st_size) {
202a79842c9Skettenis n = read(fd, kimg + pos, sb.st_size - pos);
203a79842c9Skettenis if (n == -1)
204a79842c9Skettenis goto load_failed;
205a79842c9Skettenis pos += n;
206a79842c9Skettenis }
207a79842c9Skettenis
208a79842c9Skettenis close(fd);
209a79842c9Skettenis disk_close();
210a79842c9Skettenis
211a79842c9Skettenis memset(&kargs, 0, sizeof(kargs));
212a79842c9Skettenis kargs.kimg = kimg;
213a79842c9Skettenis kargs.klen = sb.st_size;
21424add1c0Skettenis kargs.boothowto = cmd.boothowto;
21524add1c0Skettenis memcpy(kargs.bootduid, cmd.bootduid, sizeof(kargs.bootduid));
216a79842c9Skettenis
217a79842c9Skettenis printf("booting %s\n", cmd.path);
218a79842c9Skettenis ret = ioctl(kexecfd, KIOC_KEXEC, &kargs);
219a79842c9Skettenis if (ret == -1)
220a79842c9Skettenis fprintf(stderr, "failed to execute kernel %s: %s\n",
221a79842c9Skettenis cmd.path, strerror(errno));
222a79842c9Skettenis else
223a79842c9Skettenis fprintf(stderr, "kexec() returned unexpectedly\n");
224a79842c9Skettenis free(kimg);
225a79842c9Skettenis return;
226a79842c9Skettenis
227a79842c9Skettenis load_failed:
228a79842c9Skettenis fprintf(stderr, "failed to load kernel %s: %s\n",
229a79842c9Skettenis cmd.path, strerror(errno));
230a79842c9Skettenis if (fd != -1)
231a79842c9Skettenis close(fd);
232a79842c9Skettenis disk_close();
233a79842c9Skettenis free(kimg);
234a79842c9Skettenis }
235