lib/bli: Implement `LoaderTimeInitUSec` and `LoaderTimeExecUSec`
These are variables used by `systemd-analyze` and is useful for boot performance metrics.
diff --git a/common/entry.s2.c b/common/entry.s2.c
index 5f08c83a..f72e3c37 100644
--- a/common/entry.s2.c
+++ b/common/entry.s2.c
@@ -85,6 +85,8 @@ noreturn void entry(uint8_t boot_drive, int boot_from) {
panic(false, "Could not enable A20 line");
}
+ uint64_t usec_at_entry = rdtsc_usec();
+
init_e820();
init_memmap();
@@ -122,6 +124,7 @@ noreturn void entry(uint8_t boot_drive, int boot_from) {
term_fallback();
+ usec_at_bootloader_entry = usec_at_entry;
stage3_common();
}
diff --git a/common/entry.s3.c b/common/entry.s3.c
index 14bde164..cb7bb783 100644
--- a/common/entry.s3.c
+++ b/common/entry.s3.c
@@ -8,6 +8,7 @@
#include <lib/part.h>
#include <lib/config.h>
#include <lib/trace.h>
+#include <lib/bli.h>
#include <sys/e820.h>
#include <sys/a20.h>
#include <sys/idt.h>
@@ -36,6 +37,8 @@ noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
gRT = SystemTable->RuntimeServices;
efi_image_handle = ImageHandle;
+ usec_at_bootloader_entry = rdtsc_usec();
+
EFI_STATUS status;
const char *deferred_error = NULL;
@@ -179,5 +182,9 @@ noreturn void stage3_common(void) {
term_notready();
+#if defined (UEFI)
+ init_bli();
+#endif
+
menu(true);
}
diff --git a/common/lib/bli.c b/common/lib/bli.c
index ba0d70a3..358505cf 100644
--- a/common/lib/bli.c
+++ b/common/lib/bli.c
@@ -1,6 +1,9 @@
#if defined (UEFI)
+#include <stdint.h>
+#include <stddef.h>
#include <config.h>
+#include <sys/cpu.h>
#include <efi.h>
#include <lib/bli.h>
#include <lib/guid.h>
@@ -10,7 +13,51 @@
static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } };
+// The buffer must be at least 21 bytes long
+void uint64_to_decwstr(uint64_t value, wchar_t *buf) {
+ wchar_t tmp[21];
+ size_t i = 0;
+
+ if (buf == NULL) {
+ return;
+ }
+
+ if (value == 0) {
+ buf[0] = '0';
+ buf[1] = '\0';
+ return;
+ }
+
+ // Convert digits in reverse order
+ while (value > 0) {
+ tmp[i++] = '0' + (value % 10);
+ value /= 10;
+ }
+
+ // Reverse the string into the buffer
+ for (size_t j = 0; j < i; j++) {
+ buf[j] = tmp[i - j - 1];
+ }
+ buf[i] = '\0';
+}
+
+void bli_set_loader_time(wchar_t *variable, uint64_t time) {
+ if (time == 0)
+ return;
+
+ wchar_t time_wstr[21];
+ uint64_to_decwstr(time, time_wstr);
+
+ gRT->SetVariable(variable,
+ &bli_vendor_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(time_wstr),
+ time_wstr);
+}
+
void init_bli(void) {
+ bli_set_loader_time(L"LoaderTimeInitUSec", usec_at_bootloader_entry);
+
gRT->SetVariable(L"LoaderInfo",
&bli_vendor_guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
@@ -33,4 +80,8 @@ void init_bli(void) {
part_uuid);
}
+void bli_on_boot(void) {
+ bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec());
+}
+
#endif
diff --git a/common/lib/bli.h b/common/lib/bli.h
index 135dba5c..2e48041d 100644
--- a/common/lib/bli.h
+++ b/common/lib/bli.h
@@ -4,6 +4,7 @@
#if defined (UEFI)
void init_bli(void);
+void bli_on_boot(void);
#endif
diff --git a/common/lib/misc.c b/common/lib/misc.c
index 76f95ba6..1d91f731 100644
--- a/common/lib/misc.c
+++ b/common/lib/misc.c
@@ -8,6 +8,7 @@
#include <lib/real.h>
#include <lib/config.h>
#include <lib/uri.h>
+#include <lib/bli.h>
#include <fs/file.h>
#include <mm/pmm.h>
#include <libfdt/libfdt.h>
@@ -25,6 +26,8 @@ UINT32 efi_desc_ver = 0;
bool editor_enabled = true;
bool help_hidden = false;
+uint64_t usec_at_bootloader_entry;
+
#if defined (UEFI)
bool is_efi_serial_present(void) {
EFI_STATUS status;
@@ -283,6 +286,8 @@ bool efi_exit_boot_services(void) {
goto fail;
}
+ bli_on_boot();
+
const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size;
size_t retries = 0;
diff --git a/common/lib/misc.h b/common/lib/misc.h
index 5ebb124a..c92c3aa4 100644
--- a/common/lib/misc.h
+++ b/common/lib/misc.h
@@ -40,6 +40,8 @@ extern bool stage3_loaded;
extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic;
+extern uint64_t usec_at_bootloader_entry;
+
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf);
void get_absolute_path(char *path_ptr, const char *path, const char *pwd);
diff --git a/common/linker_bios.ld.in b/common/linker_bios.ld.in
index 17b46b67..4c90b986 100644
--- a/common/linker_bios.ld.in
+++ b/common/linker_bios.ld.in
@@ -42,6 +42,7 @@ SECTIONS
term_notready = .;
terms = .;
terms_i = .;
+ usec_at_bootloader_entry = .;
serial_out = .;
stage3_addr = .;
#else
diff --git a/common/menu.c b/common/menu.c
index bf5cbd71..14dc78a2 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -12,7 +12,6 @@
#include <lib/gterm.h>
#include <lib/getchar.h>
#include <lib/uri.h>
-#include <lib/bli.h>
#include <mm/pmm.h>
#include <drivers/vbe.h>
#include <drivers/vga_textmode.h>
@@ -1187,9 +1186,6 @@ noreturn void boot(char *config) {
#if defined (__riscv)
init_riscv(config);
#endif
-#if defined (UEFI)
- init_bli();
-#endif
char *cmdline = config_get_value(config, 0, "KERNEL_CMDLINE");
if (!cmdline) {
diff --git a/common/protos/chainload.c b/common/protos/chainload.c
index acf96f58..0629298d 100644
--- a/common/protos/chainload.c
+++ b/common/protos/chainload.c
@@ -12,6 +12,7 @@
#include <lib/print.h>
#include <lib/libc.h>
#include <sys/idt.h>
+#include <lib/bli.h>
#include <drivers/vga_textmode.h>
#include <mm/pmm.h>
#if defined (UEFI)
@@ -361,6 +362,8 @@ noreturn void chainload(char *config, char *cmdline) {
new_handle_loaded_image->LoadOptionsSize = cmdline_len * sizeof(CHAR16);
new_handle_loaded_image->LoadOptions = new_cmdline;
+ bli_on_boot();
+
UINTN exit_data_size = 0;
CHAR16 *exit_data = NULL;
EFI_STATUS exit_status = gBS->StartImage(new_handle, &exit_data_size, &exit_data);
diff --git a/common/sys/cpu.h b/common/sys/cpu.h
index 0ced9511..b3a79805 100644
--- a/common/sys/cpu.h
+++ b/common/sys/cpu.h
@@ -394,6 +394,12 @@ static inline uint64_t tsc_freq(void) {
#endif
}
+static inline uint64_t rdtsc_usec(void) {
+ uint64_t exec_ticks = rdtsc();
+ uint64_t freq = tsc_freq();
+ return freq != 0 ? 1000000ULL * exec_ticks / freq : 0;
+}
+
static inline void delay(uint64_t cycles) {
uint64_t next_stop = rdtsc() + cycles;
