Commit 19d54020 authored by Jack Rosenthal's avatar Jack Rosenthal Committed by Greg Kroah-Hartman
Browse files

firmware: google: Implement cbmem in sysfs driver

The CBMEM area is a downward-growing memory region used by coreboot to
dynamically allocate tagged data structures ("CBMEM entries") that
remain resident during boot.

This implements a driver which exports access to the CBMEM entries
via sysfs under /sys/bus/coreboot/devices/cbmem-<id>.

This implementation is quite versatile.  Examples of how it could be
used are given below:

* Tools like util/cbmem from the coreboot tree could use this driver
  instead of finding CBMEM in /dev/mem directly.  Alternatively,
  firmware developers debugging an issue may find the sysfs interface
  more ergonomic than the cbmem tool and choose to use it directly.

* The crossystem tool, which exposes verified boot variables, can use
  this driver to read the vboot work buffer.

* Tools which read the BIOS SPI flash (e.g., flashrom) can find the
  flash layout in CBMEM directly, which is significantly faster than
  searching the flash directly.

Write access is provided to all CBMEM regions via
/sys/bus/coreboot/devices/cbmem-<id>/mem, as the existing cbmem
tooling updates this memory region, and envisioned use cases with
crossystem can benefit from updating memory regions.

Link: https://issuetracker.google.com/239604743


Cc: Stephen Boyd <swboyd@chromium.org>
Cc: Tzung-Bi Shih <tzungbi@kernel.org>
Reviewed-by: default avatarGuenter Roeck <groeck@chromium.org>
Reviewed-by: default avatarJulius Werner <jwerner@chromium.org>
Tested-by: default avatarJack Rosenthal <jrosenth@chromium.org>
Signed-off-by: default avatarJack Rosenthal <jrosenth@chromium.org>
Link: https://lore.kernel.org/r/20221104161528.531248-1-jrosenth@chromium.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d88bd098
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
What:		/sys/bus/coreboot
Date:		August 2022
Contact:	Jack Rosenthal <jrosenth@chromium.org>
Description:
		The coreboot bus provides a variety of virtual devices used to
		access data structures created by the Coreboot BIOS.

What:		/sys/bus/coreboot/devices/cbmem-<id>
Date:		August 2022
Contact:	Jack Rosenthal <jrosenth@chromium.org>
Description:
		CBMEM is a downwards-growing memory region created by Coreboot,
		and contains tagged data structures to be shared with payloads
		in the boot process and the OS.  Each CBMEM entry is given a
		directory in /sys/bus/coreboot/devices based on its id.
		A list of ids known to Coreboot can be found in the coreboot
		source tree at
		``src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h``.

What:		/sys/bus/coreboot/devices/cbmem-<id>/address
Date:		August 2022
Contact:	Jack Rosenthal <jrosenth@chromium.org>
Description:
		This is the pyhsical memory address that the CBMEM entry's data
		begins at, in hexadecimal (e.g., ``0x76ffe000``).

What:		/sys/bus/coreboot/devices/cbmem-<id>/size
Date:		August 2022
Contact:	Jack Rosenthal <jrosenth@chromium.org>
Description:
		This is the size of the CBMEM entry's data, in hexadecimal
		(e.g., ``0x1234``).

What:		/sys/bus/coreboot/devices/cbmem-<id>/mem
Date:		August 2022
Contact:	Jack Rosenthal <jrosenth@chromium.org>
Description:
		A file exposing read/write access to the entry's data.  Note
		that this file does not support mmap(), as coreboot
		does not guarantee that the data will be page-aligned.

		The mode of this file is 0600.  While there shouldn't be
		anything security-sensitive contained in CBMEM, read access
		requires root privileges given this is exposing a small subset
		of physical memory.
+15 −0
Original line number Diff line number Diff line
@@ -19,6 +19,21 @@ config GOOGLE_SMI
	  driver provides an interface for reading and writing NVRAM
	  variables.

config GOOGLE_CBMEM
	tristate "CBMEM entries in sysfs"
	depends on GOOGLE_COREBOOT_TABLE
	help
	  CBMEM is a downwards-growing memory region created by the
	  Coreboot BIOS containing tagged data structures from the
	  BIOS.  These data structures expose things like the verified
	  boot firmware variables, flash layout, firmware event log,
	  and more.

	  This option enables the cbmem module, which causes the
	  kernel to search for Coreboot CBMEM entries, and expose the
	  memory for each entry in sysfs under
	  /sys/bus/coreboot/devices/cbmem-<id>.

config GOOGLE_COREBOOT_TABLE
	tristate "Coreboot Table Access"
	depends on HAS_IOMEM && (ACPI || OF)
+3 −0
Original line number Diff line number Diff line
@@ -7,5 +7,8 @@ obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT)   += memconsole-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o

# Must come after coreboot_table.o, as this driver depends on that bus type.
obj-$(CONFIG_GOOGLE_CBMEM)		+= cbmem.o

vpd-sysfs-y := vpd.o vpd_decode.o
obj-$(CONFIG_GOOGLE_VPD)		+= vpd-sysfs.o
+129 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * cbmem.c
 *
 * Driver for exporting cbmem entries in sysfs.
 *
 * Copyright 2022 Google LLC
 */

#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>

#include "coreboot_table.h"

struct cbmem_entry {
	char *mem_file_buf;
	u32 size;
};

static struct cbmem_entry *to_cbmem_entry(struct kobject *kobj)
{
	return dev_get_drvdata(kobj_to_dev(kobj));
}

static ssize_t mem_read(struct file *filp, struct kobject *kobj,
			struct bin_attribute *bin_attr, char *buf, loff_t pos,
			size_t count)
{
	struct cbmem_entry *entry = to_cbmem_entry(kobj);

	return memory_read_from_buffer(buf, count, &pos, entry->mem_file_buf,
				       entry->size);
}

static ssize_t mem_write(struct file *filp, struct kobject *kobj,
			 struct bin_attribute *bin_attr, char *buf, loff_t pos,
			 size_t count)
{
	struct cbmem_entry *entry = to_cbmem_entry(kobj);

	if (pos < 0 || pos >= entry->size)
		return -EINVAL;
	if (count > entry->size - pos)
		count = entry->size - pos;

	memcpy(entry->mem_file_buf + pos, buf, count);
	return count;
}
static BIN_ATTR_ADMIN_RW(mem, 0);

static ssize_t address_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct coreboot_device *cbdev = dev_to_coreboot_device(dev);

	return sysfs_emit(buf, "0x%llx\n", cbdev->cbmem_entry.address);
}
static DEVICE_ATTR_RO(address);

static ssize_t size_show(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	struct coreboot_device *cbdev = dev_to_coreboot_device(dev);

	return sysfs_emit(buf, "0x%x\n", cbdev->cbmem_entry.entry_size);
}
static DEVICE_ATTR_RO(size);

static struct attribute *attrs[] = {
	&dev_attr_address.attr,
	&dev_attr_size.attr,
	NULL,
};

static struct bin_attribute *bin_attrs[] = {
	&bin_attr_mem,
	NULL,
};

static const struct attribute_group cbmem_entry_group = {
	.attrs = attrs,
	.bin_attrs = bin_attrs,
};

static const struct attribute_group *dev_groups[] = {
	&cbmem_entry_group,
	NULL,
};

static int cbmem_entry_probe(struct coreboot_device *dev)
{
	struct cbmem_entry *entry;

	entry = devm_kzalloc(&dev->dev, sizeof(*entry), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;

	dev_set_drvdata(&dev->dev, entry);
	entry->mem_file_buf = devm_memremap(&dev->dev, dev->cbmem_entry.address,
					    dev->cbmem_entry.entry_size,
					    MEMREMAP_WB);
	if (!entry->mem_file_buf)
		return -ENOMEM;

	entry->size = dev->cbmem_entry.entry_size;

	return 0;
}

static struct coreboot_driver cbmem_entry_driver = {
	.probe = cbmem_entry_probe,
	.drv = {
		.name = "cbmem",
		.owner = THIS_MODULE,
		.dev_groups = dev_groups,
	},
	.tag = LB_TAG_CBMEM_ENTRY,
};
module_coreboot_driver(cbmem_entry_driver);

MODULE_AUTHOR("Jack Rosenthal <jrosenth@chromium.org>");
MODULE_LICENSE("GPL");
+10 −1
Original line number Diff line number Diff line
@@ -97,12 +97,21 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
		if (!device)
			return -ENOMEM;

		dev_set_name(&device->dev, "coreboot%d", i);
		device->dev.parent = dev;
		device->dev.bus = &coreboot_bus_type;
		device->dev.release = coreboot_device_release;
		memcpy(&device->entry, ptr_entry, entry->size);

		switch (device->entry.tag) {
		case LB_TAG_CBMEM_ENTRY:
			dev_set_name(&device->dev, "cbmem-%08x",
				     device->cbmem_entry.id);
			break;
		default:
			dev_set_name(&device->dev, "coreboot%d", i);
			break;
		}

		ret = device_register(&device->dev);
		if (ret) {
			put_device(&device->dev);
Loading