diff --git a/include/linux/cmdline.h b/include/linux/cmdline.h
new file mode 100644
index 000000000000..34d9d8d14672
--- /dev/null
+++ b/include/linux/cmdline.h
@@ -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
diff --git a/init/Kconfig b/init/Kconfig
index 5f5c776ef192..d72eb5a804c6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -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"
diff --git a/lib/Kconfig b/lib/Kconfig
index a38cc61256f1..b243c133ec13 100644
--- a/lib/Kconfig
+++ b/lib/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
diff --git a/lib/Makefile b/lib/Makefile
index b5307d3eec1a..6537d11d0cc0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
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..ae03cf198165
--- /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[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");
