Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/google/vpd.c
26428 views
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
const 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
const 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 const struct coreboot_device_id vpd_ids[] = {
310
{ .tag = CB_TAG_VPD },
311
{ /* sentinel */ }
312
};
313
MODULE_DEVICE_TABLE(coreboot, vpd_ids);
314
315
static struct coreboot_driver vpd_driver = {
316
.probe = vpd_probe,
317
.remove = vpd_remove,
318
.drv = {
319
.name = "vpd",
320
},
321
.id_table = vpd_ids,
322
};
323
module_coreboot_driver(vpd_driver);
324
325
MODULE_AUTHOR("Google, Inc.");
326
MODULE_DESCRIPTION("Driver for exporting Vital Product Data content to sysfs");
327
MODULE_LICENSE("GPL");
328
329