162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Misc librarized functions for cmdline poking. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/string.h> 862306a36Sopenharmony_ci#include <linux/ctype.h> 962306a36Sopenharmony_ci#include <asm/setup.h> 1062306a36Sopenharmony_ci#include <asm/cmdline.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic inline int myisspace(u8 c) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci return c <= ' '; /* Close enough approximation */ 1562306a36Sopenharmony_ci} 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Find a boolean option (like quiet,noapic,nosmp....) 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * @cmdline: the cmdline string 2162306a36Sopenharmony_ci * @max_cmdline_size: the maximum size of cmdline 2262306a36Sopenharmony_ci * @option: option string to look for 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Returns the position of that @option (starts counting with 1) 2562306a36Sopenharmony_ci * or 0 on not found. @option will only be found if it is found 2662306a36Sopenharmony_ci * as an entire word in @cmdline. For instance, if @option="car" 2762306a36Sopenharmony_ci * then a cmdline which contains "cart" will not match. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic int 3062306a36Sopenharmony_ci__cmdline_find_option_bool(const char *cmdline, int max_cmdline_size, 3162306a36Sopenharmony_ci const char *option) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci char c; 3462306a36Sopenharmony_ci int pos = 0, wstart = 0; 3562306a36Sopenharmony_ci const char *opptr = NULL; 3662306a36Sopenharmony_ci enum { 3762306a36Sopenharmony_ci st_wordstart = 0, /* Start of word/after whitespace */ 3862306a36Sopenharmony_ci st_wordcmp, /* Comparing this word */ 3962306a36Sopenharmony_ci st_wordskip, /* Miscompare, skip */ 4062306a36Sopenharmony_ci } state = st_wordstart; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!cmdline) 4362306a36Sopenharmony_ci return -1; /* No command line */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * This 'pos' check ensures we do not overrun 4762306a36Sopenharmony_ci * a non-NULL-terminated 'cmdline' 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci while (pos < max_cmdline_size) { 5062306a36Sopenharmony_ci c = *(char *)cmdline++; 5162306a36Sopenharmony_ci pos++; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci switch (state) { 5462306a36Sopenharmony_ci case st_wordstart: 5562306a36Sopenharmony_ci if (!c) 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci else if (myisspace(c)) 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci state = st_wordcmp; 6162306a36Sopenharmony_ci opptr = option; 6262306a36Sopenharmony_ci wstart = pos; 6362306a36Sopenharmony_ci fallthrough; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci case st_wordcmp: 6662306a36Sopenharmony_ci if (!*opptr) { 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * We matched all the way to the end of the 6962306a36Sopenharmony_ci * option we were looking for. If the 7062306a36Sopenharmony_ci * command-line has a space _or_ ends, then 7162306a36Sopenharmony_ci * we matched! 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (!c || myisspace(c)) 7462306a36Sopenharmony_ci return wstart; 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * We hit the end of the option, but _not_ 7762306a36Sopenharmony_ci * the end of a word on the cmdline. Not 7862306a36Sopenharmony_ci * a match. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci } else if (!c) { 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * Hit the NULL terminator on the end of 8362306a36Sopenharmony_ci * cmdline. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci } else if (c == *opptr++) { 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * We are currently matching, so continue 8962306a36Sopenharmony_ci * to the next character on the cmdline. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci state = st_wordskip; 9462306a36Sopenharmony_ci fallthrough; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci case st_wordskip: 9762306a36Sopenharmony_ci if (!c) 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci else if (myisspace(c)) 10062306a36Sopenharmony_ci state = st_wordstart; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; /* Buffer overrun */ 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * Find a non-boolean option (i.e. option=argument). In accordance with 11062306a36Sopenharmony_ci * standard Linux practice, if this option is repeated, this returns the 11162306a36Sopenharmony_ci * last instance on the command line. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * @cmdline: the cmdline string 11462306a36Sopenharmony_ci * @max_cmdline_size: the maximum size of cmdline 11562306a36Sopenharmony_ci * @option: option string to look for 11662306a36Sopenharmony_ci * @buffer: memory buffer to return the option argument 11762306a36Sopenharmony_ci * @bufsize: size of the supplied memory buffer 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Returns the length of the argument (regardless of if it was 12062306a36Sopenharmony_ci * truncated to fit in the buffer), or -1 on not found. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic int 12362306a36Sopenharmony_ci__cmdline_find_option(const char *cmdline, int max_cmdline_size, 12462306a36Sopenharmony_ci const char *option, char *buffer, int bufsize) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci char c; 12762306a36Sopenharmony_ci int pos = 0, len = -1; 12862306a36Sopenharmony_ci const char *opptr = NULL; 12962306a36Sopenharmony_ci char *bufptr = buffer; 13062306a36Sopenharmony_ci enum { 13162306a36Sopenharmony_ci st_wordstart = 0, /* Start of word/after whitespace */ 13262306a36Sopenharmony_ci st_wordcmp, /* Comparing this word */ 13362306a36Sopenharmony_ci st_wordskip, /* Miscompare, skip */ 13462306a36Sopenharmony_ci st_bufcpy, /* Copying this to buffer */ 13562306a36Sopenharmony_ci } state = st_wordstart; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!cmdline) 13862306a36Sopenharmony_ci return -1; /* No command line */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * This 'pos' check ensures we do not overrun 14262306a36Sopenharmony_ci * a non-NULL-terminated 'cmdline' 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci while (pos++ < max_cmdline_size) { 14562306a36Sopenharmony_ci c = *(char *)cmdline++; 14662306a36Sopenharmony_ci if (!c) 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci switch (state) { 15062306a36Sopenharmony_ci case st_wordstart: 15162306a36Sopenharmony_ci if (myisspace(c)) 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci state = st_wordcmp; 15562306a36Sopenharmony_ci opptr = option; 15662306a36Sopenharmony_ci fallthrough; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci case st_wordcmp: 15962306a36Sopenharmony_ci if ((c == '=') && !*opptr) { 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * We matched all the way to the end of the 16262306a36Sopenharmony_ci * option we were looking for, prepare to 16362306a36Sopenharmony_ci * copy the argument. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci len = 0; 16662306a36Sopenharmony_ci bufptr = buffer; 16762306a36Sopenharmony_ci state = st_bufcpy; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } else if (c == *opptr++) { 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * We are currently matching, so continue 17262306a36Sopenharmony_ci * to the next character on the cmdline. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci state = st_wordskip; 17762306a36Sopenharmony_ci fallthrough; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci case st_wordskip: 18062306a36Sopenharmony_ci if (myisspace(c)) 18162306a36Sopenharmony_ci state = st_wordstart; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci case st_bufcpy: 18562306a36Sopenharmony_ci if (myisspace(c)) { 18662306a36Sopenharmony_ci state = st_wordstart; 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Increment len, but don't overrun the 19062306a36Sopenharmony_ci * supplied buffer and leave room for the 19162306a36Sopenharmony_ci * NULL terminator. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci if (++len < bufsize) 19462306a36Sopenharmony_ci *bufptr++ = c; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (bufsize) 20162306a36Sopenharmony_ci *bufptr = '\0'; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return len; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint cmdline_find_option_bool(const char *cmdline, const char *option) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciint cmdline_find_option(const char *cmdline, const char *option, char *buffer, 21262306a36Sopenharmony_ci int bufsize) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option, 21562306a36Sopenharmony_ci buffer, bufsize); 21662306a36Sopenharmony_ci} 217