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 static inline int myisspace(u8 c) 12 { 13 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 __cmdline_find_option_bool(const char *cmdline, int max_cmdline_size, 29 const char *option) 30 { 31 char c; 32 int pos = 0, wstart = 0; 33 const char *opptr = NULL; 34 enum { 35 st_wordstart = 0, /* Start of word/after whitespace */ 36 st_wordcmp, /* Comparing this word */ 37 st_wordskip, /* Miscompare, skip */ 38 } state = st_wordstart; 39 40 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 while (pos < max_cmdline_size) { 48 c = *(char *)cmdline++; 49 pos++; 50 51 switch (state) { 52 case st_wordstart: 53 if (!c) 54 return 0; 55 else if (myisspace(c)) 56 break; 57 58 state = st_wordcmp; 59 opptr = option; 60 wstart = pos; 61 fallthrough; 62 63 case st_wordcmp: 64 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 if (!c || myisspace(c)) 72 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 } else if (!c) { 79 /* 80 * Hit the NULL terminator on the end of 81 * cmdline. 82 */ 83 return 0; 84 } 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 fallthrough; 93 94 case st_wordskip: 95 if (!c) 96 return 0; 97 else if (myisspace(c)) 98 state = st_wordstart; 99 break; 100 } 101 } 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 __cmdline_find_option(const char *cmdline, int max_cmdline_size, 122 const char *option, char *buffer, int bufsize) 123 { 124 char c; 125 int pos = 0, len = -1; 126 const char *opptr = NULL; 127 char *bufptr = buffer; 128 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 } state = st_wordstart; 134 135 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 while (pos++ < max_cmdline_size) { 143 c = *(char *)cmdline++; 144 if (!c) 145 break; 146 147 switch (state) { 148 case st_wordstart: 149 if (myisspace(c)) 150 break; 151 152 state = st_wordcmp; 153 opptr = option; 154 fallthrough; 155 156 case st_wordcmp: 157 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 } 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 fallthrough; 176 177 case st_wordskip: 178 if (myisspace(c)) 179 state = st_wordstart; 180 break; 181 182 case st_bufcpy: 183 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 if (++len < bufsize) 192 *bufptr++ = c; 193 } 194 break; 195 } 196 } 197 198 if (bufsize) 199 *bufptr = '\0'; 200 201 return len; 202 } 203 204 int cmdline_find_option_bool(const char *cmdline, const char *option) 205 { 206 return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option); 207 } 208 209 int cmdline_find_option(const char *cmdline, const char *option, char *buffer, 210 int bufsize) 211 { 212 return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option, 213 buffer, bufsize); 214 } 215