Path: blob/master/drivers/firmware/google/memconsole-coreboot.c
26428 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* memconsole-coreboot.c3*4* Memory based BIOS console accessed through coreboot table.5*6* Copyright 2017 Google Inc.7*/89#include <linux/device.h>10#include <linux/io.h>11#include <linux/kernel.h>12#include <linux/module.h>1314#include "memconsole.h"15#include "coreboot_table.h"1617#define CB_TAG_CBMEM_CONSOLE 0x171819/* CBMEM firmware console log descriptor. */20struct cbmem_cons {21u32 size_dont_access_after_boot;22u32 cursor;23u8 body[];24} __packed;2526#define CURSOR_MASK ((1 << 28) - 1)27#define OVERFLOW (1 << 31)2829static struct cbmem_cons *cbmem_console;30static u32 cbmem_console_size;3132/*33* The cbmem_console structure is read again on every access because it may34* change at any time if runtime firmware logs new messages. This may rarely35* lead to race conditions where the firmware overwrites the beginning of the36* ring buffer with more lines after we have already read |cursor|. It should be37* rare and harmless enough that we don't spend extra effort working around it.38*/39static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count)40{41u32 cursor = cbmem_console->cursor & CURSOR_MASK;42u32 flags = cbmem_console->cursor & ~CURSOR_MASK;43u32 size = cbmem_console_size;44struct seg { /* describes ring buffer segments in logical order */45u32 phys; /* physical offset from start of mem buffer */46u32 len; /* length of segment */47} seg[2] = { {0}, {0} };48size_t done = 0;49int i;5051if (flags & OVERFLOW) {52if (cursor > size) /* Shouldn't really happen, but... */53cursor = 0;54seg[0] = (struct seg){.phys = cursor, .len = size - cursor};55seg[1] = (struct seg){.phys = 0, .len = cursor};56} else {57seg[0] = (struct seg){.phys = 0, .len = min(cursor, size)};58}5960for (i = 0; i < ARRAY_SIZE(seg) && count > done; i++) {61done += memory_read_from_buffer(buf + done, count - done, &pos,62cbmem_console->body + seg[i].phys, seg[i].len);63pos -= seg[i].len;64}65return done;66}6768static int memconsole_probe(struct coreboot_device *dev)69{70struct cbmem_cons *tmp_cbmc;7172tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr,73sizeof(*tmp_cbmc), MEMREMAP_WB);7475if (!tmp_cbmc)76return -ENOMEM;7778/* Read size only once to prevent overrun attack through /dev/mem. */79cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;80cbmem_console = devm_memremap(&dev->dev, dev->cbmem_ref.cbmem_addr,81cbmem_console_size + sizeof(*cbmem_console),82MEMREMAP_WB);83memunmap(tmp_cbmc);8485if (IS_ERR(cbmem_console))86return PTR_ERR(cbmem_console);8788memconsole_setup(memconsole_coreboot_read);8990return memconsole_sysfs_init();91}9293static void memconsole_remove(struct coreboot_device *dev)94{95memconsole_exit();96}9798static const struct coreboot_device_id memconsole_ids[] = {99{ .tag = CB_TAG_CBMEM_CONSOLE },100{ /* sentinel */ }101};102MODULE_DEVICE_TABLE(coreboot, memconsole_ids);103104static struct coreboot_driver memconsole_driver = {105.probe = memconsole_probe,106.remove = memconsole_remove,107.drv = {108.name = "memconsole",109},110.id_table = memconsole_ids,111};112module_coreboot_driver(memconsole_driver);113114MODULE_AUTHOR("Google, Inc.");115MODULE_DESCRIPTION("Memory based BIOS console accessed through coreboot table");116MODULE_LICENSE("GPL");117118119