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/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 eabfd6b6b2..abf7693a36 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; @@ -1111,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); } } @@ -2989,6 +2994,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/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..3cecc7be06 100644 --- a/src/boot/efi-log.c +++ b/src/boot/efi-log.c @@ -1,10 +1,78 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #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; + +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, +}; + +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 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 (;;) @@ -31,14 +99,18 @@ 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); + + if (log_level > log_max_level) + return status; 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..459838782b 100644 --- a/src/boot/efi-log.h +++ b/src/boot/efi-log.h @@ -21,17 +21,49 @@ __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; + +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); +void log_set_max_level_from_smbios(void); + _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/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) 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;