From 2bb5ecbcd62354c0448e05feb0adadb9b96d47d5 Mon Sep 17 00:00:00 2001 From: Johnny Lin Date: Mon, 29 May 2023 16:23:02 +0800 Subject: drivers/ocp/vpd: Overwrite Linux payload's kernel command via VPD Add a new Kconfig LINUXPAYLOAD_CMDLINE_VPD_OVERWRITE that can overwrite Linux payload's kernel command line from VPD. Currently only overwrite Linux kernel command line 'loglevel' via VPD key 'kernel_log_level'. TESTED=On OCP Delta Lake, with kernel_log_level set to 0, warm reboot time can see about 10 seconds improvement comparing to kernel log level 7. Change-Id: Idf06c7ab9958c940fc3b23d560bb9dade991a6da Signed-off-by: Johnny Lin Reviewed-on: https://review.coreboot.org/c/coreboot/+/75510 Tested-by: build bot (Jenkins) Reviewed-by: David Hendricks --- src/drivers/ocp/include/vpd.h | 3 +++ src/drivers/ocp/vpd/Kconfig | 9 +++++++ src/drivers/ocp/vpd/Makefile.inc | 1 + src/drivers/ocp/vpd/vpd_cmdline.c | 56 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 src/drivers/ocp/vpd/vpd_cmdline.c (limited to 'src/drivers/ocp') diff --git a/src/drivers/ocp/include/vpd.h b/src/drivers/ocp/include/vpd.h index 33fece5c4d..60299e38fd 100644 --- a/src/drivers/ocp/include/vpd.h +++ b/src/drivers/ocp/include/vpd.h @@ -77,6 +77,9 @@ enum cxl_memory_mode { /* Socket 1 core disable bitmask */ #define CORE_DIS_BITMSK1 "core_disable_bitmask1" +/* Linux payload kernel log level */ +#define KERNEL_LOG_LEVEL "kernel_log_level" + /* Get VPD key with provided fallback value and min/max ranges */ int get_int_from_vpd_range(const char *const key, const int fallback, const int min, const int max); 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 +#include +#include +#include +#include + +#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); +} -- cgit v1.2.3