cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

memconsole-coreboot.c (2980B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * memconsole-coreboot.c
      4 *
      5 * Memory based BIOS console accessed through coreboot table.
      6 *
      7 * Copyright 2017 Google Inc.
      8 */
      9
     10#include <linux/device.h>
     11#include <linux/io.h>
     12#include <linux/kernel.h>
     13#include <linux/module.h>
     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. */
     21struct cbmem_cons {
     22	u32 size_dont_access_after_boot;
     23	u32 cursor;
     24	u8  body[];
     25} __packed;
     26
     27#define CURSOR_MASK ((1 << 28) - 1)
     28#define OVERFLOW (1 << 31)
     29
     30static struct cbmem_cons *cbmem_console;
     31static u32 cbmem_console_size;
     32
     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 */
     40static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count)
     41{
     42	u32 cursor = cbmem_console->cursor & CURSOR_MASK;
     43	u32 flags = cbmem_console->cursor & ~CURSOR_MASK;
     44	u32 size = cbmem_console_size;
     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;
     67}
     68
     69static int memconsole_probe(struct coreboot_device *dev)
     70{
     71	struct cbmem_cons *tmp_cbmc;
     72
     73	tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr,
     74			    sizeof(*tmp_cbmc), MEMREMAP_WB);
     75
     76	if (!tmp_cbmc)
     77		return -ENOMEM;
     78
     79	/* Read size only once to prevent overrun attack through /dev/mem. */
     80	cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;
     81	cbmem_console = devm_memremap(&dev->dev, dev->cbmem_ref.cbmem_addr,
     82				 cbmem_console_size + sizeof(*cbmem_console),
     83				 MEMREMAP_WB);
     84	memunmap(tmp_cbmc);
     85
     86	if (IS_ERR(cbmem_console))
     87		return PTR_ERR(cbmem_console);
     88
     89	memconsole_setup(memconsole_coreboot_read);
     90
     91	return memconsole_sysfs_init();
     92}
     93
     94static void memconsole_remove(struct coreboot_device *dev)
     95{
     96	memconsole_exit();
     97}
     98
     99static struct coreboot_driver memconsole_driver = {
    100	.probe = memconsole_probe,
    101	.remove = memconsole_remove,
    102	.drv = {
    103		.name = "memconsole",
    104	},
    105	.tag = CB_TAG_CBMEM_CONSOLE,
    106};
    107module_coreboot_driver(memconsole_driver);
    108
    109MODULE_AUTHOR("Google, Inc.");
    110MODULE_LICENSE("GPL");