// SPDX-License-Identifier: GPL-2.0-only1/*2*3* Misc librarized functions for cmdline poking.4*/5#include <linux/kernel.h>6#include <linux/string.h>7#include <linux/ctype.h>89#include <asm/setup.h>10#include <asm/cmdline.h>11#include <asm/bug.h>1213static inline int myisspace(u8 c)14{15return c <= ' '; /* Close enough approximation */16}1718/*19* Find a boolean option (like quiet,noapic,nosmp....)20*21* @cmdline: the cmdline string22* @max_cmdline_size: the maximum size of cmdline23* @option: option string to look for24*25* Returns the position of that @option (starts counting with 1)26* or 0 on not found. @option will only be found if it is found27* as an entire word in @cmdline. For instance, if @option="car"28* then a cmdline which contains "cart" will not match.29*/30static int31__cmdline_find_option_bool(const char *cmdline, int max_cmdline_size,32const char *option)33{34char c;35int pos = 0, wstart = 0;36const char *opptr = NULL;37enum {38st_wordstart = 0, /* Start of word/after whitespace */39st_wordcmp, /* Comparing this word */40st_wordskip, /* Miscompare, skip */41} state = st_wordstart;4243if (!cmdline)44return -1; /* No command line */4546/*47* This 'pos' check ensures we do not overrun48* a non-NULL-terminated 'cmdline'49*/50while (pos < max_cmdline_size) {51c = *(char *)cmdline++;52pos++;5354switch (state) {55case st_wordstart:56if (!c)57return 0;58else if (myisspace(c))59break;6061state = st_wordcmp;62opptr = option;63wstart = pos;64fallthrough;6566case st_wordcmp:67if (!*opptr) {68/*69* We matched all the way to the end of the70* option we were looking for. If the71* command-line has a space _or_ ends, then72* we matched!73*/74if (!c || myisspace(c))75return wstart;76/*77* We hit the end of the option, but _not_78* the end of a word on the cmdline. Not79* a match.80*/81} else if (!c) {82/*83* Hit the NULL terminator on the end of84* cmdline.85*/86return 0;87} else if (c == *opptr++) {88/*89* We are currently matching, so continue90* to the next character on the cmdline.91*/92break;93}94state = st_wordskip;95fallthrough;9697case st_wordskip:98if (!c)99return 0;100else if (myisspace(c))101state = st_wordstart;102break;103}104}105106return 0; /* Buffer overrun */107}108109/*110* Find a non-boolean option (i.e. option=argument). In accordance with111* standard Linux practice, if this option is repeated, this returns the112* last instance on the command line.113*114* @cmdline: the cmdline string115* @max_cmdline_size: the maximum size of cmdline116* @option: option string to look for117* @buffer: memory buffer to return the option argument118* @bufsize: size of the supplied memory buffer119*120* Returns the length of the argument (regardless of if it was121* truncated to fit in the buffer), or -1 on not found.122*/123static int124__cmdline_find_option(const char *cmdline, int max_cmdline_size,125const char *option, char *buffer, int bufsize)126{127char c;128int pos = 0, len = -1;129const char *opptr = NULL;130char *bufptr = buffer;131enum {132st_wordstart = 0, /* Start of word/after whitespace */133st_wordcmp, /* Comparing this word */134st_wordskip, /* Miscompare, skip */135st_bufcpy, /* Copying this to buffer */136} state = st_wordstart;137138if (!cmdline)139return -1; /* No command line */140141/*142* This 'pos' check ensures we do not overrun143* a non-NULL-terminated 'cmdline'144*/145while (pos++ < max_cmdline_size) {146c = *(char *)cmdline++;147if (!c)148break;149150switch (state) {151case st_wordstart:152if (myisspace(c))153break;154155state = st_wordcmp;156opptr = option;157fallthrough;158159case st_wordcmp:160if ((c == '=') && !*opptr) {161/*162* We matched all the way to the end of the163* option we were looking for, prepare to164* copy the argument.165*/166len = 0;167bufptr = buffer;168state = st_bufcpy;169break;170} else if (c == *opptr++) {171/*172* We are currently matching, so continue173* to the next character on the cmdline.174*/175break;176}177state = st_wordskip;178fallthrough;179180case st_wordskip:181if (myisspace(c))182state = st_wordstart;183break;184185case st_bufcpy:186if (myisspace(c)) {187state = st_wordstart;188} else {189/*190* Increment len, but don't overrun the191* supplied buffer and leave room for the192* NULL terminator.193*/194if (++len < bufsize)195*bufptr++ = c;196}197break;198}199}200201if (bufsize)202*bufptr = '\0';203204return len;205}206207int cmdline_find_option_bool(const char *cmdline, const char *option)208{209int ret;210211ret = __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);212if (ret > 0)213return ret;214215if (IS_ENABLED(CONFIG_CMDLINE_BOOL) && !builtin_cmdline_added)216return __cmdline_find_option_bool(builtin_cmdline, COMMAND_LINE_SIZE, option);217218return ret;219}220221int cmdline_find_option(const char *cmdline, const char *option, char *buffer,222int bufsize)223{224int ret;225226ret = __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option, buffer, bufsize);227if (ret > 0)228return ret;229230if (IS_ENABLED(CONFIG_CMDLINE_BOOL) && !builtin_cmdline_added)231return __cmdline_find_option(builtin_cmdline, COMMAND_LINE_SIZE, option, buffer, bufsize);232233return ret;234}235236237