| // 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 (IS_ERR(entry->mem_file_buf)) |
| return PTR_ERR(entry->mem_file_buf); |
| |
| entry->size = dev->cbmem_entry.entry_size; |
| |
| return 0; |
| } |
| |
| static const struct coreboot_device_id cbmem_ids[] = { |
| { .tag = LB_TAG_CBMEM_ENTRY }, |
| { /* sentinel */ } |
| }; |
| MODULE_DEVICE_TABLE(coreboot, cbmem_ids); |
| |
| static struct coreboot_driver cbmem_entry_driver = { |
| .probe = cbmem_entry_probe, |
| .drv = { |
| .name = "cbmem", |
| .owner = THIS_MODULE, |
| .dev_groups = dev_groups, |
| }, |
| .id_table = cbmem_ids, |
| }; |
| module_coreboot_driver(cbmem_entry_driver); |
| |
| MODULE_AUTHOR("Jack Rosenthal <jrosenth@chromium.org>"); |
| MODULE_LICENSE("GPL"); |