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