summaryrefslogtreecommitdiff
path: root/src/drivers/ocp/vpd
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/ocp/vpd')
-rw-r--r--src/drivers/ocp/vpd/Kconfig9
-rw-r--r--src/drivers/ocp/vpd/Makefile.inc1
-rw-r--r--src/drivers/ocp/vpd/vpd_cmdline.c56
3 files changed, 66 insertions, 0 deletions
diff --git a/src/drivers/ocp/vpd/Kconfig b/src/drivers/ocp/vpd/Kconfig
index ebecbe6962..12ee1855a2 100644
--- a/src/drivers/ocp/vpd/Kconfig
+++ b/src/drivers/ocp/vpd/Kconfig
@@ -4,3 +4,12 @@ config OCP_VPD
depends on VPD
help
It implements functions that get common VPD variables for OCP projects.
+
+config LINUXPAYLOAD_CMDLINE_VPD_OVERWRITE
+ bool
+ default n
+ depends on VPD
+ help
+ Overwrite Linux payload's kernel command line by using VPD. Currently only
+ overwrite the value of kernel command line 'loglevel'. The Linux kernel command
+ line data is detected in the last segment loaded in memory and overwritten.
diff --git a/src/drivers/ocp/vpd/Makefile.inc b/src/drivers/ocp/vpd/Makefile.inc
index 8db5afaf47..b40534aec4 100644
--- a/src/drivers/ocp/vpd/Makefile.inc
+++ b/src/drivers/ocp/vpd/Makefile.inc
@@ -1,5 +1,6 @@
romstage-$(CONFIG_OCP_VPD) += vpd_util.c
ramstage-$(CONFIG_OCP_VPD) += vpd_util.c
+ramstage-$(CONFIG_LINUXPAYLOAD_CMDLINE_VPD_OVERWRITE) += vpd_cmdline.c
ifeq ($(CONFIG_VPD),y)
all-$(CONFIG_CONSOLE_OVERRIDE_LOGLEVEL) += loglevel_vpd.c
endif
diff --git a/src/drivers/ocp/vpd/vpd_cmdline.c b/src/drivers/ocp/vpd/vpd_cmdline.c
new file mode 100644
index 0000000000..44e166c5c7
--- /dev/null
+++ b/src/drivers/ocp/vpd/vpd_cmdline.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <console/console.h>
+#include <drivers/vpd/vpd.h>
+#include <drivers/ocp/include/vpd.h>
+#include <string.h>
+#include <program_loading.h>
+
+#define CMDLINE_LOGLVL_STR "loglevel="
+
+static void overwrite_kernel_loglevel(uintptr_t start)
+{
+ int val;
+ if (!vpd_get_int(KERNEL_LOG_LEVEL, VPD_RW_THEN_RO, &val)) {
+ printk(BIOS_DEBUG, "%s: not able to get VPD %s\n", __func__, KERNEL_LOG_LEVEL);
+ return;
+ }
+
+ printk(BIOS_DEBUG, "%s: VPD %s, got %d\n", __func__, KERNEL_LOG_LEVEL, val);
+ if (val < 0 || val > 7) {
+ printk(BIOS_INFO, "Invalid VPD for Linux kernel log level\n");
+ return;
+ }
+
+ int loglevel;
+ char *loc = strstr((const char *)start, CMDLINE_LOGLVL_STR);
+ if (!loc) {
+ printk(BIOS_INFO, "%s is not found from LINUX_COMMAND_LINE\n",
+ CMDLINE_LOGLVL_STR);
+ return;
+ }
+
+ char *loc_bkup;
+ loc += strlen(CMDLINE_LOGLVL_STR);
+ loc_bkup = loc;
+ loglevel = skip_atoi(&loc);
+ printk(BIOS_DEBUG, "Original kernel log level is %d\n", loglevel);
+ /* Unlikely but don't overwrite with such an unexpected case. */
+ if (loglevel < 0 || loglevel > 7) {
+ printk(BIOS_DEBUG, "Invalid kernel log level, must be from 0 to 7.\n");
+ return;
+ }
+
+ char c = '0' + val;
+ printk(BIOS_INFO, "Overwrite kernel log level with %c from VPD.\n", c);
+ memcpy(loc_bkup, &c, 1);
+}
+
+void platform_segment_loaded(uintptr_t start, size_t size, int flags)
+{
+ /* CONFIG_LINUX_COMMAND_LINE is in the final segment. */
+ if (flags != SEG_FINAL)
+ return;
+
+ overwrite_kernel_loglevel(start);
+}