new file mode 100644
@@ -0,0 +1,103 @@
+/* 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 <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)
+
+#endif
@@ -2034,6 +2034,84 @@ config PROFILING
config TRACEPOINTS
bool
+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
+ 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"
@@ -672,6 +672,10 @@ config OBJAGG
config STRING_SELFTEST
tristate "Test string functions"
+config TEST_CMDLINE
+ depends on CMDLINE_BOOL && !CMDLINE_OVERRIDE
+ tristate "Test generic command line handling"
+
endmenu
config GENERIC_IOREMAP
@@ -354,3 +354,6 @@ obj-$(CONFIG_BITS_TEST) += test_bits.o
obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
+
+obj-$(CONFIG_TEST_CMDLINE) += test_cmdline1.o
+obj-$(CONFIG_CMDLINE_BOOL) += generic_cmdline.o
new file mode 100644
@@ -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)
+
new file mode 100644
@@ -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[512];
+
+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");