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