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