0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/ctype.h>
0011 #include <linux/init.h>
0012 #include <linux/io.h>
0013 #include <linux/kernel.h>
0014 #include <linux/kobject.h>
0015 #include <linux/list.h>
0016 #include <linux/module.h>
0017 #include <linux/of_address.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/slab.h>
0020 #include <linux/sysfs.h>
0021
0022 #include "coreboot_table.h"
0023 #include "vpd_decode.h"
0024
0025 #define CB_TAG_VPD 0x2c
0026 #define VPD_CBMEM_MAGIC 0x43524f53
0027
0028 static struct kobject *vpd_kobj;
0029
0030 struct vpd_cbmem {
0031 u32 magic;
0032 u32 version;
0033 u32 ro_size;
0034 u32 rw_size;
0035 u8 blob[];
0036 };
0037
0038 struct vpd_section {
0039 bool enabled;
0040 const char *name;
0041 char *raw_name;
0042 struct kobject *kobj;
0043 char *baseaddr;
0044 struct bin_attribute bin_attr;
0045 struct list_head attribs;
0046 };
0047
0048 struct vpd_attrib_info {
0049 char *key;
0050 const char *value;
0051 struct bin_attribute bin_attr;
0052 struct list_head list;
0053 };
0054
0055 static struct vpd_section ro_vpd;
0056 static struct vpd_section rw_vpd;
0057
0058 static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
0059 struct bin_attribute *bin_attr, char *buf,
0060 loff_t pos, size_t count)
0061 {
0062 struct vpd_attrib_info *info = bin_attr->private;
0063
0064 return memory_read_from_buffer(buf, count, &pos, info->value,
0065 info->bin_attr.size);
0066 }
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 static int vpd_section_check_key_name(const u8 *key, s32 key_len)
0082 {
0083 int c;
0084
0085 while (key_len-- > 0) {
0086 c = *key++;
0087
0088 if (!isalnum(c) && c != '_')
0089 return VPD_FAIL;
0090 }
0091
0092 return VPD_OK;
0093 }
0094
0095 static int vpd_section_attrib_add(const u8 *key, u32 key_len,
0096 const u8 *value, u32 value_len,
0097 void *arg)
0098 {
0099 int ret;
0100 struct vpd_section *sec = arg;
0101 struct vpd_attrib_info *info;
0102
0103
0104
0105
0106
0107 if (vpd_section_check_key_name(key, key_len) != VPD_OK)
0108 return VPD_OK;
0109
0110 info = kzalloc(sizeof(*info), GFP_KERNEL);
0111 if (!info)
0112 return -ENOMEM;
0113
0114 info->key = kstrndup(key, key_len, GFP_KERNEL);
0115 if (!info->key) {
0116 ret = -ENOMEM;
0117 goto free_info;
0118 }
0119
0120 sysfs_bin_attr_init(&info->bin_attr);
0121 info->bin_attr.attr.name = info->key;
0122 info->bin_attr.attr.mode = 0444;
0123 info->bin_attr.size = value_len;
0124 info->bin_attr.read = vpd_attrib_read;
0125 info->bin_attr.private = info;
0126
0127 info->value = value;
0128
0129 INIT_LIST_HEAD(&info->list);
0130
0131 ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
0132 if (ret)
0133 goto free_info_key;
0134
0135 list_add_tail(&info->list, &sec->attribs);
0136 return 0;
0137
0138 free_info_key:
0139 kfree(info->key);
0140 free_info:
0141 kfree(info);
0142
0143 return ret;
0144 }
0145
0146 static void vpd_section_attrib_destroy(struct vpd_section *sec)
0147 {
0148 struct vpd_attrib_info *info;
0149 struct vpd_attrib_info *temp;
0150
0151 list_for_each_entry_safe(info, temp, &sec->attribs, list) {
0152 sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
0153 kfree(info->key);
0154 kfree(info);
0155 }
0156 }
0157
0158 static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
0159 struct bin_attribute *bin_attr, char *buf,
0160 loff_t pos, size_t count)
0161 {
0162 struct vpd_section *sec = bin_attr->private;
0163
0164 return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
0165 sec->bin_attr.size);
0166 }
0167
0168 static int vpd_section_create_attribs(struct vpd_section *sec)
0169 {
0170 s32 consumed;
0171 int ret;
0172
0173 consumed = 0;
0174 do {
0175 ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
0176 &consumed, vpd_section_attrib_add, sec);
0177 } while (ret == VPD_OK);
0178
0179 return 0;
0180 }
0181
0182 static int vpd_section_init(const char *name, struct vpd_section *sec,
0183 phys_addr_t physaddr, size_t size)
0184 {
0185 int err;
0186
0187 sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
0188 if (!sec->baseaddr)
0189 return -ENOMEM;
0190
0191 sec->name = name;
0192
0193
0194 sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
0195 if (!sec->raw_name) {
0196 err = -ENOMEM;
0197 goto err_memunmap;
0198 }
0199
0200 sysfs_bin_attr_init(&sec->bin_attr);
0201 sec->bin_attr.attr.name = sec->raw_name;
0202 sec->bin_attr.attr.mode = 0444;
0203 sec->bin_attr.size = size;
0204 sec->bin_attr.read = vpd_section_read;
0205 sec->bin_attr.private = sec;
0206
0207 err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
0208 if (err)
0209 goto err_free_raw_name;
0210
0211 sec->kobj = kobject_create_and_add(name, vpd_kobj);
0212 if (!sec->kobj) {
0213 err = -EINVAL;
0214 goto err_sysfs_remove;
0215 }
0216
0217 INIT_LIST_HEAD(&sec->attribs);
0218 vpd_section_create_attribs(sec);
0219
0220 sec->enabled = true;
0221
0222 return 0;
0223
0224 err_sysfs_remove:
0225 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
0226 err_free_raw_name:
0227 kfree(sec->raw_name);
0228 err_memunmap:
0229 memunmap(sec->baseaddr);
0230 return err;
0231 }
0232
0233 static int vpd_section_destroy(struct vpd_section *sec)
0234 {
0235 if (sec->enabled) {
0236 vpd_section_attrib_destroy(sec);
0237 kobject_put(sec->kobj);
0238 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
0239 kfree(sec->raw_name);
0240 memunmap(sec->baseaddr);
0241 sec->enabled = false;
0242 }
0243
0244 return 0;
0245 }
0246
0247 static int vpd_sections_init(phys_addr_t physaddr)
0248 {
0249 struct vpd_cbmem *temp;
0250 struct vpd_cbmem header;
0251 int ret = 0;
0252
0253 temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
0254 if (!temp)
0255 return -ENOMEM;
0256
0257 memcpy(&header, temp, sizeof(struct vpd_cbmem));
0258 memunmap(temp);
0259
0260 if (header.magic != VPD_CBMEM_MAGIC)
0261 return -ENODEV;
0262
0263 if (header.ro_size) {
0264 ret = vpd_section_init("ro", &ro_vpd,
0265 physaddr + sizeof(struct vpd_cbmem),
0266 header.ro_size);
0267 if (ret)
0268 return ret;
0269 }
0270
0271 if (header.rw_size) {
0272 ret = vpd_section_init("rw", &rw_vpd,
0273 physaddr + sizeof(struct vpd_cbmem) +
0274 header.ro_size, header.rw_size);
0275 if (ret) {
0276 vpd_section_destroy(&ro_vpd);
0277 return ret;
0278 }
0279 }
0280
0281 return 0;
0282 }
0283
0284 static int vpd_probe(struct coreboot_device *dev)
0285 {
0286 int ret;
0287
0288 vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
0289 if (!vpd_kobj)
0290 return -ENOMEM;
0291
0292 ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
0293 if (ret) {
0294 kobject_put(vpd_kobj);
0295 return ret;
0296 }
0297
0298 return 0;
0299 }
0300
0301 static void vpd_remove(struct coreboot_device *dev)
0302 {
0303 vpd_section_destroy(&ro_vpd);
0304 vpd_section_destroy(&rw_vpd);
0305
0306 kobject_put(vpd_kobj);
0307 }
0308
0309 static struct coreboot_driver vpd_driver = {
0310 .probe = vpd_probe,
0311 .remove = vpd_remove,
0312 .drv = {
0313 .name = "vpd",
0314 },
0315 .tag = CB_TAG_VPD,
0316 };
0317 module_coreboot_driver(vpd_driver);
0318
0319 MODULE_AUTHOR("Google, Inc.");
0320 MODULE_LICENSE("GPL");