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;