Merge tag 'efi-next-for-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI updates from Ard Biesheuvel:

- Expose the OVMF firmware debug log via sysfs

- Lower the default log level for the EFI stub to avoid corrupting any
splash screens with unimportant diagnostic output

* tag 'efi-next-for-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
efi: add API doc entry for ovmf_debug_log
efistub: Lower default log level
efi: add ovmf debug log driver

+141 -2
+7
Documentation/ABI/testing/sysfs-firmware-efi
··· 36 36 Table version 2 on Dell EMC PowerEdge systems in binary format 37 37 Users: It is used by Dell EMC OpenManage Server Administrator tool to 38 38 populate BIOS setup page. 39 + 40 + What: /sys/firmware/efi/ovmf_debug_log 41 + Date: July 2025 42 + Contact: Gerd Hoffmann <kraxel@redhat.com>, linux-efi@vger.kernel.org 43 + Description: Displays the content of the OVMF debug log buffer. The file is 44 + only present in case the firmware supports logging to a memory 45 + buffer.
+8
drivers/firmware/efi/Kconfig
··· 263 263 virt/coco/efi_secret module to access the secrets, which in turn 264 264 allows userspace programs to access the injected secrets. 265 265 266 + config OVMF_DEBUG_LOG 267 + bool "Expose OVMF firmware debug log via sysfs" 268 + depends on EFI 269 + help 270 + Recent OVMF versions (edk2-stable202508 + newer) can write 271 + their debug log to a memory buffer. This driver exposes the 272 + log content via sysfs (/sys/firmware/efi/ovmf_debug_log). 273 + 266 274 config UNACCEPTED_MEMORY 267 275 bool 268 276 depends on EFI_STUB
+1
drivers/firmware/efi/Makefile
··· 29 29 obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o 30 30 obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o 31 31 obj-$(CONFIG_LOAD_UEFI_KEYS) += mokvar-table.o 32 + obj-$(CONFIG_OVMF_DEBUG_LOG) += ovmf-debug-log.o 32 33 33 34 obj-$(CONFIG_SYSFB) += sysfb_efi.o 34 35
+8
drivers/firmware/efi/efi.c
··· 45 45 .esrt = EFI_INVALID_TABLE_ADDR, 46 46 .tpm_log = EFI_INVALID_TABLE_ADDR, 47 47 .tpm_final_log = EFI_INVALID_TABLE_ADDR, 48 + .ovmf_debug_log = EFI_INVALID_TABLE_ADDR, 48 49 #ifdef CONFIG_LOAD_UEFI_KEYS 49 50 .mokvar_table = EFI_INVALID_TABLE_ADDR, 50 51 #endif ··· 474 473 platform_device_register_simple("efi_secret", 0, NULL, 0); 475 474 #endif 476 475 476 + if (IS_ENABLED(CONFIG_OVMF_DEBUG_LOG) && 477 + efi.ovmf_debug_log != EFI_INVALID_TABLE_ADDR) 478 + ovmf_log_probe(efi.ovmf_debug_log); 479 + 477 480 return 0; 478 481 479 482 err_remove_group: ··· 622 617 {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, 623 618 {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, 624 619 {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, 620 + #ifdef CONFIG_OVMF_DEBUG_LOG 621 + {OVMF_MEMORY_LOG_TABLE_GUID, &efi.ovmf_debug_log, "OvmfDebugLog" }, 622 + #endif 625 623 #ifdef CONFIG_EFI_RCI2_TABLE 626 624 {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, 627 625 #endif
+2 -2
drivers/firmware/efi/libstub/printk.c
··· 5 5 #include <linux/ctype.h> 6 6 #include <linux/efi.h> 7 7 #include <linux/kernel.h> 8 - #include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ 8 + #include <linux/kern_levels.h> 9 9 #include <asm/efi.h> 10 10 #include <asm/setup.h> 11 11 12 12 #include "efistub.h" 13 13 14 - int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; 14 + int efi_loglevel = LOGLEVEL_NOTICE; 15 15 16 16 /** 17 17 * efi_char16_puts() - Write a UCS-2 encoded string to the console
+111
drivers/firmware/efi/ovmf-debug-log.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/efi.h> 4 + #include <linux/init.h> 5 + #include <linux/io.h> 6 + #include <linux/kernel.h> 7 + #include <linux/kobject.h> 8 + #include <linux/module.h> 9 + #include <linux/platform_device.h> 10 + #include <linux/sysfs.h> 11 + 12 + #define OVMF_DEBUG_LOG_MAGIC1 0x3167646d666d766f // "ovmfmdg1" 13 + #define OVMF_DEBUG_LOG_MAGIC2 0x3267646d666d766f // "ovmfmdg2" 14 + 15 + struct ovmf_debug_log_header { 16 + u64 magic1; 17 + u64 magic2; 18 + u64 hdr_size; 19 + u64 log_size; 20 + u64 lock; // edk2 spinlock 21 + u64 head_off; 22 + u64 tail_off; 23 + u64 truncated; 24 + u8 fw_version[128]; 25 + }; 26 + 27 + static struct ovmf_debug_log_header *hdr; 28 + static u8 *logbuf; 29 + static u64 logbufsize; 30 + 31 + static ssize_t ovmf_log_read(struct file *filp, struct kobject *kobj, 32 + const struct bin_attribute *attr, char *buf, 33 + loff_t offset, size_t count) 34 + { 35 + u64 start, end; 36 + 37 + start = hdr->head_off + offset; 38 + if (hdr->head_off > hdr->tail_off && start >= hdr->log_size) 39 + start -= hdr->log_size; 40 + 41 + end = start + count; 42 + if (start > hdr->tail_off) { 43 + if (end > hdr->log_size) 44 + end = hdr->log_size; 45 + } else { 46 + if (end > hdr->tail_off) 47 + end = hdr->tail_off; 48 + } 49 + 50 + if (start > logbufsize || end > logbufsize) 51 + return 0; 52 + if (start >= end) 53 + return 0; 54 + 55 + memcpy(buf, logbuf + start, end - start); 56 + return end - start; 57 + } 58 + 59 + static struct bin_attribute ovmf_log_bin_attr = { 60 + .attr = { 61 + .name = "ovmf_debug_log", 62 + .mode = 0444, 63 + }, 64 + .read = ovmf_log_read, 65 + }; 66 + 67 + int __init ovmf_log_probe(unsigned long ovmf_debug_log_table) 68 + { 69 + int ret = -EINVAL; 70 + u64 size; 71 + 72 + /* map + verify header */ 73 + hdr = memremap(ovmf_debug_log_table, sizeof(*hdr), MEMREMAP_WB); 74 + if (!hdr) { 75 + pr_err("OVMF debug log: header map failed\n"); 76 + return -EINVAL; 77 + } 78 + 79 + if (hdr->magic1 != OVMF_DEBUG_LOG_MAGIC1 || 80 + hdr->magic2 != OVMF_DEBUG_LOG_MAGIC2) { 81 + printk(KERN_ERR "OVMF debug log: magic mismatch\n"); 82 + goto err_unmap; 83 + } 84 + 85 + size = hdr->hdr_size + hdr->log_size; 86 + pr_info("OVMF debug log: firmware version: \"%s\"\n", hdr->fw_version); 87 + pr_info("OVMF debug log: buffer size: %lluk\n", size / 1024); 88 + 89 + /* map complete log buffer */ 90 + memunmap(hdr); 91 + hdr = memremap(ovmf_debug_log_table, size, MEMREMAP_WB); 92 + if (!hdr) { 93 + pr_err("OVMF debug log: buffer map failed\n"); 94 + return -EINVAL; 95 + } 96 + logbuf = (void *)hdr + hdr->hdr_size; 97 + logbufsize = hdr->log_size; 98 + 99 + ovmf_log_bin_attr.size = size; 100 + ret = sysfs_create_bin_file(efi_kobj, &ovmf_log_bin_attr); 101 + if (ret != 0) { 102 + pr_err("OVMF debug log: sysfs register failed\n"); 103 + goto err_unmap; 104 + } 105 + 106 + return 0; 107 + 108 + err_unmap: 109 + memunmap(hdr); 110 + return ret; 111 + }
+4
include/linux/efi.h
··· 439 439 440 440 /* OVMF protocol GUIDs */ 441 441 #define OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL_GUID EFI_GUID(0xc5a010fe, 0x38a7, 0x4531, 0x8a, 0x4a, 0x05, 0x00, 0xd2, 0xfd, 0x16, 0x49) 442 + #define OVMF_MEMORY_LOG_TABLE_GUID EFI_GUID(0x95305139, 0xb20f, 0x4723, 0x84, 0x25, 0x62, 0x7c, 0x88, 0x8f, 0xf1, 0x21) 442 443 443 444 typedef struct { 444 445 efi_guid_t guid; ··· 643 642 unsigned long esrt; /* ESRT table */ 644 643 unsigned long tpm_log; /* TPM2 Event Log table */ 645 644 unsigned long tpm_final_log; /* TPM2 Final Events Log table */ 645 + unsigned long ovmf_debug_log; 646 646 unsigned long mokvar_table; /* MOK variable config table */ 647 647 unsigned long coco_secret; /* Confidential computing secret table */ 648 648 unsigned long unaccepted; /* Unaccepted memory table */ ··· 1345 1343 } 1346 1344 1347 1345 umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n); 1346 + 1347 + int ovmf_log_probe(unsigned long ovmf_debug_log_table); 1348 1348 1349 1349 /* 1350 1350 * efivar ops event type