xref: /linux/arch/x86/lib/cmdline.c (revision 34e3c4500cdc06094b37a41b622598098308ba8f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  * Misc librarized functions for cmdline poking.
5  */
6 #include <linux/kernel.h>
7 #include <linux/string.h>
8 #include <linux/ctype.h>
9 
10 #include <asm/setup.h>
11 #include <asm/cmdline.h>
12 #include <asm/bug.h>
13 
14 static inline int myisspace(u8 c)
15 {
16 	return c <= ' ';	/* Close enough approximation */
17 }
18 
19 /*
20  * Find a boolean option (like quiet,noapic,nosmp....)
21  *
22  * @cmdline: the cmdline string
23  * @max_cmdline_size: the maximum size of cmdline
24  * @option: option string to look for
25  *
26  * Returns the position of that @option (starts counting with 1)
27  * or 0 on not found.  @option will only be found if it is found
28  * as an entire word in @cmdline.  For instance, if @option="car"
29  * then a cmdline which contains "cart" will not match.
30  */
31 static int
32 __cmdline_find_option_bool(const char *cmdline, int max_cmdline_size,
33 			   const char *option)
34 {
35 	char c;
36 	int pos = 0, wstart = 0;
37 	const char *opptr = NULL;
38 	enum {
39 		st_wordstart = 0,	/* Start of word/after whitespace */
40 		st_wordcmp,	/* Comparing this word */
41 		st_wordskip,	/* Miscompare, skip */
42 	} state = st_wordstart;
43 
44 	if (!cmdline)
45 		return -1;      /* No command line */
46 
47 	/*
48 	 * This 'pos' check ensures we do not overrun
49 	 * a non-NULL-terminated 'cmdline'
50 	 */
51 	while (pos < max_cmdline_size) {
52 		c = *(char *)cmdline++;
53 		pos++;
54 
55 		switch (state) {
56 		case st_wordstart:
57 			if (!c)
58 				return 0;
59 			else if (myisspace(c))
60 				break;
61 
62 			state = st_wordcmp;
63 			opptr = option;
64 			wstart = pos;
65 			fallthrough;
66 
67 		case st_wordcmp:
68 			if (!*opptr) {
69 				/*
70 				 * We matched all the way to the end of the
71 				 * option we were looking for.  If the
72 				 * command-line has a space _or_ ends, then
73 				 * we matched!
74 				 */
75 				if (!c || myisspace(c))
76 					return wstart;
77 				/*
78 				 * We hit the end of the option, but _not_
79 				 * the end of a word on the cmdline.  Not
80 				 * a match.
81 				 */
82 			} else if (!c) {
83 				/*
84 				 * Hit the NULL terminator on the end of
85 				 * cmdline.
86 				 */
87 				return 0;
88 			} else if (c == *opptr++) {
89 				/*
90 				 * We are currently matching, so continue
91 				 * to the next character on the cmdline.
92 				 */
93 				break;
94 			}
95 			state = st_wordskip;
96 			fallthrough;
97 
98 		case st_wordskip:
99 			if (!c)
100 				return 0;
101 			else if (myisspace(c))
102 				state = st_wordstart;
103 			break;
104 		}
105 	}
106 
107 	return 0;	/* Buffer overrun */
108 }
109 
110 /*
111  * Find a non-boolean option (i.e. option=argument). In accordance with
112  * standard Linux practice, if this option is repeated, this returns the
113  * last instance on the command line.
114  *
115  * @cmdline: the cmdline string
116  * @max_cmdline_size: the maximum size of cmdline
117  * @option: option string to look for
118  * @buffer: memory buffer to return the option argument
119  * @bufsize: size of the supplied memory buffer
120  *
121  * Returns the length of the argument (regardless of if it was
122  * truncated to fit in the buffer), or -1 on not found.
123  */
124 static int
125 __cmdline_find_option(const char *cmdline, int max_cmdline_size,
126 		      const char *option, char *buffer, int bufsize)
127 {
128 	char c;
129 	int pos = 0, len = -1;
130 	const char *opptr = NULL;
131 	char *bufptr = buffer;
132 	enum {
133 		st_wordstart = 0,	/* Start of word/after whitespace */
134 		st_wordcmp,	/* Comparing this word */
135 		st_wordskip,	/* Miscompare, skip */
136 		st_bufcpy,	/* Copying this to buffer */
137 	} state = st_wordstart;
138 
139 	if (!cmdline)
140 		return -1;      /* No command line */
141 
142 	/*
143 	 * This 'pos' check ensures we do not overrun
144 	 * a non-NULL-terminated 'cmdline'
145 	 */
146 	while (pos++ < max_cmdline_size) {
147 		c = *(char *)cmdline++;
148 		if (!c)
149 			break;
150 
151 		switch (state) {
152 		case st_wordstart:
153 			if (myisspace(c))
154 				break;
155 
156 			state = st_wordcmp;
157 			opptr = option;
158 			fallthrough;
159 
160 		case st_wordcmp:
161 			if ((c == '=') && !*opptr) {
162 				/*
163 				 * We matched all the way to the end of the
164 				 * option we were looking for, prepare to
165 				 * copy the argument.
166 				 */
167 				len = 0;
168 				bufptr = buffer;
169 				state = st_bufcpy;
170 				break;
171 			} else if (c == *opptr++) {
172 				/*
173 				 * We are currently matching, so continue
174 				 * to the next character on the cmdline.
175 				 */
176 				break;
177 			}
178 			state = st_wordskip;
179 			fallthrough;
180 
181 		case st_wordskip:
182 			if (myisspace(c))
183 				state = st_wordstart;
184 			break;
185 
186 		case st_bufcpy:
187 			if (myisspace(c)) {
188 				state = st_wordstart;
189 			} else {
190 				/*
191 				 * Increment len, but don't overrun the
192 				 * supplied buffer and leave room for the
193 				 * NULL terminator.
194 				 */
195 				if (++len < bufsize)
196 					*bufptr++ = c;
197 			}
198 			break;
199 		}
200 	}
201 
202 	if (bufsize)
203 		*bufptr = '\0';
204 
205 	return len;
206 }
207 
208 int cmdline_find_option_bool(const char *cmdline, const char *option)
209 {
210 	int ret;
211 
212 	ret = __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);
213 	if (ret > 0)
214 		return ret;
215 
216 	if (IS_ENABLED(CONFIG_CMDLINE_BOOL) && !builtin_cmdline_added)
217 		return __cmdline_find_option_bool(builtin_cmdline, COMMAND_LINE_SIZE, option);
218 
219 	return ret;
220 }
221 
222 int cmdline_find_option(const char *cmdline, const char *option, char *buffer,
223 			int bufsize)
224 {
225 	int ret;
226 
227 	ret = __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option, buffer, bufsize);
228 	if (ret > 0)
229 		return ret;
230 
231 	if (IS_ENABLED(CONFIG_CMDLINE_BOOL) && !builtin_cmdline_added)
232 		return __cmdline_find_option(builtin_cmdline, COMMAND_LINE_SIZE, option, buffer, bufsize);
233 
234 	return ret;
235 }
236