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