1
2
3
4
5
6
7
8
9
10#include <linux/ctype.h>
11#include <linux/init.h>
12#include <linux/io.h>
13#include <linux/kernel.h>
14#include <linux/kobject.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <linux/of_address.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/sysfs.h>
21
22#include "coreboot_table.h"
23#include "vpd_decode.h"
24
25#define CB_TAG_VPD 0x2c
26#define VPD_CBMEM_MAGIC 0x43524f53
27
28static struct kobject *vpd_kobj;
29
30struct vpd_cbmem {
31 u32 magic;
32 u32 version;
33 u32 ro_size;
34 u32 rw_size;
35 u8 blob[];
36};
37
38struct vpd_section {
39 bool enabled;
40 const char *name;
41 char *raw_name;
42 struct kobject *kobj;
43 char *baseaddr;
44 struct bin_attribute bin_attr;
45 struct list_head attribs;
46};
47
48struct vpd_attrib_info {
49 char *key;
50 const char *value;
51 struct bin_attribute bin_attr;
52 struct list_head list;
53};
54
55static struct vpd_section ro_vpd;
56static struct vpd_section rw_vpd;
57
58static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
59 struct bin_attribute *bin_attr, char *buf,
60 loff_t pos, size_t count)
61{
62 struct vpd_attrib_info *info = bin_attr->private;
63
64 return memory_read_from_buffer(buf, count, &pos, info->value,
65 info->bin_attr.size);
66}
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81static int vpd_section_check_key_name(const u8 *key, s32 key_len)
82{
83 int c;
84
85 while (key_len-- > 0) {
86 c = *key++;
87
88 if (!isalnum(c) && c != '_')
89 return VPD_FAIL;
90 }
91
92 return VPD_OK;
93}
94
95static int vpd_section_attrib_add(const u8 *key, u32 key_len,
96 const u8 *value, u32 value_len,
97 void *arg)
98{
99 int ret;
100 struct vpd_section *sec = arg;
101 struct vpd_attrib_info *info;
102
103
104
105
106
107 if (vpd_section_check_key_name(key, key_len) != VPD_OK)
108 return VPD_OK;
109
110 info = kzalloc(sizeof(*info), GFP_KERNEL);
111 if (!info)
112 return -ENOMEM;
113
114 info->key = kstrndup(key, key_len, GFP_KERNEL);
115 if (!info->key) {
116 ret = -ENOMEM;
117 goto free_info;
118 }
119
120 sysfs_bin_attr_init(&info->bin_attr);
121 info->bin_attr.attr.name = info->key;
122 info->bin_attr.attr.mode = 0444;
123 info->bin_attr.size = value_len;
124 info->bin_attr.read = vpd_attrib_read;
125 info->bin_attr.private = info;
126
127 info->value = value;
128
129 INIT_LIST_HEAD(&info->list);
130
131 ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
132 if (ret)
133 goto free_info_key;
134
135 list_add_tail(&info->list, &sec->attribs);
136 return 0;
137
138free_info_key:
139 kfree(info->key);
140free_info:
141 kfree(info);
142
143 return ret;
144}
145
146static void vpd_section_attrib_destroy(struct vpd_section *sec)
147{
148 struct vpd_attrib_info *info;
149 struct vpd_attrib_info *temp;
150
151 list_for_each_entry_safe(info, temp, &sec->attribs, list) {
152 sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
153 kfree(info->key);
154 kfree(info);
155 }
156}
157
158static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
159 struct bin_attribute *bin_attr, char *buf,
160 loff_t pos, size_t count)
161{
162 struct vpd_section *sec = bin_attr->private;
163
164 return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
165 sec->bin_attr.size);
166}
167
168static int vpd_section_create_attribs(struct vpd_section *sec)
169{
170 s32 consumed;
171 int ret;
172
173 consumed = 0;
174 do {
175 ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
176 &consumed, vpd_section_attrib_add, sec);
177 } while (ret == VPD_OK);
178
179 return 0;
180}
181
182static int vpd_section_init(const char *name, struct vpd_section *sec,
183 phys_addr_t physaddr, size_t size)
184{
185 int err;
186
187 sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
188 if (!sec->baseaddr)
189 return -ENOMEM;
190
191 sec->name = name;
192
193
194 sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
195 if (!sec->raw_name) {
196 err = -ENOMEM;
197 goto err_memunmap;
198 }
199
200 sysfs_bin_attr_init(&sec->bin_attr);
201 sec->bin_attr.attr.name = sec->raw_name;
202 sec->bin_attr.attr.mode = 0444;
203 sec->bin_attr.size = size;
204 sec->bin_attr.read = vpd_section_read;
205 sec->bin_attr.private = sec;
206
207 err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
208 if (err)
209 goto err_free_raw_name;
210
211 sec->kobj = kobject_create_and_add(name, vpd_kobj);
212 if (!sec->kobj) {
213 err = -EINVAL;
214 goto err_sysfs_remove;
215 }
216
217 INIT_LIST_HEAD(&sec->attribs);
218 vpd_section_create_attribs(sec);
219
220 sec->enabled = true;
221
222 return 0;
223
224err_sysfs_remove:
225 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
226err_free_raw_name:
227 kfree(sec->raw_name);
228err_memunmap:
229 memunmap(sec->baseaddr);
230 return err;
231}
232
233static int vpd_section_destroy(struct vpd_section *sec)
234{
235 if (sec->enabled) {
236 vpd_section_attrib_destroy(sec);
237 kobject_put(sec->kobj);
238 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
239 kfree(sec->raw_name);
240 memunmap(sec->baseaddr);
241 sec->enabled = false;
242 }
243
244 return 0;
245}
246
247static int vpd_sections_init(phys_addr_t physaddr)
248{
249 struct vpd_cbmem *temp;
250 struct vpd_cbmem header;
251 int ret = 0;
252
253 temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
254 if (!temp)
255 return -ENOMEM;
256
257 memcpy(&header, temp, sizeof(struct vpd_cbmem));
258 memunmap(temp);
259
260 if (header.magic != VPD_CBMEM_MAGIC)
261 return -ENODEV;
262
263 if (header.ro_size) {
264 ret = vpd_section_init("ro", &ro_vpd,
265 physaddr + sizeof(struct vpd_cbmem),
266 header.ro_size);
267 if (ret)
268 return ret;
269 }
270
271 if (header.rw_size) {
272 ret = vpd_section_init("rw", &rw_vpd,
273 physaddr + sizeof(struct vpd_cbmem) +
274 header.ro_size, header.rw_size);
275 if (ret) {
276 vpd_section_destroy(&ro_vpd);
277 return ret;
278 }
279 }
280
281 return 0;
282}
283
284static int vpd_probe(struct coreboot_device *dev)
285{
286 int ret;
287
288 vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
289 if (!vpd_kobj)
290 return -ENOMEM;
291
292 ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
293 if (ret) {
294 kobject_put(vpd_kobj);
295 return ret;
296 }
297
298 return 0;
299}
300
301static int vpd_remove(struct coreboot_device *dev)
302{
303 vpd_section_destroy(&ro_vpd);
304 vpd_section_destroy(&rw_vpd);
305
306 kobject_put(vpd_kobj);
307
308 return 0;
309}
310
311static struct coreboot_driver vpd_driver = {
312 .probe = vpd_probe,
313 .remove = vpd_remove,
314 .drv = {
315 .name = "vpd",
316 },
317 .tag = CB_TAG_VPD,
318};
319module_coreboot_driver(vpd_driver);
320
321MODULE_AUTHOR("Google, Inc.");
322MODULE_LICENSE("GPL");
323