sd-boot: allow configuration of log levels (#38701)

This allows for more liberal usage of logging functionality as messages
will no longer always show up on screen, regardless of urgency. The log
level to use can be configured through an SMBIOS type 11 string
(`io.systemd.boot.loglevel=`) or by using the `log-level` option in
loader.conf. Valid values are debug, info, notice, warning, err, crit,
alert, and emerg. By default, info will be used.
This commit is contained in:
Yu Watanabe
2025-09-18 12:06:48 +09:00
committed by GitHub
9 changed files with 188 additions and 15 deletions
+22
View File
@@ -480,6 +480,28 @@ sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.e
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term>log-level</term>
<listitem><para>Controls the log level used by
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
<para>Valid values are <literal>emerg</literal>, <literal>alert</literal>, <literal>crit</literal>,
<literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>,
<literal>info</literal>, and <literal>debug</literal>.</para>
<para>If unspecified, <literal>info</literal> will be used, unless one has already been configured
via an SMBIOS Type 11 string, see
<citerefentry><refentrytitle>smbios-type-11</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
<para>Note that the configured level will only be used after <filename>loader.conf</filename> has
been parsed, so log messages generated before that point may be unaffected by this setting.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
+11 -1
View File
@@ -80,10 +80,20 @@
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
menu. For details see
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></para>
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>io.systemd.boot.loglevel=</varname><replaceable>LEVEL</replaceable></term>
<listitem><para>This allows configuration of the log level, and is read by <command>systemd-boot</command>.
For details see
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
+10
View File
@@ -640,6 +640,16 @@ uki-url http://example.com/somedir/fooos.efi</programlisting>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>io.systemd.boot.loglevel</varname></term>
<listitem><para>If set, the value of this string is used as log level. Valid values (from most to
least critical) are <literal>emerg</literal>, <literal>alert</literal>, <literal>crit</literal>,
<literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>, <literal>info</literal>,
and <literal>debug</literal>.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
+8
View File
@@ -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);
+1 -1
View File
@@ -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) {
+74 -2
View File
@@ -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);
+41 -9
View File
@@ -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) \
+19
View File
@@ -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)
+2 -2
View File
@@ -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;