xref: /linux/drivers/firmware/efi/ovmf-debug-log.c (revision 402e262d77cd8b075a56809e30e6903ef648ad1a)
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 
ovmf_log_read(struct file * filp,struct kobject * kobj,const struct bin_attribute * attr,char * buf,loff_t offset,size_t count)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 
ovmf_log_probe(unsigned long ovmf_debug_log_table)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 }
112