1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cbmem.c |
4 | * |
5 | * Driver for exporting cbmem entries in sysfs. |
6 | * |
7 | * Copyright 2022 Google LLC |
8 | */ |
9 | |
10 | #include <linux/device.h> |
11 | #include <linux/init.h> |
12 | #include <linux/io.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/kobject.h> |
15 | #include <linux/module.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/sysfs.h> |
19 | |
20 | #include "coreboot_table.h" |
21 | |
22 | struct cbmem_entry { |
23 | char *mem_file_buf; |
24 | u32 size; |
25 | }; |
26 | |
27 | static struct cbmem_entry *to_cbmem_entry(struct kobject *kobj) |
28 | { |
29 | return dev_get_drvdata(kobj_to_dev(kobj)); |
30 | } |
31 | |
32 | static ssize_t mem_read(struct file *filp, struct kobject *kobj, |
33 | struct bin_attribute *bin_attr, char *buf, loff_t pos, |
34 | size_t count) |
35 | { |
36 | struct cbmem_entry *entry = to_cbmem_entry(kobj); |
37 | |
38 | return memory_read_from_buffer(to: buf, count, ppos: &pos, from: entry->mem_file_buf, |
39 | available: entry->size); |
40 | } |
41 | |
42 | static ssize_t mem_write(struct file *filp, struct kobject *kobj, |
43 | struct bin_attribute *bin_attr, char *buf, loff_t pos, |
44 | size_t count) |
45 | { |
46 | struct cbmem_entry *entry = to_cbmem_entry(kobj); |
47 | |
48 | if (pos < 0 || pos >= entry->size) |
49 | return -EINVAL; |
50 | if (count > entry->size - pos) |
51 | count = entry->size - pos; |
52 | |
53 | memcpy(entry->mem_file_buf + pos, buf, count); |
54 | return count; |
55 | } |
56 | static BIN_ATTR_ADMIN_RW(mem, 0); |
57 | |
58 | static ssize_t address_show(struct device *dev, struct device_attribute *attr, |
59 | char *buf) |
60 | { |
61 | struct coreboot_device *cbdev = dev_to_coreboot_device(dev); |
62 | |
63 | return sysfs_emit(buf, fmt: "0x%llx\n" , cbdev->cbmem_entry.address); |
64 | } |
65 | static DEVICE_ATTR_RO(address); |
66 | |
67 | static ssize_t size_show(struct device *dev, struct device_attribute *attr, |
68 | char *buf) |
69 | { |
70 | struct coreboot_device *cbdev = dev_to_coreboot_device(dev); |
71 | |
72 | return sysfs_emit(buf, fmt: "0x%x\n" , cbdev->cbmem_entry.entry_size); |
73 | } |
74 | static DEVICE_ATTR_RO(size); |
75 | |
76 | static struct attribute *attrs[] = { |
77 | &dev_attr_address.attr, |
78 | &dev_attr_size.attr, |
79 | NULL, |
80 | }; |
81 | |
82 | static struct bin_attribute *bin_attrs[] = { |
83 | &bin_attr_mem, |
84 | NULL, |
85 | }; |
86 | |
87 | static const struct attribute_group cbmem_entry_group = { |
88 | .attrs = attrs, |
89 | .bin_attrs = bin_attrs, |
90 | }; |
91 | |
92 | static const struct attribute_group *dev_groups[] = { |
93 | &cbmem_entry_group, |
94 | NULL, |
95 | }; |
96 | |
97 | static int cbmem_entry_probe(struct coreboot_device *dev) |
98 | { |
99 | struct cbmem_entry *entry; |
100 | |
101 | entry = devm_kzalloc(dev: &dev->dev, size: sizeof(*entry), GFP_KERNEL); |
102 | if (!entry) |
103 | return -ENOMEM; |
104 | |
105 | dev_set_drvdata(dev: &dev->dev, data: entry); |
106 | entry->mem_file_buf = devm_memremap(dev: &dev->dev, offset: dev->cbmem_entry.address, |
107 | size: dev->cbmem_entry.entry_size, |
108 | flags: MEMREMAP_WB); |
109 | if (IS_ERR(ptr: entry->mem_file_buf)) |
110 | return PTR_ERR(ptr: entry->mem_file_buf); |
111 | |
112 | entry->size = dev->cbmem_entry.entry_size; |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static const struct coreboot_device_id cbmem_ids[] = { |
118 | { .tag = LB_TAG_CBMEM_ENTRY }, |
119 | { /* sentinel */ } |
120 | }; |
121 | MODULE_DEVICE_TABLE(coreboot, cbmem_ids); |
122 | |
123 | static struct coreboot_driver cbmem_entry_driver = { |
124 | .probe = cbmem_entry_probe, |
125 | .drv = { |
126 | .name = "cbmem" , |
127 | .owner = THIS_MODULE, |
128 | .dev_groups = dev_groups, |
129 | }, |
130 | .id_table = cbmem_ids, |
131 | }; |
132 | module_coreboot_driver(cbmem_entry_driver); |
133 | |
134 | MODULE_AUTHOR("Jack Rosenthal <jrosenth@chromium.org>" ); |
135 | MODULE_LICENSE("GPL" ); |
136 | |