xref: /freebsd/sys/kern/subr_boot.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5  * All Rights Reserved.
6  * Copyright (c) 1998 Robert Nordier
7  * All Rights Reserved.
8  * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
9  * All rights reserved.
10  * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com>
11  * All Rights Reserved.
12  * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
13  * Copyright (c) 2018 Netflix, Inc.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer
20  *    in this position and unchanged.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 /* Note: This is compiled in both the kernel and boot loader contexts */
42 
43 #include <sys/param.h>
44 #ifdef _KERNEL
45 #include <sys/systm.h>
46 #else
47 #include <stand.h>
48 #endif
49 #include <sys/reboot.h>
50 #include <sys/boot.h>
51 #include <sys/tslog.h>
52 
53 #ifdef _KERNEL
54 #define SETENV(k, v)	kern_setenv(k, v)
55 #define GETENV(k)	kern_getenv(k)
56 #define FREE(v)		freeenv(v)
57 #else	/* Boot loader */
58 #define SETENV(k, v)	setenv(k, v, 1)
59 #define GETENV(k)	getenv(k)
60 #define	FREE(v)
61 #endif
62 
63 static struct
64 {
65 	const char	*ev;
66 	int		mask;
67 } howto_names[] = {
68 	{ "boot_askname",	RB_ASKNAME},
69 	{ "boot_cdrom",		RB_CDROM},
70 	{ "boot_ddb",		RB_KDB},
71 	{ "boot_dfltroot",	RB_DFLTROOT},
72 	{ "boot_gdb",		RB_GDB},
73 	{ "boot_multicons",	RB_MULTIPLE},
74 	{ "boot_mute",		RB_MUTE},
75 	{ "boot_pause",		RB_PAUSE},
76 	{ "boot_serial",	RB_SERIAL},
77 	{ "boot_single",	RB_SINGLE},
78 	{ "boot_verbose",	RB_VERBOSE},
79 	{ NULL,	0}
80 };
81 
82 /*
83  * In the boot environment, we often parse a command line and have to throw away
84  * its contents. As we do so, we set environment variables that correspond to
85  * the flags we encounter. Later, to get a howto mask, we grovel through these
86  * to reconstruct it. This also allows users in their loader.conf to set them
87  * and have the kernel see them.
88  */
89 
90 /**
91  * @brief convert the env vars in howto_names into a howto mask
92  */
93 int
94 boot_env_to_howto(void)
95 {
96 	int i, howto;
97 	char *val;
98 
99 	TSENTER();
100 	for (howto = 0, i = 0; howto_names[i].ev != NULL; i++) {
101 		val = GETENV(howto_names[i].ev);
102 		if (val != NULL && strcasecmp(val, "no") != 0)
103 			howto |= howto_names[i].mask;
104 		FREE(val);
105 	}
106 	TSEXIT();
107 	return (howto);
108 }
109 
110 /**
111  * @brief Set env vars from howto_names based on howto passed in
112  */
113 void
114 boot_howto_to_env(int howto)
115 {
116 	int i;
117 
118 	for (i = 0; howto_names[i].ev != NULL; i++)
119 		if (howto & howto_names[i].mask)
120 			SETENV(howto_names[i].ev, "YES");
121 }
122 
123 /**
124  * @brief Helper routine to parse a single arg and return its mask
125  *
126  * Parse all the - options to create a mask (or a serial speed in the
127  * case of -S). If the arg doesn't start with '-' assume it's an env
128  * variable and set that instead.
129  */
130 int
131 boot_parse_arg(const char *v)
132 {
133 	char *n;
134 	int howto;
135 
136 #if 0
137 /* Need to see if this is better or worse than the meat of the #else */
138 static const char howto_switches[] = "aCdrgDmphsv";
139 static int howto_masks[] = {
140 	RB_ASKNAME, RB_CDROM, RB_KDB, RB_DFLTROOT, RB_GDB, RB_MULTIPLE,
141 	RB_MUTE, RB_PAUSE, RB_SERIAL, RB_SINGLE, RB_VERBOSE
142 };
143 
144 	opts = strchr(kargs, '-');
145 	while (opts != NULL) {
146 		while (*(++opts) != '\0') {
147 			sw = strchr(howto_switches, *opts);
148 			if (sw == NULL)
149 				break;
150 			howto |= howto_masks[sw - howto_switches];
151 		}
152 		opts = strchr(opts, '-');
153 	}
154 #else
155 	howto = 0;
156 	if (*v == '-') {
157 		while (*v != '\0') {
158 			v++;
159 			switch (*v) {
160 			case 'a': howto |= RB_ASKNAME; break;
161 			case 'C': howto |= RB_CDROM; break;
162 			case 'd': howto |= RB_KDB; break;
163 			case 'D': howto |= RB_MULTIPLE; break;
164 			case 'm': howto |= RB_MUTE; break;
165 			case 'g': howto |= RB_GDB; break;
166 			case 'h': howto |= RB_SERIAL; break;
167 			case 'p': howto |= RB_PAUSE; break;
168 			case 'P': howto |= RB_PROBE; break;
169 			case 'r': howto |= RB_DFLTROOT; break;
170 			case 's': howto |= RB_SINGLE; break;
171 			case 'S': SETENV("comconsole_speed", v + 1); v += strlen(v); break;
172 			case 'v': howto |= RB_VERBOSE; break;
173 			}
174 		}
175 	} else {
176 		char buf[128];
177 		char *vv = buf;
178 
179 		strlcpy(buf, v, sizeof(buf));
180 		n = strsep(&vv, "=");
181 		if (vv == NULL)
182 			SETENV(n, "1");
183 		else
184 			SETENV(n, vv);
185 	}
186 #endif
187 	return (howto);
188 }
189 
190 /**
191  * @brief breakup the command line into args, and pass to boot_parse_arg
192  */
193 int
194 boot_parse_cmdline_delim(char *cmdline, const char *delim)
195 {
196 	char *v;
197 	int howto;
198 
199 	TSENTER();
200 	howto = 0;
201 	while ((v = strsep(&cmdline, delim)) != NULL) {
202 		if (*v == '\0')
203 			continue;
204 		howto |= boot_parse_arg(v);
205 	}
206 	TSEXIT();
207 	return (howto);
208 }
209 
210 /**
211  * @brief Simplified interface for common 'space or tab separated' args
212  */
213 int
214 boot_parse_cmdline(char *cmdline)
215 {
216 
217 	return (boot_parse_cmdline_delim(cmdline, " \t\n"));
218 }
219 
220 /**
221  * @brief Pass a vector of strings to boot_parse_arg
222  */
223 int
224 boot_parse_args(int argc, char *argv[])
225 {
226         int i, howto;
227 
228 	howto = 0;
229         for (i = 1; i < argc; i++)
230                 howto |= boot_parse_arg(argv[i]);
231 	return (howto);
232 }
233