xref: /openbmc/linux/drivers/firmware/google/vpd.c (revision 5f680532)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * vpd.c
4  *
5  * Driver for exporting VPD content to sysfs.
6  *
7  * Copyright 2017 Google Inc.
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 
28 static struct kobject *vpd_kobj;
29 
30 struct vpd_cbmem {
31 	u32 magic;
32 	u32 version;
33 	u32 ro_size;
34 	u32 rw_size;
35 	u8  blob[];
36 };
37 
38 struct vpd_section {
39 	bool enabled;
40 	const char *name;
41 	char *raw_name;                /* the string name_raw */
42 	struct kobject *kobj;          /* vpd/name directory */
43 	char *baseaddr;
44 	struct bin_attribute bin_attr; /* vpd/name_raw bin_attribute */
45 	struct list_head attribs;      /* key/value in vpd_attrib_info list */
46 };
47 
48 struct vpd_attrib_info {
49 	char *key;
50 	const char *value;
51 	struct bin_attribute bin_attr;
52 	struct list_head list;
53 };
54 
55 static struct vpd_section ro_vpd;
56 static struct vpd_section rw_vpd;
57 
vpd_attrib_read(struct file * filp,struct kobject * kobp,struct bin_attribute * bin_attr,char * buf,loff_t pos,size_t count)58 static 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  * vpd_section_check_key_name()
70  *
71  * The VPD specification supports only [a-zA-Z0-9_]+ characters in key names but
72  * old firmware versions may have entries like "S/N" which are problematic when
73  * exporting them as sysfs attributes. These keys present in old firmwares are
74  * ignored.
75  *
76  * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
77  *
78  * @key: The key name to check
79  * @key_len: key name length
80  */
vpd_section_check_key_name(const u8 * key,s32 key_len)81 static 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 
vpd_section_attrib_add(const u8 * key,u32 key_len,const u8 * value,u32 value_len,void * arg)95 static 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 	 * Return VPD_OK immediately to decode next entry if the current key
105 	 * name contains invalid characters.
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 
138 free_info_key:
139 	kfree(info->key);
140 free_info:
141 	kfree(info);
142 
143 	return ret;
144 }
145 
vpd_section_attrib_destroy(struct vpd_section * sec)146 static 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 
vpd_section_read(struct file * filp,struct kobject * kobp,struct bin_attribute * bin_attr,char * buf,loff_t pos,size_t count)158 static 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 
vpd_section_create_attribs(struct vpd_section * sec)168 static 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 
vpd_section_init(const char * name,struct vpd_section * sec,phys_addr_t physaddr,size_t size)182 static 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 	/* We want to export the raw partition with name ${name}_raw */
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 
224 err_sysfs_remove:
225 	sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
226 err_free_raw_name:
227 	kfree(sec->raw_name);
228 err_memunmap:
229 	memunmap(sec->baseaddr);
230 	return err;
231 }
232 
vpd_section_destroy(struct vpd_section * sec)233 static 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 
vpd_sections_init(phys_addr_t physaddr)247 static 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 
vpd_probe(struct coreboot_device * dev)284 static 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 
vpd_remove(struct coreboot_device * dev)301 static void 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 
309 static struct coreboot_driver vpd_driver = {
310 	.probe = vpd_probe,
311 	.remove = vpd_remove,
312 	.drv = {
313 		.name = "vpd",
314 	},
315 	.tag = CB_TAG_VPD,
316 };
317 module_coreboot_driver(vpd_driver);
318 
319 MODULE_AUTHOR("Google, Inc.");
320 MODULE_LICENSE("GPL");
321