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