Merge tag 'pm-6.12-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-block.git] / arch / x86 / lib / cmdline.c
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 }