]>
Commit | Line | Data |
---|---|---|
d9523678 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
d384d6f4 TE |
2 | /* |
3 | * memconsole-coreboot.c | |
4 | * | |
5 | * Memory based BIOS console accessed through coreboot table. | |
6 | * | |
7 | * Copyright 2017 Google Inc. | |
d384d6f4 TE |
8 | */ |
9 | ||
294b2a90 | 10 | #include <linux/device.h> |
381e9760 | 11 | #include <linux/io.h> |
d384d6f4 TE |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> | |
d384d6f4 TE |
14 | |
15 | #include "memconsole.h" | |
16 | #include "coreboot_table.h" | |
17 | ||
18 | #define CB_TAG_CBMEM_CONSOLE 0x17 | |
19 | ||
20 | /* CBMEM firmware console log descriptor. */ | |
21 | struct cbmem_cons { | |
40fbb238 | 22 | u32 size_dont_access_after_boot; |
a5061d02 | 23 | u32 cursor; |
ea9ee997 | 24 | u8 body[]; |
d384d6f4 TE |
25 | } __packed; |
26 | ||
a5061d02 JW |
27 | #define CURSOR_MASK ((1 << 28) - 1) |
28 | #define OVERFLOW (1 << 31) | |
29 | ||
e07f1009 | 30 | static struct cbmem_cons *cbmem_console; |
40fbb238 | 31 | static u32 cbmem_console_size; |
d384d6f4 | 32 | |
a5061d02 JW |
33 | /* |
34 | * The cbmem_console structure is read again on every access because it may | |
35 | * change at any time if runtime firmware logs new messages. This may rarely | |
36 | * lead to race conditions where the firmware overwrites the beginning of the | |
37 | * ring buffer with more lines after we have already read |cursor|. It should be | |
38 | * rare and harmless enough that we don't spend extra effort working around it. | |
39 | */ | |
7918cfc4 JW |
40 | static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count) |
41 | { | |
a5061d02 JW |
42 | u32 cursor = cbmem_console->cursor & CURSOR_MASK; |
43 | u32 flags = cbmem_console->cursor & ~CURSOR_MASK; | |
40fbb238 | 44 | u32 size = cbmem_console_size; |
a5061d02 JW |
45 | struct seg { /* describes ring buffer segments in logical order */ |
46 | u32 phys; /* physical offset from start of mem buffer */ | |
47 | u32 len; /* length of segment */ | |
48 | } seg[2] = { {0}, {0} }; | |
49 | size_t done = 0; | |
50 | int i; | |
51 | ||
52 | if (flags & OVERFLOW) { | |
53 | if (cursor > size) /* Shouldn't really happen, but... */ | |
54 | cursor = 0; | |
55 | seg[0] = (struct seg){.phys = cursor, .len = size - cursor}; | |
56 | seg[1] = (struct seg){.phys = 0, .len = cursor}; | |
57 | } else { | |
58 | seg[0] = (struct seg){.phys = 0, .len = min(cursor, size)}; | |
59 | } | |
60 | ||
61 | for (i = 0; i < ARRAY_SIZE(seg) && count > done; i++) { | |
62 | done += memory_read_from_buffer(buf + done, count - done, &pos, | |
63 | cbmem_console->body + seg[i].phys, seg[i].len); | |
64 | pos -= seg[i].len; | |
65 | } | |
66 | return done; | |
7918cfc4 JW |
67 | } |
68 | ||
294b2a90 | 69 | static int memconsole_probe(struct coreboot_device *dev) |
d384d6f4 | 70 | { |
e07f1009 | 71 | struct cbmem_cons *tmp_cbmc; |
d384d6f4 | 72 | |
294b2a90 SH |
73 | tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr, |
74 | sizeof(*tmp_cbmc), MEMREMAP_WB); | |
d384d6f4 TE |
75 | |
76 | if (!tmp_cbmc) | |
77 | return -ENOMEM; | |
78 | ||
40fbb238 JW |
79 | /* Read size only once to prevent overrun attack through /dev/mem. */ |
80 | cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; | |
b0503584 | 81 | cbmem_console = devm_memremap(&dev->dev, dev->cbmem_ref.cbmem_addr, |
40fbb238 | 82 | cbmem_console_size + sizeof(*cbmem_console), |
d384d6f4 TE |
83 | MEMREMAP_WB); |
84 | memunmap(tmp_cbmc); | |
85 | ||
b0503584 SB |
86 | if (IS_ERR(cbmem_console)) |
87 | return PTR_ERR(cbmem_console); | |
d384d6f4 | 88 | |
7918cfc4 | 89 | memconsole_setup(memconsole_coreboot_read); |
d384d6f4 TE |
90 | |
91 | return memconsole_sysfs_init(); | |
92 | } | |
93 | ||
294b2a90 | 94 | static int memconsole_remove(struct coreboot_device *dev) |
d384d6f4 TE |
95 | { |
96 | memconsole_exit(); | |
97 | ||
d384d6f4 TE |
98 | return 0; |
99 | } | |
100 | ||
294b2a90 | 101 | static struct coreboot_driver memconsole_driver = { |
d384d6f4 TE |
102 | .probe = memconsole_probe, |
103 | .remove = memconsole_remove, | |
294b2a90 | 104 | .drv = { |
d384d6f4 TE |
105 | .name = "memconsole", |
106 | }, | |
294b2a90 | 107 | .tag = CB_TAG_CBMEM_CONSOLE, |
d384d6f4 | 108 | }; |
35463503 | 109 | module_coreboot_driver(memconsole_driver); |
d384d6f4 TE |
110 | |
111 | MODULE_AUTHOR("Google, Inc."); | |
112 | MODULE_LICENSE("GPL"); |