xref: /openbmc/linux/drivers/firmware/google/vpd.c (revision ba61bb17)
1 /*
2  * vpd.c
3  *
4  * Driver for exporting VPD content to sysfs.
5  *
6  * Copyright 2017 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License v2.0 as published by
10  * the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 #include <linux/ctype.h>
19 #include <linux/init.h>
20 #include <linux/io.h>
21 #include <linux/kernel.h>
22 #include <linux/kobject.h>
23 #include <linux/list.h>
24 #include <linux/module.h>
25 #include <linux/of_address.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28 #include <linux/sysfs.h>
29 
30 #include "coreboot_table.h"
31 #include "vpd_decode.h"
32 
33 #define CB_TAG_VPD      0x2c
34 #define VPD_CBMEM_MAGIC 0x43524f53
35 
36 static struct kobject *vpd_kobj;
37 
38 struct vpd_cbmem {
39 	u32 magic;
40 	u32 version;
41 	u32 ro_size;
42 	u32 rw_size;
43 	u8  blob[0];
44 };
45 
46 struct vpd_section {
47 	bool enabled;
48 	const char *name;
49 	char *raw_name;                /* the string name_raw */
50 	struct kobject *kobj;          /* vpd/name directory */
51 	char *baseaddr;
52 	struct bin_attribute bin_attr; /* vpd/name_raw bin_attribute */
53 	struct list_head attribs;      /* key/value in vpd_attrib_info list */
54 };
55 
56 struct vpd_attrib_info {
57 	char *key;
58 	const char *value;
59 	struct bin_attribute bin_attr;
60 	struct list_head list;
61 };
62 
63 static struct vpd_section ro_vpd;
64 static struct vpd_section rw_vpd;
65 
66 static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
67 			       struct bin_attribute *bin_attr, char *buf,
68 			       loff_t pos, size_t count)
69 {
70 	struct vpd_attrib_info *info = bin_attr->private;
71 
72 	return memory_read_from_buffer(buf, count, &pos, info->value,
73 				       info->bin_attr.size);
74 }
75 
76 /*
77  * vpd_section_check_key_name()
78  *
79  * The VPD specification supports only [a-zA-Z0-9_]+ characters in key names but
80  * old firmware versions may have entries like "S/N" which are problematic when
81  * exporting them as sysfs attributes. These keys present in old firmwares are
82  * ignored.
83  *
84  * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
85  *
86  * @key: The key name to check
87  * @key_len: key name length
88  */
89 static int vpd_section_check_key_name(const u8 *key, s32 key_len)
90 {
91 	int c;
92 
93 	while (key_len-- > 0) {
94 		c = *key++;
95 
96 		if (!isalnum(c) && c != '_')
97 			return VPD_FAIL;
98 	}
99 
100 	return VPD_OK;
101 }
102 
103 static int vpd_section_attrib_add(const u8 *key, s32 key_len,
104 				  const u8 *value, s32 value_len,
105 				  void *arg)
106 {
107 	int ret;
108 	struct vpd_section *sec = arg;
109 	struct vpd_attrib_info *info;
110 
111 	/*
112 	 * Return VPD_OK immediately to decode next entry if the current key
113 	 * name contains invalid characters.
114 	 */
115 	if (vpd_section_check_key_name(key, key_len) != VPD_OK)
116 		return VPD_OK;
117 
118 	info = kzalloc(sizeof(*info), GFP_KERNEL);
119 	if (!info)
120 		return -ENOMEM;
121 
122 	info->key = kstrndup(key, key_len, GFP_KERNEL);
123 	if (!info->key) {
124 		ret = -ENOMEM;
125 		goto free_info;
126 	}
127 
128 	sysfs_bin_attr_init(&info->bin_attr);
129 	info->bin_attr.attr.name = info->key;
130 	info->bin_attr.attr.mode = 0444;
131 	info->bin_attr.size = value_len;
132 	info->bin_attr.read = vpd_attrib_read;
133 	info->bin_attr.private = info;
134 
135 	info->value = value;
136 
137 	INIT_LIST_HEAD(&info->list);
138 
139 	ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
140 	if (ret)
141 		goto free_info_key;
142 
143 	list_add_tail(&info->list, &sec->attribs);
144 	return 0;
145 
146 free_info_key:
147 	kfree(info->key);
148 free_info:
149 	kfree(info);
150 
151 	return ret;
152 }
153 
154 static void vpd_section_attrib_destroy(struct vpd_section *sec)
155 {
156 	struct vpd_attrib_info *info;
157 	struct vpd_attrib_info *temp;
158 
159 	list_for_each_entry_safe(info, temp, &sec->attribs, list) {
160 		sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
161 		kfree(info->key);
162 		kfree(info);
163 	}
164 }
165 
166 static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
167 				struct bin_attribute *bin_attr, char *buf,
168 				loff_t pos, size_t count)
169 {
170 	struct vpd_section *sec = bin_attr->private;
171 
172 	return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
173 				       sec->bin_attr.size);
174 }
175 
176 static int vpd_section_create_attribs(struct vpd_section *sec)
177 {
178 	s32 consumed;
179 	int ret;
180 
181 	consumed = 0;
182 	do {
183 		ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
184 					&consumed, vpd_section_attrib_add, sec);
185 	} while (ret == VPD_OK);
186 
187 	return 0;
188 }
189 
190 static int vpd_section_init(const char *name, struct vpd_section *sec,
191 			    phys_addr_t physaddr, size_t size)
192 {
193 	int err;
194 
195 	sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
196 	if (!sec->baseaddr)
197 		return -ENOMEM;
198 
199 	sec->name = name;
200 
201 	/* We want to export the raw partion with name ${name}_raw */
202 	sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
203 	if (!sec->raw_name) {
204 		err = -ENOMEM;
205 		goto err_memunmap;
206 	}
207 
208 	sysfs_bin_attr_init(&sec->bin_attr);
209 	sec->bin_attr.attr.name = sec->raw_name;
210 	sec->bin_attr.attr.mode = 0444;
211 	sec->bin_attr.size = size;
212 	sec->bin_attr.read = vpd_section_read;
213 	sec->bin_attr.private = sec;
214 
215 	err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
216 	if (err)
217 		goto err_free_raw_name;
218 
219 	sec->kobj = kobject_create_and_add(name, vpd_kobj);
220 	if (!sec->kobj) {
221 		err = -EINVAL;
222 		goto err_sysfs_remove;
223 	}
224 
225 	INIT_LIST_HEAD(&sec->attribs);
226 	vpd_section_create_attribs(sec);
227 
228 	sec->enabled = true;
229 
230 	return 0;
231 
232 err_sysfs_remove:
233 	sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
234 err_free_raw_name:
235 	kfree(sec->raw_name);
236 err_memunmap:
237 	memunmap(sec->baseaddr);
238 	return err;
239 }
240 
241 static int vpd_section_destroy(struct vpd_section *sec)
242 {
243 	if (sec->enabled) {
244 		vpd_section_attrib_destroy(sec);
245 		kobject_put(sec->kobj);
246 		sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
247 		kfree(sec->raw_name);
248 		memunmap(sec->baseaddr);
249 	}
250 
251 	return 0;
252 }
253 
254 static int vpd_sections_init(phys_addr_t physaddr)
255 {
256 	struct vpd_cbmem __iomem *temp;
257 	struct vpd_cbmem header;
258 	int ret = 0;
259 
260 	temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
261 	if (!temp)
262 		return -ENOMEM;
263 
264 	memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem));
265 	memunmap(temp);
266 
267 	if (header.magic != VPD_CBMEM_MAGIC)
268 		return -ENODEV;
269 
270 	if (header.ro_size) {
271 		ret = vpd_section_init("ro", &ro_vpd,
272 				       physaddr + sizeof(struct vpd_cbmem),
273 				       header.ro_size);
274 		if (ret)
275 			return ret;
276 	}
277 
278 	if (header.rw_size) {
279 		ret = vpd_section_init("rw", &rw_vpd,
280 				       physaddr + sizeof(struct vpd_cbmem) +
281 				       header.ro_size, header.rw_size);
282 		if (ret)
283 			return ret;
284 	}
285 
286 	return 0;
287 }
288 
289 static int vpd_probe(struct coreboot_device *dev)
290 {
291 	int ret;
292 
293 	vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
294 	if (!vpd_kobj)
295 		return -ENOMEM;
296 
297 	ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
298 	if (ret) {
299 		kobject_put(vpd_kobj);
300 		return ret;
301 	}
302 
303 	return 0;
304 }
305 
306 static int vpd_remove(struct coreboot_device *dev)
307 {
308 	vpd_section_destroy(&ro_vpd);
309 	vpd_section_destroy(&rw_vpd);
310 
311 	kobject_put(vpd_kobj);
312 
313 	return 0;
314 }
315 
316 static struct coreboot_driver vpd_driver = {
317 	.probe = vpd_probe,
318 	.remove = vpd_remove,
319 	.drv = {
320 		.name = "vpd",
321 	},
322 	.tag = CB_TAG_VPD,
323 };
324 
325 static int __init coreboot_vpd_init(void)
326 {
327 	return coreboot_driver_register(&vpd_driver);
328 }
329 
330 static void __exit coreboot_vpd_exit(void)
331 {
332 	coreboot_driver_unregister(&vpd_driver);
333 }
334 
335 module_init(coreboot_vpd_init);
336 module_exit(coreboot_vpd_exit);
337 
338 MODULE_AUTHOR("Google, Inc.");
339 MODULE_LICENSE("GPL");
340