summaryrefslogtreecommitdiff
path: root/drivers/firmware/google/cbmem.c
blob: 6f810d720f4d9e17a9186f3bdaa313fbbddf1c46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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",
		.dev_groups = dev_groups,
	},
	.id_table = cbmem_ids,
};
module_coreboot_driver(cbmem_entry_driver);

MODULE_AUTHOR("Jack Rosenthal <jrosenth@chromium.org>");
MODULE_LICENSE("GPL");