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