Skip to content

Commit

Permalink
firmware: google: Implement cbmem in sysfs driver
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: Tzung-Bi Shih <[email protected]>
Reviewed-by: Guenter Roeck <[email protected]>
Reviewed-by: Julius Werner <[email protected]>
Tested-by: Jack Rosenthal <[email protected]>
Signed-off-by: Jack Rosenthal <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
jackrosenthal authored and gregkh committed Nov 10, 2022
1 parent d88bd09 commit 19d5402
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 1 deletion.
45 changes: 45 additions & 0 deletions Documentation/ABI/testing/sysfs-bus-coreboot
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
What: /sys/bus/coreboot
Date: August 2022
Contact: Jack Rosenthal <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 changes: 15 additions & 0 deletions drivers/firmware/google/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions drivers/firmware/google/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 129 additions & 0 deletions drivers/firmware/google/cbmem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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 <[email protected]>");
MODULE_LICENSE("GPL");
11 changes: 10 additions & 1 deletion drivers/firmware/google/coreboot_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions drivers/firmware/google/coreboot_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ struct lb_cbmem_ref {
u64 cbmem_addr;
};

#define LB_TAG_CBMEM_ENTRY 0x31

/* Corresponds to LB_TAG_CBMEM_ENTRY */
struct lb_cbmem_entry {
u32 tag;
u32 size;

u64 address;
u32 entry_size;
u32 id;
};

/* Describes framebuffer setup by coreboot */
struct lb_framebuffer {
u32 tag;
Expand All @@ -65,10 +77,16 @@ struct coreboot_device {
union {
struct coreboot_table_entry entry;
struct lb_cbmem_ref cbmem_ref;
struct lb_cbmem_entry cbmem_entry;
struct lb_framebuffer framebuffer;
};
};

static inline struct coreboot_device *dev_to_coreboot_device(struct device *dev)
{
return container_of(dev, struct coreboot_device, dev);
}

/* A driver for handling devices described in coreboot tables. */
struct coreboot_driver {
int (*probe)(struct coreboot_device *);
Expand Down

0 comments on commit 19d5402

Please sign in to comment.