diff mbox series

[1/8] CMDLINE: add generic builtin command line

Message ID 20231110013817.2378507-2-danielwa@cisco.com (mailing list archive)
State Handled Elsewhere
Headers show
Series generic command line v6 | expand

Commit Message

Daniel Walker (danielwa) Nov. 10, 2023, 1:38 a.m. UTC
This code allows architectures to use a generic builtin command line.
The state of the builtin command line options across architecture is
diverse. MIPS and X86 once has similar systems, then mips added some
options to allow extending the command line. Powerpc did something
simiar in adding the ability to extend. Even with mips and powerpc
enhancement the needs of Cisco are not met on these platforms.

The code in this commit unifies the code into a generic
header file under the CONFIG_GENERIC_CMDLINE option. When this
option is enabled the architecture can call the cmdline_add_builtin()
to add the builtin command line. The generic code provides both
append and/or prepend options and provides a way to redefine these
option after the kernel is compiled.

This code also includes test's which are meant to confirm
functionality.

This unified implementation offers the same functionality needed by
Cisco on all platform which we enable it on.

Cc: xe-linux-external@cisco.com
Signed-off-by: Ruslan Bilovol <rbilovol@cisco.com>
Signed-off-by: Daniel Walker <danielwa@cisco.com>
---
 include/linux/cmdline.h | 106 ++++++++++++++++++++++++++++++
 init/Kconfig            |  79 +++++++++++++++++++++++
 lib/Kconfig             |   4 ++
 lib/Makefile            |   3 +
 lib/generic_cmdline.S   |  53 +++++++++++++++
 lib/test_cmdline1.c     | 139 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 384 insertions(+)
 create mode 100644 include/linux/cmdline.h
 create mode 100644 lib/generic_cmdline.S
 create mode 100644 lib/test_cmdline1.c

Comments

kernel test robot Nov. 10, 2023, 4:12 p.m. UTC | #1
Hi Daniel,

kernel test robot noticed the following build errors:

[auto build test ERROR on v6.6]
[cannot apply to arm64/for-next/core efi/next tip/x86/core robh/for-next linus/master next-20231110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Daniel-Walker/CMDLINE-add-generic-builtin-command-line/20231110-094423
base:   v6.6
patch link:    https://lore.kernel.org/r/20231110013817.2378507-2-danielwa%40cisco.com
patch subject: [PATCH 1/8] CMDLINE: add generic builtin command line
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20231110/202311102331.GllFaI9t-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231110/202311102331.GllFaI9t-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311102331.GllFaI9t-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/kernel.h:15,
                    from include/linux/interrupt.h:6,
                    from arch/sparc/include/asm/setup.h:8,
                    from lib/generic_cmdline.S:5:
>> include/linux/align.h:8: warning: "ALIGN" redefined
       8 | #define ALIGN(x, a)             __ALIGN_KERNEL((x), (a))
         | 
   In file included from include/linux/export.h:6,
                    from lib/generic_cmdline.S:2:
   include/linux/linkage.h:103: note: this is the location of the previous definition
     103 | #define ALIGN __ALIGN
         | 
   In file included from include/linux/kcsan-checks.h:13,
                    from include/linux/instrumented.h:12,
                    from include/linux/atomic/atomic-instrumented.h:17,
                    from include/linux/atomic.h:82,
                    from include/asm-generic/bitops/lock.h:5,
                    from arch/sparc/include/asm/bitops_64.h:52,
                    from arch/sparc/include/asm/bitops.h:5,
                    from include/linux/bitops.h:68,
                    from include/linux/kernel.h:22:
>> include/linux/compiler_attributes.h:55: warning: "__always_inline" redefined
      55 | #define __always_inline                 inline __attribute__((__always_inline__))
         | 
   In file included from include/linux/stddef.h:5,
                    from include/linux/kernel.h:18:
   include/uapi/linux/stddef.h:8: note: this is the location of the previous definition
       8 | #define __always_inline inline
         | 
>> include/linux/compiler_attributes.h:91:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
      91 | #if __has_attribute(__copy__)
         |     ^~~~~~~~~~~~~~~
>> include/linux/compiler_attributes.h:91:20: error: missing binary operator before token "("
      91 | #if __has_attribute(__copy__)
         |                    ^
   include/linux/compiler_attributes.h:104:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     104 | #if __has_attribute(__counted_by__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:104:20: error: missing binary operator before token "("
     104 | #if __has_attribute(__counted_by__)
         |                    ^
>> include/linux/compiler_attributes.h:107: warning: "__counted_by" redefined
     107 | # define __counted_by(member)
         | 
   include/uapi/linux/stddef.h:55: note: this is the location of the previous definition
      55 | #define __counted_by(m)
         | 
   include/linux/compiler_attributes.h:116:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     116 | #if __has_attribute(__diagnose_as_builtin__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:116:20: error: missing binary operator before token "("
     116 | #if __has_attribute(__diagnose_as_builtin__)
         |                    ^
   include/linux/compiler_attributes.h:139:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     139 | #if __has_attribute(__designated_init__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:139:20: error: missing binary operator before token "("
     139 | #if __has_attribute(__designated_init__)
         |                    ^
   include/linux/compiler_attributes.h:150:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     150 | #if __has_attribute(__error__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:150:20: error: missing binary operator before token "("
     150 | #if __has_attribute(__error__)
         |                    ^
   include/linux/compiler_attributes.h:161:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     161 | #if __has_attribute(__externally_visible__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:161:20: error: missing binary operator before token "("
     161 | #if __has_attribute(__externally_visible__)
         |                    ^
   include/linux/compiler_attributes.h:198:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     198 | #if __has_attribute(__no_caller_saved_registers__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:198:20: error: missing binary operator before token "("
     198 | #if __has_attribute(__no_caller_saved_registers__)
         |                    ^
   include/linux/compiler_attributes.h:209:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     209 | #if __has_attribute(__noclone__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:209:20: error: missing binary operator before token "("
     209 | #if __has_attribute(__noclone__)
         |                    ^
   include/linux/compiler_attributes.h:226:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     226 | #if __has_attribute(__fallthrough__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:226:20: error: missing binary operator before token "("
     226 | #if __has_attribute(__fallthrough__)
         |                    ^
   include/linux/compiler_attributes.h:252:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     252 | #if __has_attribute(__nonstring__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:252:20: error: missing binary operator before token "("
     252 | #if __has_attribute(__nonstring__)
         |                    ^
   include/linux/compiler_attributes.h:264:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     264 | #if __has_attribute(__no_profile_instrument_function__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:264:20: error: missing binary operator before token "("
     264 | #if __has_attribute(__no_profile_instrument_function__)
         |                    ^
   include/linux/compiler_attributes.h:283:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     283 | #if __has_attribute(__no_stack_protector__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:283:20: error: missing binary operator before token "("
     283 | #if __has_attribute(__no_stack_protector__)
         |                    ^
   include/linux/compiler_attributes.h:294:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     294 | #if __has_attribute(__overloadable__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:294:20: error: missing binary operator before token "("
     294 | #if __has_attribute(__overloadable__)
         |                    ^
   include/linux/compiler_attributes.h:313:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     313 | #if __has_attribute(__pass_dynamic_object_size__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:313:20: error: missing binary operator before token "("
     313 | #if __has_attribute(__pass_dynamic_object_size__)
         |                    ^
   include/linux/compiler_attributes.h:318:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     318 | #if __has_attribute(__pass_object_size__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:318:20: error: missing binary operator before token "("
     318 | #if __has_attribute(__pass_object_size__)
         |                    ^
   include/linux/compiler_attributes.h:363:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     363 | #if __has_attribute(__warning__)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:363:20: error: missing binary operator before token "("
     363 | #if __has_attribute(__warning__)
         |                    ^
   include/linux/compiler_attributes.h:380:5: warning: "__has_attribute" is not defined, evaluates to 0 [-Wundef]
     380 | #if __has_attribute(disable_sanitizer_instrumentation)
         |     ^~~~~~~~~~~~~~~
   include/linux/compiler_attributes.h:380:20: error: missing binary operator before token "("
     380 | #if __has_attribute(disable_sanitizer_instrumentation)
         |                    ^


vim +91 include/linux/compiler_attributes.h

86cffecdeaa278 Kees Cook      2021-11-05   45  
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   46  /*
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   47   * Note: users of __always_inline currently do not write "inline" themselves,
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   48   * which seems to be required by gcc to apply the attribute according
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   49   * to its docs (and also "warning: always_inline function might not be
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   50   * inlinable [-Wattributes]" is emitted).
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   51   *
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   52   *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-always_005finline-function-attribute
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   53   * clang: mentioned
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   54   */
a3f8a30f3f0079 Miguel Ojeda   2018-08-30  @55  #define __always_inline                 inline __attribute__((__always_inline__))
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   56  
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   57  /*
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   58   * The second argument is optional (default 0), so we use a variadic macro
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   59   * to make the shorthand.
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   60   *
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   61   * Beware: Do not apply this to functions which may return
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   62   * ERR_PTRs. Also, it is probably unwise to apply it to functions
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   63   * returning extra information in the low bits (but in that case the
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   64   * compiler should see some alignment anyway, when the return value is
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   65   * massaged by 'flags = ptr & 3; ptr &= ~3;').
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   66   *
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   67   *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-assume_005faligned-function-attribute
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   68   * clang: https://clang.llvm.org/docs/AttributeReference.html#assume-aligned
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   69   */
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   70  #define __assume_aligned(a, ...)        __attribute__((__assume_aligned__(a, ## __VA_ARGS__)))
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   71  
54da6a0924311c Peter Zijlstra 2023-05-26   72  /*
54da6a0924311c Peter Zijlstra 2023-05-26   73   *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute
54da6a0924311c Peter Zijlstra 2023-05-26   74   * clang: https://clang.llvm.org/docs/AttributeReference.html#cleanup
54da6a0924311c Peter Zijlstra 2023-05-26   75   */
54da6a0924311c Peter Zijlstra 2023-05-26   76  #define __cleanup(func)			__attribute__((__cleanup__(func)))
54da6a0924311c Peter Zijlstra 2023-05-26   77  
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   78  /*
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   79   * Note the long name.
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   80   *
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   81   *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   82   */
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   83  #define __attribute_const__             __attribute__((__const__))
a3f8a30f3f0079 Miguel Ojeda   2018-08-30   84  
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   85  /*
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   86   * Optional: only supported since gcc >= 9
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   87   * Optional: not supported by clang
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   88   *
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   89   *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-copy-function-attribute
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   90   */
c0d9782f5b6d71 Miguel Ojeda   2019-02-08  @91  #if __has_attribute(__copy__)
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   92  # define __copy(symbol)                 __attribute__((__copy__(symbol)))
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   93  #else
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   94  # define __copy(symbol)
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   95  #endif
c0d9782f5b6d71 Miguel Ojeda   2019-02-08   96  
c8248faf3ca276 Kees Cook      2023-08-17   97  /*
c8248faf3ca276 Kees Cook      2023-08-17   98   * Optional: only supported since gcc >= 14
c8248faf3ca276 Kees Cook      2023-08-17   99   * Optional: only supported since clang >= 18
c8248faf3ca276 Kees Cook      2023-08-17  100   *
c8248faf3ca276 Kees Cook      2023-08-17  101   *   gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
c8248faf3ca276 Kees Cook      2023-08-17  102   * clang: https://reviews.llvm.org/D148381
c8248faf3ca276 Kees Cook      2023-08-17  103   */
c8248faf3ca276 Kees Cook      2023-08-17  104  #if __has_attribute(__counted_by__)
c8248faf3ca276 Kees Cook      2023-08-17  105  # define __counted_by(member)		__attribute__((__counted_by__(member)))
c8248faf3ca276 Kees Cook      2023-08-17  106  #else
c8248faf3ca276 Kees Cook      2023-08-17 @107  # define __counted_by(member)
c8248faf3ca276 Kees Cook      2023-08-17  108  #endif
c8248faf3ca276 Kees Cook      2023-08-17  109
Christophe Leroy Nov. 23, 2023, 6:32 a.m. UTC | #2
Le 10/11/2023 à 02:38, Daniel Walker a écrit :
> This code allows architectures to use a generic builtin command line.
> The state of the builtin command line options across architecture is
> diverse. MIPS and X86 once has similar systems, then mips added some
> options to allow extending the command line. Powerpc did something
> simiar in adding the ability to extend. Even with mips and powerpc
> enhancement the needs of Cisco are not met on these platforms.

What are those needs, can you list them in the cover letter, and 
probably also in here ?
Are those needs specific to Cisco or can they interest the entire Linux 
community ?

> 
> The code in this commit unifies the code into a generic
> header file under the CONFIG_GENERIC_CMDLINE option. When this
> option is enabled the architecture can call the cmdline_add_builtin()
> to add the builtin command line. The generic code provides both
> append and/or prepend options and provides a way to redefine these
> option after the kernel is compiled.

Explain how.

> 
> This code also includes test's which are meant to confirm
> functionality.

Would be better to have test part as a separate patch if possible.

> 
> This unified implementation offers the same functionality needed by
> Cisco on all platform which we enable it on.

Cisco ... cisco ... cisco ...

> 
> Cc: xe-linux-external@cisco.com
> Signed-off-by: Ruslan Bilovol <rbilovol@cisco.com>
> Signed-off-by: Daniel Walker <danielwa@cisco.com>
> ---
>   include/linux/cmdline.h | 106 ++++++++++++++++++++++++++++++
>   init/Kconfig            |  79 +++++++++++++++++++++++
>   lib/Kconfig             |   4 ++
>   lib/Makefile            |   3 +
>   lib/generic_cmdline.S   |  53 +++++++++++++++
>   lib/test_cmdline1.c     | 139 ++++++++++++++++++++++++++++++++++++++++
>   6 files changed, 384 insertions(+)
>   create mode 100644 include/linux/cmdline.h
>   create mode 100644 lib/generic_cmdline.S
>   create mode 100644 lib/test_cmdline1.c
> 
> diff --git a/include/linux/cmdline.h b/include/linux/cmdline.h
> new file mode 100644
> index 000000000000..a94758a0f257
> --- /dev/null
> +++ b/include/linux/cmdline.h
> @@ -0,0 +1,106 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_CMDLINE_H
> +#define _LINUX_CMDLINE_H
> +/*
> + *
> + * Copyright (C) 2006,2021. Cisco Systems, Inc.
> + *
> + * Generic Append/Prepend cmdline support.
> + */
> +
> +
> +#include <linux/ctype.h>
> +#include <linux/cache.h>
> +#include <asm/setup.h>
> +
> +#ifdef CONFIG_CMDLINE_BOOL
> +extern char cmdline_prepend[];
> +extern char cmdline_append[];
> +extern char cmdline_tmp[];
> +#define CMDLINE_PREPEND cmdline_prepend
> +#define CMDLINE_APPEND cmdline_append
> +#define CMDLINE_TMP cmdline_tmp
> +#define CMDLINE_STATIC_PREPEND CONFIG_CMDLINE_PREPEND
> +#define CMDLINE_STATIC_APPEND CONFIG_CMDLINE_APPEND

Too many macros reduces the readability of code, avoid them when possible.

> +#else

Explain why this else leg is needed. It should be possible to set 
default values directly in Kconfig.

> +#define CMDLINE_PREPEND ""
> +#define CMDLINE_APPEND ""
> +#define CMDLINE_TMP ""
> +#define CMDLINE_STATIC_PREPEND ""
> +#define CMDLINE_STATIC_APPEND ""
> +#endif
> +
> +#ifndef CMDLINE_STRLCAT
> +#define CMDLINE_STRLCAT strlcat
> +#endif
> +
> +#ifndef CMDLINE_STRLEN
> +#define CMDLINE_STRLEN strlen
> +#endif
> +
> +/*
> + * This function will append or prepend a builtin command line to the command
> + * line provided by the bootloader. Kconfig options can be used to alter
> + * the behavior of this builtin command line.
> + * @dest: The destination of the final appended/prepended string
> + * @tmp: temporary space used for prepending
> + * @prepend: string to prepend to @dest
> + * @append: string to append to @dest
> + * @length: the maximum length of the strings above.
> + * @cmdline_strlen: point to a compatible strlen

Remove that function pointer argument and use macros.

> + * @cmdline_strlcat: point to a compatible strlcat

Same

> + * This function returns true when the builtin command line was copied successfully
> + * and false when there was not enough room to copy all parts of the command line.

What happens when it returns false, is it partially copied or nothing is 
done ?

> + */
> +static inline bool
> +__cmdline_add_builtin(
> +		char *dest,
> +		char *tmp,
> +		char *prepend,
> +		char *append,
> +		unsigned long length,
> +		size_t (*cmdline_strlen)(const char *s),
> +		size_t (*cmdline_strlcat)(char *dest, const char *src, size_t count))

This cmdline feature is used in early deep parts of architectures, so in 
a way more or less comparable to linux-mm. Approach with linux-mm has 
always been to define macros that can be overriden by architectures. 
Please do the same and define cmdline_strlen() and cmdline_strlcat() as 
macros that can be overriden by architectures instead of passing 
function pointers. And keep macro names as lower case for this type of 
macros.

> +{
> +	size_t total_length = 0, tmp_length;

Try to use shorter names for local variables, see kernel codying style.

> +
> +	if (!IS_ENABLED(CONFIG_GENERIC_CMDLINE))
> +		return true;
> +
> +	if (!IS_ENABLED(CONFIG_CMDLINE_BOOL))
> +		return true;
> +
> +	if (IS_ENABLED(CONFIG_CMDLINE_OVERRIDE))
> +		dest[0] = '\0';
> +	else
> +		total_length += cmdline_strlen(dest);

All those Kconfig options should be explained in the commit message, 
that would help understanding the patch.

Impact on existing defconfigs should also be taken care of and minimised.

> +
> +	tmp_length = cmdline_strlen(append);
> +	if (tmp_length > 0) {
> +		cmdline_strlcat(dest, append, length);
> +		total_length += tmp_length;
> +	}
> +
> +	tmp_length = cmdline_strlen(prepend);
> +	if (tmp_length > 0) {
> +		cmdline_strlcat(tmp, prepend, length);
> +		cmdline_strlcat(tmp, dest, length);
> +		dest[0] = '\0';
> +		cmdline_strlcat(dest, tmp, length);
> +		total_length += tmp_length;
> +	}
> +
> +	tmp[0] = '\0';

What's the purpose of setting tmp[0] to 0 ?

> +
> +	if (total_length > length)
> +		return false;
> +
> +	return true;

Can be writen as:

	return total_length <= length;

> +}
> +
> +#define cmdline_add_builtin(dest) \
> +	__cmdline_add_builtin(dest, CMDLINE_TMP, CMDLINE_PREPEND, CMDLINE_APPEND, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT)
> +
> +#define cmdline_get_static_builtin(dest) \
> +	(CMDLINE_STATIC_PREPEND CMDLINE_STATIC_APPEND)
> +#endif
> diff --git a/init/Kconfig b/init/Kconfig
> index 6d35728b94b2..703eed88d140 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1922,6 +1922,85 @@ config TRACEPOINTS
>   
>   source "kernel/Kconfig.kexec"
>   
> +config GENERIC_CMDLINE
> +	bool
> +
> +if GENERIC_CMDLINE
> +
> +config CMDLINE_BOOL
> +	bool "Built-in kernel command line"
> +	help
> +	  Allow for specifying boot arguments to the kernel at
> +	  build time.  On some systems (e.g. embedded ones), it is
> +	  necessary or convenient to provide some or all of the
> +	  kernel boot arguments with the kernel itself (that is,
> +	  to not rely on the boot loader to provide them.)
> +
> +	  To compile command line arguments into the kernel,
> +	  set this option to 'Y', then fill in the
> +	  the boot arguments in CONFIG_CMDLINE.
> +
> +	  Systems with fully functional boot loaders (i.e. non-embedded)
> +	  should leave this option set to 'N'.
> +
> +config CMDLINE_APPEND
> +	string "Built-in kernel command string append"
> +	depends on CMDLINE_BOOL
> +	default ""
> +	help
> +	  Enter arguments here that should be compiled into the kernel
> +	  image and used at boot time.  If the boot loader provides a
> +	  command line at boot time, this string is appended to it to
> +	  form the full kernel command line, when the system boots.
> +
> +	  However, you can use the CONFIG_CMDLINE_OVERRIDE option to
> +	  change this behavior.
> +
> +	  In most cases, the command line (whether built-in or provided
> +	  by the boot loader) should specify the device for the root
> +	  file system.
> +
> +config CMDLINE_PREPEND
> +	string "Built-in kernel command string prepend"
> +	depends on CMDLINE_BOOL
> +	default ""
> +	help
> +	  Enter arguments here that should be compiled into the kernel
> +	  image and used at boot time.  If the boot loader provides a
> +	  command line at boot time, this string is prepended to it to
> +	  form the full kernel command line, when the system boots.
> +
> +	  However, you can use the CONFIG_CMDLINE_OVERRIDE option to
> +	  change this behavior.
> +
> +	  In most cases, the command line (whether built-in or provided
> +	  by the boot loader) should specify the device for the root
> +	  file system.
> +
> +config CMDLINE_EXTRA
> +	bool "Reserve more space for inserting prepend and append without recompiling"
> +	depends on CMDLINE_BOOL
> +	select SYSTEM_EXTRA_CERTIFICATE
> +	help
> +	  If set, space for an append and prepend will be reserved in the kernel
> +	  image. This allows updating or changing the append and prepend to a large
> +	  string then the kernel was compiled for without recompiling the kernel.

s/large/larger
s/then/than

> +
> +	  The maximum size is the command line size for each prepend and append.
> +
> +config CMDLINE_OVERRIDE
> +	bool "Built-in command line overrides boot loader arguments"
> +	depends on CMDLINE_BOOL
> +	help
> +	  Set this option to 'Y' to have the kernel ignore the boot loader
> +	  command line, and use ONLY the built-in command line. In this case
> +	  append and prepend strings are concatenated to form the full
> +	  command line.
> +
> +	  This is used to work around broken boot loaders.  This should
> +	  be set to 'N' under normal conditions.
> +endif

An analysis of what exists on each existing architecture should 
demonstrate that you have defined all needed options.

> +
>   endmenu		# General setup
>   
>   source "arch/Kconfig"
> diff --git a/lib/Kconfig b/lib/Kconfig
> index c686f4adc124..d520f1aa7c32 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -729,6 +729,10 @@ config PARMAN
>   config OBJAGG
>   	tristate "objagg" if COMPILE_TEST
>   
> +config TEST_CMDLINE
> +	depends on CMDLINE_BOOL && !CMDLINE_OVERRIDE
> +	tristate "Test generic command line handling"
> +

Put the test part in a second patch.

>   endmenu
>   
>   config GENERIC_IOREMAP
> diff --git a/lib/Makefile b/lib/Makefile
> index 740109b6e2c8..aa7b14a0ced7 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -438,3 +438,6 @@ $(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
>   ifeq ($(CONFIG_FORTIFY_SOURCE),y)
>   $(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
>   endif
> +
> +obj-$(CONFIG_TEST_CMDLINE) += test_cmdline1.o
> +obj-$(CONFIG_CMDLINE_BOOL)     += generic_cmdline.o
> diff --git a/lib/generic_cmdline.S b/lib/generic_cmdline.S
> new file mode 100644
> index 000000000000..223763f9eeb6
> --- /dev/null
> +++ b/lib/generic_cmdline.S
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/export.h>
> +#include <linux/init.h>
> +
> +#include <asm/setup.h>
> +
> +        __INITDATA
> +
> +       .align 8
> +       .global cmdline_prepend
> +cmdline_prepend:
> +       .ifnc CONFIG_CMDLINE_PREPEND,""
> +       .ascii CONFIG_CMDLINE_PREPEND
> +       .string " "
> +       .else
> +       .byte 0x0
> +       .endif
> +#ifdef CONFIG_CMDLINE_EXTRA
> +       .space COMMAND_LINE_SIZE - (.-cmdline_prepend)
> +       .size cmdline_prepend, COMMAND_LINE_SIZE
> +#endif /* CONFIG_CMDLINE_EXTRA */
> +
> +cmdline_prepend_end:
> +       .size cmdline_prepend, (cmdline_prepend_end - cmdline_prepend)
> +
> +       .align 8
> +       .global cmdline_tmp
> +cmdline_tmp:
> +       .ifnc CONFIG_CMDLINE_PREPEND,""
> +       .size cmdline_tmp, COMMAND_LINE_SIZE
> +       .space COMMAND_LINE_SIZE
> +       .else
> +       .byte 0x0
> +       .endif
> +cmdline_tmp_end:
> +       .size cmdline_tmp, (cmdline_tmp_end - cmdline_tmp)
> +
> +       .align 8
> +       .global cmdline_append
> +       .size cmdline_append, COMMAND_LINE_SIZE
> +cmdline_append:
> +       .ifnc CONFIG_CMDLINE_APPEND,""
> +       .ascii " "
> +       .string CONFIG_CMDLINE_APPEND
> +       .else
> +       .byte 0x0
> +       .endif
> +#ifdef CONFIG_CMDLINE_EXTRA
> +       .space COMMAND_LINE_SIZE - (.-cmdline_append)
> +#endif /* CONFIG_CMDLINE_EXTRA */
> +cmdline_append_end:
> +       .size cmdline_append, (cmdline_append_end - cmdline_append)
> +

Can all this be done in a C file, possibly with inline assembly if 
required, instead of an assembly file ?

> diff --git a/lib/test_cmdline1.c b/lib/test_cmdline1.c
> new file mode 100644
> index 000000000000..bcaffcc024e4
> --- /dev/null
> +++ b/lib/test_cmdline1.c
> @@ -0,0 +1,139 @@
> +// SPDX-License-Identifier: GPL-2.0-only

Why 2.0-only ? Not sure it is the preferred licence for new files unless 
they are tied to something already existing.

> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitmap.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/cmdline.h>
> +#include <linux/uaccess.h>
> +
> +#include "../tools/testing/selftests/kselftest_module.h"

Is it common practice to include tools/testing/ stuff into kernel ?

> +
> +KSTM_MODULE_GLOBALS();
> +
> +char test1_prepend[] = "prepend ";
> +char test1_append[] = " append";
> +char test1_bootloader_args[] = "console=ttyS0 log_level=3";
> +char test1_result[] = "prepend console=ttyS0 log_level=3 append";
> +
> +char test2_append[] = " append";
> +char test2_bootloader_args[] = "console=ttyS0 log_level=3";
> +char test2_result[] = "console=ttyS0 log_level=3 append";
> +
> +char test3_prepend[] = "prepend ";
> +char test3_bootloader_args[] = "console=ttyS0 log_level=3";
> +char test3_result[] = "prepend console=ttyS0 log_level=3";
> +
> +char test4_bootloader_args[] = "console=ttyS0 log_level=3";
> +char test4_result[] = "console=ttyS0 log_level=3";
> +
> +char test5_prepend[] = "prepend ";
> +char test5_append[] = " append";
> +static char test5_bootloader_args[] =
> +"00000000000000 011111111111111 0222222222222222 033333333333333 "
> +"10000000000000 111111111111111 1222222222222222 133333333333333 "
> +"20000000000000 211111111111111 2222222222222222 233333333333333 "
> +"30000000000000 311111111111111 3222222222222222 333333333333333 "
> +"40000000000000 411111111111111 4222222222222222 433333333333333 "
> +"50000000000000 511111111111111 5222222222222222 533333333333333 "
> +"60000000000000 611111111111111 6222222222222222 633333333333333 "
> +"70000000000000 711111111111111 7222222222222222 733333333333333";
> +static char test5_result[] =
> +"prepend 00000000000000 011111111111111 0222222222222222 033333333333333 "
> +"10000000000000 111111111111111 1222222222222222 133333333333333 "
> +"20000000000000 211111111111111 2222222222222222 233333333333333 "
> +"30000000000000 311111111111111 3222222222222222 333333333333333 "
> +"40000000000000 411111111111111 4222222222222222 433333333333333 "
> +"50000000000000 511111111111111 5222222222222222 533333333333333 "
> +"60000000000000 611111111111111 6222222222222222 633333333333333 "
> +"70000000000000 711111111111111 7222222222222222 7333333";
> +
> +char test5_boot_command_line[COMMAND_LINE_SIZE];
> +
> +char test_tmp[COMMAND_LINE_SIZE];
> +
> +char test_boot_command_line[COMMAND_LINE_SIZE];
> +
> +static void __init selftest(void)
> +{
> +	bool result;
> +
> +	/* Normal operation */
> +	strcpy(test_boot_command_line, test1_bootloader_args);
> +	test_tmp[0] = '\0';
> +	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, test1_prepend, test1_append, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
> +
> +	if (result == true && !strncmp(test_boot_command_line, test1_result, COMMAND_LINE_SIZE)) {
> +		pr_info("test1 success.\n");
> +	} else {
> +		pr_info("test1 failed. OUTPUT BELOW:\n");
> +		pr_info("\"%s\"\n", test_boot_command_line);
> +		failed_tests++;
> +	}
> +	total_tests++;
> +
> +	/* Missing prepend */
> +	strcpy(test_boot_command_line, test2_bootloader_args);
> +	test_tmp[0] = '\0';
> +	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, "", test2_append, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
> +
> +	if (result == true && !strncmp(test_boot_command_line, test2_result, COMMAND_LINE_SIZE)) {
> +		pr_info("test2 success.\n");
> +	} else {
> +		pr_info("test2 failed. OUTPUT BELOW:\n");
> +		pr_info("\"%s\"\n", test_boot_command_line);
> +		failed_tests++;
> +	}

Can you refactor and avoid repeating those 7 lines five times ?

> +	total_tests++;
> +
> +	/* Missing append */
> +	strcpy(test_boot_command_line, test3_bootloader_args);
> +	test_tmp[0] = '\0';
> +	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, test3_prepend, "", COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
> +
> +	if (result == true && !strncmp(test_boot_command_line, test3_result, COMMAND_LINE_SIZE)) {
> +		pr_info("test3 success.\n");
> +	} else {
> +		pr_info("test3 failed. OUTPUT BELOW:\n");
> +		pr_info("\"%s\"\n", test_boot_command_line);
> +		failed_tests++;
> +	}
> +	total_tests++;
> +
> +	/* Missing append and prepend */
> +	strcpy(test_boot_command_line, test4_bootloader_args);
> +	test_tmp[0] = '\0';
> +	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, "", "", COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
> +
> +	if (result == true && !strncmp(test_boot_command_line, test4_result, COMMAND_LINE_SIZE)) {
> +		pr_info("test4 success.\n");
> +	} else {
> +		pr_info("test4 failed. OUTPUT BELOW:\n");
> +		pr_info("\"%s\"\n", test_boot_command_line);
> +		failed_tests++;
> +	}
> +	total_tests++;
> +
> +	/* Already full boot arguments */
> +	strcpy(test5_boot_command_line, test5_bootloader_args);
> +	test_tmp[0] = '\0';
> +	result = __cmdline_add_builtin(test5_boot_command_line, test_tmp, test5_prepend, test5_append, 512, CMDLINE_STRLEN, CMDLINE_STRLCAT);
> +
> +	if (result == false && !strncmp(test5_boot_command_line, test5_result, COMMAND_LINE_SIZE)) {
> +		pr_info("test5 success.\n");
> +	} else {
> +		pr_info("test5 failed. OUTPUT BELOW:\n");
> +		pr_info("\"%s\"\n", test5_boot_command_line);
> +		failed_tests++;
> +	}
> +	total_tests++;
> +}
> +
> +KSTM_MODULE_LOADERS(cmdline_test);
> +MODULE_AUTHOR("Daniel Walker <danielwa@cisco.com>");
> +MODULE_LICENSE("GPL");
Jaskaran Singh Dec. 4, 2023, 11:11 a.m. UTC | #3
On 11/10/2023 7:08 AM, Daniel Walker wrote:
> diff --git a/lib/generic_cmdline.S b/lib/generic_cmdline.S
> new file mode 100644
> index 000000000000..223763f9eeb6
> --- /dev/null
> +++ b/lib/generic_cmdline.S
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/export.h>
> +#include <linux/init.h>
> +
> +#include <asm/setup.h>
> +
> +        __INITDATA
> +
> +       .align 8
> +       .global cmdline_prepend
> +cmdline_prepend:
> +       .ifnc CONFIG_CMDLINE_PREPEND,""
> +       .ascii CONFIG_CMDLINE_PREPEND
> +       .string " "
> +       .else
> +       .byte 0x0
> +       .endif
> +#ifdef CONFIG_CMDLINE_EXTRA
> +       .space COMMAND_LINE_SIZE - (.-cmdline_prepend)
> +       .size cmdline_prepend, COMMAND_LINE_SIZE
> +#endif /* CONFIG_CMDLINE_EXTRA */
> +
> +cmdline_prepend_end:
> +       .size cmdline_prepend, (cmdline_prepend_end - cmdline_prepend)
> +
> +       .align 8
> +       .global cmdline_tmp
> +cmdline_tmp:
> +       .ifnc CONFIG_CMDLINE_PREPEND,""
> +       .size cmdline_tmp, COMMAND_LINE_SIZE
> +       .space COMMAND_LINE_SIZE
> +       .else
> +       .byte 0x0
> +       .endif
> +cmdline_tmp_end:
> +       .size cmdline_tmp, (cmdline_tmp_end - cmdline_tmp)
> +
> +       .align 8
> +       .global cmdline_append
> +       .size cmdline_append, COMMAND_LINE_SIZE
> +cmdline_append:
> +       .ifnc CONFIG_CMDLINE_APPEND,""
> +       .ascii " "
> +       .string CONFIG_CMDLINE_APPEND
> +       .else
> +       .byte 0x0
> +       .endif
> +#ifdef CONFIG_CMDLINE_EXTRA
> +       .space COMMAND_LINE_SIZE - (.-cmdline_append)
> +#endif /* CONFIG_CMDLINE_EXTRA */
> +cmdline_append_end:
> +       .size cmdline_append, (cmdline_append_end - cmdline_append)
> +

Hi Daniel,

I picked these patches to test a usecase of ours. generic_cmdline.S does
not escape semicolons in the CMDLINE_APPEND and CMDLINE_PREPEND strings.
Take this config snippet for example:

CONFIG_CMDLINE_APPEND="slub_debug=FZP,zs_handle,zspage;FZPU page_owner=on"
CONFIG_CMDLINE_BOOL=y
# CONFIG_CMDLINE_EXTRA is not set
# CONFIG_CMDLINE_OVERRIDE is not set
# CONFIG_CMDLINE_PREPEND is not set
# CONFIG_TEST_CMDLINE is not set

While compiling, the word FZPU is considered as a mnemonic because of
the semicolon preceding it causing parsing to fail:

kernel/lib/generic_cmdline.S: Assembler messages:
kernel/lib/generic_cmdline.S:42: Warning: missing closing `"'
kernel/lib/generic_cmdline.S:42: Error: unknown mnemonic `fzpu' -- `fzpu
page_owner=on",""'

Maybe Christophe's suggestion of moving this code to a C file and using
inline assembly helps mitigate similar problems?

Thanks,
Jaskaran.
diff mbox series

Patch

diff --git a/include/linux/cmdline.h b/include/linux/cmdline.h
new file mode 100644
index 000000000000..a94758a0f257
--- /dev/null
+++ b/include/linux/cmdline.h
@@ -0,0 +1,106 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CMDLINE_H
+#define _LINUX_CMDLINE_H
+/*
+ *
+ * Copyright (C) 2006,2021. Cisco Systems, Inc.
+ *
+ * Generic Append/Prepend cmdline support.
+ */
+
+
+#include <linux/ctype.h>
+#include <linux/cache.h>
+#include <asm/setup.h>
+
+#ifdef CONFIG_CMDLINE_BOOL
+extern char cmdline_prepend[];
+extern char cmdline_append[];
+extern char cmdline_tmp[];
+#define CMDLINE_PREPEND cmdline_prepend
+#define CMDLINE_APPEND cmdline_append
+#define CMDLINE_TMP cmdline_tmp
+#define CMDLINE_STATIC_PREPEND CONFIG_CMDLINE_PREPEND
+#define CMDLINE_STATIC_APPEND CONFIG_CMDLINE_APPEND
+#else
+#define CMDLINE_PREPEND ""
+#define CMDLINE_APPEND ""
+#define CMDLINE_TMP ""
+#define CMDLINE_STATIC_PREPEND ""
+#define CMDLINE_STATIC_APPEND ""
+#endif
+
+#ifndef CMDLINE_STRLCAT
+#define CMDLINE_STRLCAT strlcat
+#endif
+
+#ifndef CMDLINE_STRLEN
+#define CMDLINE_STRLEN strlen
+#endif
+
+/*
+ * This function will append or prepend a builtin command line to the command
+ * line provided by the bootloader. Kconfig options can be used to alter
+ * the behavior of this builtin command line.
+ * @dest: The destination of the final appended/prepended string
+ * @tmp: temporary space used for prepending
+ * @prepend: string to prepend to @dest
+ * @append: string to append to @dest
+ * @length: the maximum length of the strings above.
+ * @cmdline_strlen: point to a compatible strlen
+ * @cmdline_strlcat: point to a compatible strlcat
+ * This function returns true when the builtin command line was copied successfully
+ * and false when there was not enough room to copy all parts of the command line.
+ */
+static inline bool
+__cmdline_add_builtin(
+		char *dest,
+		char *tmp,
+		char *prepend,
+		char *append,
+		unsigned long length,
+		size_t (*cmdline_strlen)(const char *s),
+		size_t (*cmdline_strlcat)(char *dest, const char *src, size_t count))
+{
+	size_t total_length = 0, tmp_length;
+
+	if (!IS_ENABLED(CONFIG_GENERIC_CMDLINE))
+		return true;
+
+	if (!IS_ENABLED(CONFIG_CMDLINE_BOOL))
+		return true;
+
+	if (IS_ENABLED(CONFIG_CMDLINE_OVERRIDE))
+		dest[0] = '\0';
+	else
+		total_length += cmdline_strlen(dest);
+
+	tmp_length = cmdline_strlen(append);
+	if (tmp_length > 0) {
+		cmdline_strlcat(dest, append, length);
+		total_length += tmp_length;
+	}
+
+	tmp_length = cmdline_strlen(prepend);
+	if (tmp_length > 0) {
+		cmdline_strlcat(tmp, prepend, length);
+		cmdline_strlcat(tmp, dest, length);
+		dest[0] = '\0';
+		cmdline_strlcat(dest, tmp, length);
+		total_length += tmp_length;
+	}
+
+	tmp[0] = '\0';
+
+	if (total_length > length)
+		return false;
+
+	return true;
+}
+
+#define cmdline_add_builtin(dest) \
+	__cmdline_add_builtin(dest, CMDLINE_TMP, CMDLINE_PREPEND, CMDLINE_APPEND, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT)
+
+#define cmdline_get_static_builtin(dest) \
+	(CMDLINE_STATIC_PREPEND CMDLINE_STATIC_APPEND)
+#endif
diff --git a/init/Kconfig b/init/Kconfig
index 6d35728b94b2..703eed88d140 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1922,6 +1922,85 @@  config TRACEPOINTS
 
 source "kernel/Kconfig.kexec"
 
+config GENERIC_CMDLINE
+	bool
+
+if GENERIC_CMDLINE
+
+config CMDLINE_BOOL
+	bool "Built-in kernel command line"
+	help
+	  Allow for specifying boot arguments to the kernel at
+	  build time.  On some systems (e.g. embedded ones), it is
+	  necessary or convenient to provide some or all of the
+	  kernel boot arguments with the kernel itself (that is,
+	  to not rely on the boot loader to provide them.)
+
+	  To compile command line arguments into the kernel,
+	  set this option to 'Y', then fill in the
+	  the boot arguments in CONFIG_CMDLINE.
+
+	  Systems with fully functional boot loaders (i.e. non-embedded)
+	  should leave this option set to 'N'.
+
+config CMDLINE_APPEND
+	string "Built-in kernel command string append"
+	depends on CMDLINE_BOOL
+	default ""
+	help
+	  Enter arguments here that should be compiled into the kernel
+	  image and used at boot time.  If the boot loader provides a
+	  command line at boot time, this string is appended to it to
+	  form the full kernel command line, when the system boots.
+
+	  However, you can use the CONFIG_CMDLINE_OVERRIDE option to
+	  change this behavior.
+
+	  In most cases, the command line (whether built-in or provided
+	  by the boot loader) should specify the device for the root
+	  file system.
+
+config CMDLINE_PREPEND
+	string "Built-in kernel command string prepend"
+	depends on CMDLINE_BOOL
+	default ""
+	help
+	  Enter arguments here that should be compiled into the kernel
+	  image and used at boot time.  If the boot loader provides a
+	  command line at boot time, this string is prepended to it to
+	  form the full kernel command line, when the system boots.
+
+	  However, you can use the CONFIG_CMDLINE_OVERRIDE option to
+	  change this behavior.
+
+	  In most cases, the command line (whether built-in or provided
+	  by the boot loader) should specify the device for the root
+	  file system.
+
+config CMDLINE_EXTRA
+	bool "Reserve more space for inserting prepend and append without recompiling"
+	depends on CMDLINE_BOOL
+	select SYSTEM_EXTRA_CERTIFICATE
+	help
+	  If set, space for an append and prepend will be reserved in the kernel
+	  image. This allows updating or changing the append and prepend to a large
+	  string then the kernel was compiled for without recompiling the kernel.
+
+	  The maximum size is the command line size for each prepend and append.
+
+config CMDLINE_OVERRIDE
+	bool "Built-in command line overrides boot loader arguments"
+	depends on CMDLINE_BOOL
+	help
+	  Set this option to 'Y' to have the kernel ignore the boot loader
+	  command line, and use ONLY the built-in command line. In this case
+	  append and prepend strings are concatenated to form the full
+	  command line.
+
+	  This is used to work around broken boot loaders.  This should
+	  be set to 'N' under normal conditions.
+endif
+
 endmenu		# General setup
 
 source "arch/Kconfig"
diff --git a/lib/Kconfig b/lib/Kconfig
index c686f4adc124..d520f1aa7c32 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -729,6 +729,10 @@  config PARMAN
 config OBJAGG
 	tristate "objagg" if COMPILE_TEST
 
+config TEST_CMDLINE
+	depends on CMDLINE_BOOL && !CMDLINE_OVERRIDE
+	tristate "Test generic command line handling"
+
 endmenu
 
 config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index 740109b6e2c8..aa7b14a0ced7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -438,3 +438,6 @@  $(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
 ifeq ($(CONFIG_FORTIFY_SOURCE),y)
 $(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
 endif
+
+obj-$(CONFIG_TEST_CMDLINE) += test_cmdline1.o
+obj-$(CONFIG_CMDLINE_BOOL)     += generic_cmdline.o
diff --git a/lib/generic_cmdline.S b/lib/generic_cmdline.S
new file mode 100644
index 000000000000..223763f9eeb6
--- /dev/null
+++ b/lib/generic_cmdline.S
@@ -0,0 +1,53 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/export.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+
+        __INITDATA
+
+       .align 8
+       .global cmdline_prepend
+cmdline_prepend:
+       .ifnc CONFIG_CMDLINE_PREPEND,""
+       .ascii CONFIG_CMDLINE_PREPEND
+       .string " "
+       .else
+       .byte 0x0
+       .endif
+#ifdef CONFIG_CMDLINE_EXTRA
+       .space COMMAND_LINE_SIZE - (.-cmdline_prepend)
+       .size cmdline_prepend, COMMAND_LINE_SIZE
+#endif /* CONFIG_CMDLINE_EXTRA */
+
+cmdline_prepend_end:
+       .size cmdline_prepend, (cmdline_prepend_end - cmdline_prepend)
+
+       .align 8
+       .global cmdline_tmp
+cmdline_tmp:
+       .ifnc CONFIG_CMDLINE_PREPEND,""
+       .size cmdline_tmp, COMMAND_LINE_SIZE
+       .space COMMAND_LINE_SIZE
+       .else
+       .byte 0x0
+       .endif
+cmdline_tmp_end:
+       .size cmdline_tmp, (cmdline_tmp_end - cmdline_tmp)
+
+       .align 8
+       .global cmdline_append
+       .size cmdline_append, COMMAND_LINE_SIZE
+cmdline_append:
+       .ifnc CONFIG_CMDLINE_APPEND,""
+       .ascii " "
+       .string CONFIG_CMDLINE_APPEND
+       .else
+       .byte 0x0
+       .endif
+#ifdef CONFIG_CMDLINE_EXTRA
+       .space COMMAND_LINE_SIZE - (.-cmdline_append)
+#endif /* CONFIG_CMDLINE_EXTRA */
+cmdline_append_end:
+       .size cmdline_append, (cmdline_append_end - cmdline_append)
+
diff --git a/lib/test_cmdline1.c b/lib/test_cmdline1.c
new file mode 100644
index 000000000000..bcaffcc024e4
--- /dev/null
+++ b/lib/test_cmdline1.c
@@ -0,0 +1,139 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/cmdline.h>
+#include <linux/uaccess.h>
+
+#include "../tools/testing/selftests/kselftest_module.h"
+
+KSTM_MODULE_GLOBALS();
+
+char test1_prepend[] = "prepend ";
+char test1_append[] = " append";
+char test1_bootloader_args[] = "console=ttyS0 log_level=3";
+char test1_result[] = "prepend console=ttyS0 log_level=3 append";
+
+char test2_append[] = " append";
+char test2_bootloader_args[] = "console=ttyS0 log_level=3";
+char test2_result[] = "console=ttyS0 log_level=3 append";
+
+char test3_prepend[] = "prepend ";
+char test3_bootloader_args[] = "console=ttyS0 log_level=3";
+char test3_result[] = "prepend console=ttyS0 log_level=3";
+
+char test4_bootloader_args[] = "console=ttyS0 log_level=3";
+char test4_result[] = "console=ttyS0 log_level=3";
+
+char test5_prepend[] = "prepend ";
+char test5_append[] = " append";
+static char test5_bootloader_args[] =
+"00000000000000 011111111111111 0222222222222222 033333333333333 "
+"10000000000000 111111111111111 1222222222222222 133333333333333 "
+"20000000000000 211111111111111 2222222222222222 233333333333333 "
+"30000000000000 311111111111111 3222222222222222 333333333333333 "
+"40000000000000 411111111111111 4222222222222222 433333333333333 "
+"50000000000000 511111111111111 5222222222222222 533333333333333 "
+"60000000000000 611111111111111 6222222222222222 633333333333333 "
+"70000000000000 711111111111111 7222222222222222 733333333333333";
+static char test5_result[] =
+"prepend 00000000000000 011111111111111 0222222222222222 033333333333333 "
+"10000000000000 111111111111111 1222222222222222 133333333333333 "
+"20000000000000 211111111111111 2222222222222222 233333333333333 "
+"30000000000000 311111111111111 3222222222222222 333333333333333 "
+"40000000000000 411111111111111 4222222222222222 433333333333333 "
+"50000000000000 511111111111111 5222222222222222 533333333333333 "
+"60000000000000 611111111111111 6222222222222222 633333333333333 "
+"70000000000000 711111111111111 7222222222222222 7333333";
+
+char test5_boot_command_line[COMMAND_LINE_SIZE];
+
+char test_tmp[COMMAND_LINE_SIZE];
+
+char test_boot_command_line[COMMAND_LINE_SIZE];
+
+static void __init selftest(void)
+{
+	bool result;
+
+	/* Normal operation */
+	strcpy(test_boot_command_line, test1_bootloader_args);
+	test_tmp[0] = '\0';
+	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, test1_prepend, test1_append, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
+
+	if (result == true && !strncmp(test_boot_command_line, test1_result, COMMAND_LINE_SIZE)) {
+		pr_info("test1 success.\n");
+	} else {
+		pr_info("test1 failed. OUTPUT BELOW:\n");
+		pr_info("\"%s\"\n", test_boot_command_line);
+		failed_tests++;
+	}
+	total_tests++;
+
+	/* Missing prepend */
+	strcpy(test_boot_command_line, test2_bootloader_args);
+	test_tmp[0] = '\0';
+	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, "", test2_append, COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
+
+	if (result == true && !strncmp(test_boot_command_line, test2_result, COMMAND_LINE_SIZE)) {
+		pr_info("test2 success.\n");
+	} else {
+		pr_info("test2 failed. OUTPUT BELOW:\n");
+		pr_info("\"%s\"\n", test_boot_command_line);
+		failed_tests++;
+	}
+	total_tests++;
+
+	/* Missing append */
+	strcpy(test_boot_command_line, test3_bootloader_args);
+	test_tmp[0] = '\0';
+	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, test3_prepend, "", COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
+
+	if (result == true && !strncmp(test_boot_command_line, test3_result, COMMAND_LINE_SIZE)) {
+		pr_info("test3 success.\n");
+	} else {
+		pr_info("test3 failed. OUTPUT BELOW:\n");
+		pr_info("\"%s\"\n", test_boot_command_line);
+		failed_tests++;
+	}
+	total_tests++;
+
+	/* Missing append and prepend */
+	strcpy(test_boot_command_line, test4_bootloader_args);
+	test_tmp[0] = '\0';
+	result = __cmdline_add_builtin(test_boot_command_line, test_tmp, "", "", COMMAND_LINE_SIZE, CMDLINE_STRLEN, CMDLINE_STRLCAT);
+
+	if (result == true && !strncmp(test_boot_command_line, test4_result, COMMAND_LINE_SIZE)) {
+		pr_info("test4 success.\n");
+	} else {
+		pr_info("test4 failed. OUTPUT BELOW:\n");
+		pr_info("\"%s\"\n", test_boot_command_line);
+		failed_tests++;
+	}
+	total_tests++;
+
+	/* Already full boot arguments */
+	strcpy(test5_boot_command_line, test5_bootloader_args);
+	test_tmp[0] = '\0';
+	result = __cmdline_add_builtin(test5_boot_command_line, test_tmp, test5_prepend, test5_append, 512, CMDLINE_STRLEN, CMDLINE_STRLCAT);
+
+	if (result == false && !strncmp(test5_boot_command_line, test5_result, COMMAND_LINE_SIZE)) {
+		pr_info("test5 success.\n");
+	} else {
+		pr_info("test5 failed. OUTPUT BELOW:\n");
+		pr_info("\"%s\"\n", test5_boot_command_line);
+		failed_tests++;
+	}
+	total_tests++;
+}
+
+KSTM_MODULE_LOADERS(cmdline_test);
+MODULE_AUTHOR("Daniel Walker <danielwa@cisco.com>");
+MODULE_LICENSE("GPL");