From 419e4dc450055a27b8ab267c57fe0b6877afe4c8 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sat, 23 Aug 2025 17:27:20 +0200 Subject: [PATCH 1/4] sd-boot: efi-log: use log levels internally Change log_internal() to receive a log level from which a text color is derived, rather than the text color directly, and adjust various log_* macros to use them internally. --- src/boot/drivers.c | 2 +- src/boot/efi-log.c | 16 ++++++++++++++-- src/boot/efi-log.h | 42 +++++++++++++++++++++++++++++++++--------- src/boot/random-seed.c | 4 ++-- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/boot/drivers.c b/src/boot/drivers.c index a44957b4e4..d58b1e39da 100644 --- a/src/boot/drivers.c +++ b/src/boot/drivers.c @@ -35,7 +35,7 @@ static EFI_STATUS load_one_driver( if (loaded_image->ImageCodeType != EfiBootServicesCode && loaded_image->ImageCodeType != EfiRuntimeServicesCode) - return log_error("Image %ls is not a driver, refusing.", fname); + return log_error_status(EFI_INVALID_PARAMETER, "Image %ls is not a driver, refusing.", fname); err = BS->StartImage(image, NULL, NULL); if (err != EFI_SUCCESS) { diff --git a/src/boot/efi-log.c b/src/boot/efi-log.c index 1810375c6a..5b831608f4 100644 --- a/src/boot/efi-log.c +++ b/src/boot/efi-log.c @@ -6,6 +6,17 @@ static unsigned log_count = 0; +static const uint8_t log_level_color[_LOG_MAX] = { + [LOG_EMERG] = EFI_LIGHTRED, + [LOG_ALERT] = EFI_LIGHTRED, + [LOG_CRIT] = EFI_LIGHTRED, + [LOG_ERR] = EFI_LIGHTRED, + [LOG_WARNING] = EFI_YELLOW, + [LOG_NOTICE] = EFI_WHITE, + [LOG_INFO] = EFI_WHITE, + [LOG_DEBUG] = EFI_LIGHTGRAY, +}; + void freeze(void) { for (;;) BS->Stall(60 * 1000 * 1000); @@ -31,14 +42,15 @@ void efi_assert(const char *expr, const char *file, unsigned line, const char *f freeze(); } -EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *format, ...) { +EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *format, ...) { assert(format); + assert(log_level >= 0 && log_level < _LOG_MAX); int32_t attr = ST->ConOut->Mode->Attribute; if (ST->ConOut->Mode->CursorColumn > 0) ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n"); - ST->ConOut->SetAttribute(ST->ConOut, EFI_TEXT_ATTR(text_color, EFI_BLACK)); + ST->ConOut->SetAttribute(ST->ConOut, EFI_TEXT_ATTR(log_level_color[log_level], EFI_BLACK)); va_list ap; va_start(ap, format); diff --git a/src/boot/efi-log.h b/src/boot/efi-log.h index 06954b06f0..9a3c784d6f 100644 --- a/src/boot/efi-log.h +++ b/src/boot/efi-log.h @@ -21,17 +21,41 @@ __attribute__((no_stack_protector, noinline)) void __stack_chk_guard_init(void); # define __stack_chk_guard_init() #endif +typedef enum LogLevel { + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG, + _LOG_MAX, + _LOG_INVALID = -1, +} LogLevel; + _noreturn_ void freeze(void); void log_wait(void); -_gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *format, ...); -#define log_full(status, text_color, format, ...) \ - log_internal(status, text_color, "%s:%i@%s: " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__) -#define log_debug(...) log_full(EFI_SUCCESS, EFI_LIGHTGRAY, __VA_ARGS__) -#define log_info(...) log_full(EFI_SUCCESS, EFI_WHITE, __VA_ARGS__) -#define log_warning_status(status, ...) log_full(status, EFI_YELLOW, __VA_ARGS__) -#define log_error_status(status, ...) log_full(status, EFI_LIGHTRED, __VA_ARGS__) -#define log_error(...) log_full(EFI_INVALID_PARAMETER, EFI_LIGHTRED, __VA_ARGS__) -#define log_oom() log_full(EFI_OUT_OF_RESOURCES, EFI_LIGHTRED, "Out of memory.") +_gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *format, ...); + +#define log_full(status, log_level, format, ...) \ + log_internal(status, log_level, "%s:%i@%s: " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__) + +#define log_debug(...) log_full(EFI_SUCCESS, LOG_DEBUG, __VA_ARGS__) +#define log_info(...) log_full(EFI_SUCCESS, LOG_INFO, __VA_ARGS__) +#define log_notice(...) log_full(EFI_SUCCESS, LOG_NOTICE, __VA_ARGS__) +#define log_warning(...) log_full(EFI_SUCCESS, LOG_WARNING, __VA_ARGS__) +#define log_error(...) log_full(EFI_SUCCESS, LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(EFI_SUCCESS, LOG_EMERG, __VA_ARGS__) + +#define log_debug_status(status, ...) log_full(status, LOG_DEBUG, __VA_ARGS__) +#define log_info_status(status, ...) log_full(status, LOG_INFO, __VA_ARGS__) +#define log_notice_status(status, ...) log_full(status, LOG_NOTICE, __VA_ARGS__) +#define log_warning_status(status, ...) log_full(status, LOG_WARNING, __VA_ARGS__) +#define log_error_status(status, ...) log_full(status, LOG_ERR, __VA_ARGS__) +#define log_emergency_status(status, ...) log_full(status, LOG_EMERG, __VA_ARGS__) + +#define log_oom() log_full(EFI_OUT_OF_RESOURCES, LOG_ERR, "Out of memory.") /* Debugging helper — please keep this around, even if not used */ #define log_hexdump(prefix, data, size) \ diff --git a/src/boot/random-seed.c b/src/boot/random-seed.c index 59ed782060..30e74af214 100644 --- a/src/boot/random-seed.c +++ b/src/boot/random-seed.c @@ -211,10 +211,10 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) { size = info->FileSize; if (size < RANDOM_MAX_SIZE_MIN) - return log_error("Random seed file is too short."); + return log_error_status(EFI_INVALID_PARAMETER, "Random seed file is too short."); if (size > RANDOM_MAX_SIZE_MAX) - return log_error("Random seed file is too large."); + return log_error_status(EFI_INVALID_PARAMETER, "Random seed file is too large."); seed = xmalloc(size); rsize = size; From 4f35d74998f3ff858c1912e779a57e07b0664505 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sun, 24 Aug 2025 22:21:36 +0200 Subject: [PATCH 2/4] sd-boot: allow setting the maximum log level --- src/boot/boot.c | 2 ++ src/boot/efi-log.c | 43 +++++++++++++++++++++++++++++++++++++ src/boot/efi-log.h | 7 ++++++ src/boot/efi-string-table.h | 19 ++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/src/boot/boot.c b/src/boot/boot.c index eabfd6b6b2..3dd91f4396 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -361,6 +361,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) { if (config->console_mode_efivar != CONSOLE_MODE_KEEP) printf(" console-mode (EFI var): %" PRIi64 "\n", config->console_mode_efivar); + printf(" log-level: %s\n", log_level_to_string(log_get_max_level())); + if (!ps_continue()) return; diff --git a/src/boot/efi-log.c b/src/boot/efi-log.c index 5b831608f4..5bb2c0e490 100644 --- a/src/boot/efi-log.c +++ b/src/boot/efi-log.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "efi-log.h" +#include "efi-string-table.h" #include "proto/rng.h" #include "util.h" static unsigned log_count = 0; +static LogLevel log_max_level = LOG_INFO; static const uint8_t log_level_color[_LOG_MAX] = { [LOG_EMERG] = EFI_LIGHTRED, @@ -17,6 +19,44 @@ static const uint8_t log_level_color[_LOG_MAX] = { [LOG_DEBUG] = EFI_LIGHTGRAY, }; +static const char *const log_level_table[_LOG_MAX] = { + [LOG_EMERG] = "emerg", + [LOG_ALERT] = "alert", + [LOG_CRIT] = "crit", + [LOG_ERR] = "err", + [LOG_WARNING] = "warning", + [LOG_NOTICE] = "notice", + [LOG_INFO] = "info", + [LOG_DEBUG] = "debug", +}; + +DEFINE_STRING_TABLE_LOOKUP(log_level, LogLevel); + +LogLevel log_get_max_level(void) { + return log_max_level; +} + +int log_set_max_level(LogLevel level) { + assert(level >= 0 && level < _LOG_MAX); + + int old = log_max_level; + log_max_level = level; + return old; +} + +int log_set_max_level_from_string(const char *e) { + int r; + + assert(e); + + r = log_level_from_string(e); + if (r < 0) + return r; + + log_set_max_level(r); + return 0; +} + void freeze(void) { for (;;) BS->Stall(60 * 1000 * 1000); @@ -46,6 +86,9 @@ EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *forma assert(format); assert(log_level >= 0 && log_level < _LOG_MAX); + if (log_level > log_max_level) + return status; + int32_t attr = ST->ConOut->Mode->Attribute; if (ST->ConOut->Mode->CursorColumn > 0) diff --git a/src/boot/efi-log.h b/src/boot/efi-log.h index 9a3c784d6f..418b9f90be 100644 --- a/src/boot/efi-log.h +++ b/src/boot/efi-log.h @@ -34,6 +34,13 @@ typedef enum LogLevel { _LOG_INVALID = -1, } LogLevel; +LogLevel log_level_from_string(const char *s) _pure_; +const char* log_level_to_string(LogLevel l) _const_; + +LogLevel log_get_max_level(void) _pure_; +int log_set_max_level(LogLevel level); +int log_set_max_level_from_string(const char *e); + _noreturn_ void freeze(void); void log_wait(void); _gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *format, ...); diff --git a/src/boot/efi-string-table.h b/src/boot/efi-string-table.h index 1053e75269..666d849e0a 100644 --- a/src/boot/efi-string-table.h +++ b/src/boot/efi-string-table.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "efi-string.h" #include "macro-fundamental.h" #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ @@ -9,5 +10,23 @@ return name##_table[i]; \ } +#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope) \ + scope type name##_from_string(const char *s) { \ + if (!s) \ + return (type) -1; \ + for (size_t i = 0; i < ELEMENTSOF(name##_table); ++i) \ + if (streq8(name##_table[i], s)) \ + return (type) i; \ + return (type) -1; \ + } + +#define _DEFINE_STRING_TABLE_LOOKUP(name, type, scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name, type, scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope) + +#define DEFINE_STRING_TABLE_LOOKUP(name, type) _DEFINE_STRING_TABLE_LOOKUP(name, type,) #define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,) +#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name, type) _DEFINE_STRING_TABLE_LOOKUP(name, type, static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name, type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, static) From 0ce83b8a578f3076d9ecff6b1d59613ff4afa3b5 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Sun, 24 Aug 2025 22:22:59 +0200 Subject: [PATCH 3/4] sd-boot: allow setting the log level through SMBIOS 11 Allow configuring the log level used by sd-boot by setting `io.systemd.boot.loglevel=` as SMBIOS type 11 string. `info` is used if unset. --- man/smbios-type-11.xml | 12 +++++++++++- man/systemd-boot.xml | 10 ++++++++++ src/boot/boot.c | 3 +++ src/boot/efi-log.c | 17 +++++++++++++++++ src/boot/efi-log.h | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/man/smbios-type-11.xml b/man/smbios-type-11.xml index b576677d27..70f66fe0f6 100644 --- a/man/smbios-type-11.xml +++ b/man/smbios-type-11.xml @@ -80,10 +80,20 @@ This allows inserting additional entries into the systemd-boot menu. For details see - systemd-boot7 + systemd-boot7. + + + io.systemd.boot.loglevel=LEVEL + + This allows configuration of the log level, and is read by systemd-boot. + For details see + systemd-boot7. + + + diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index b01299143c..94eaae2d6b 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -640,6 +640,16 @@ uki-url http://example.com/somedir/fooos.efi + + + io.systemd.boot.loglevel + If set, the value of this string is used as log level. Valid values (from most to + least critical) are emerg, alert, crit, + err, warning, notice, info, + and debug. + + + diff --git a/src/boot/boot.c b/src/boot/boot.c index 3dd91f4396..3ac59866aa 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -2991,6 +2991,9 @@ static EFI_STATUS run(EFI_HANDLE image) { uint64_t init_usec; bool menu = false; + /* set loglevel early to simplify debugging before loader.conf is loaded */ + log_set_max_level_from_smbios(); + init_usec = time_usec(); err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image); diff --git a/src/boot/efi-log.c b/src/boot/efi-log.c index 5bb2c0e490..3cecc7be06 100644 --- a/src/boot/efi-log.c +++ b/src/boot/efi-log.c @@ -3,7 +3,9 @@ #include "efi-log.h" #include "efi-string-table.h" #include "proto/rng.h" +#include "smbios.h" #include "util.h" +#include "vmm.h" static unsigned log_count = 0; static LogLevel log_max_level = LOG_INFO; @@ -57,6 +59,21 @@ int log_set_max_level_from_string(const char *e) { return 0; } +void log_set_max_level_from_smbios(void) { + int r; + + if (is_confidential_vm()) + return; /* Don't consume SMBIOS in Confidential Computing contexts */ + + const char *level_str = smbios_find_oem_string("io.systemd.boot.loglevel=", /* after= */ NULL); + if (!level_str) + return; + + r = log_set_max_level_from_string(level_str); + if (r < 0) + log_warning("Failed to parse log level '%s', ignoring.", level_str); +} + void freeze(void) { for (;;) BS->Stall(60 * 1000 * 1000); diff --git a/src/boot/efi-log.h b/src/boot/efi-log.h index 418b9f90be..459838782b 100644 --- a/src/boot/efi-log.h +++ b/src/boot/efi-log.h @@ -40,6 +40,7 @@ const char* log_level_to_string(LogLevel l) _const_; LogLevel log_get_max_level(void) _pure_; int log_set_max_level(LogLevel level); int log_set_max_level_from_string(const char *e); +void log_set_max_level_from_smbios(void); _noreturn_ void freeze(void); void log_wait(void); From b3f3ce28f85509b688b30ae4714447780a48746f Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Tue, 26 Aug 2025 20:13:00 +0200 Subject: [PATCH 4/4] sd-boot: allow setting the log level through loader.conf Allow configuring the log level used by sd-boot by setting `log-level=` in loader.conf. `info` is used by default. --- man/loader.conf.xml | 22 ++++++++++++++++++++++ src/boot/boot.c | 3 +++ 2 files changed, 25 insertions(+) diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 1a90e15ffa..0d12b1e874 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -480,6 +480,28 @@ sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.e + + + log-level + + Controls the log level used by + systemd-boot7. + + + Valid values are emerg, alert, crit, + err, warning, notice, + info, and debug. + + If unspecified, info will be used, unless one has already been configured + via an SMBIOS Type 11 string, see + smbios-type-117. + + + Note that the configured level will only be used after loader.conf has + been parsed, so log messages generated before that point may be unaffected by this setting. + + + diff --git a/src/boot/boot.c b/src/boot/boot.c index 3ac59866aa..abf7693a36 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -1113,6 +1113,9 @@ static void config_defaults_load_from_file(Config *config, char *content) { } config->console_mode = u; } + } else if (streq8(key, "log-level")) { + if (log_set_max_level_from_string(value) < 0) + log_error("Error parsing 'log-level' config option, ignoring: %s", value); } }