RDMA/mlx5: Recover from fatal event in dual port mode
[linux-2.6-microblaze.git] / drivers / firmware / google / vpd.c
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
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  */
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
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
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
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
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
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
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
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
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
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");