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