GNU Linux-libre 6.8.9-gnu
[releases.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 #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 }