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 info->key = kzalloc(key_len + 1, GFP_KERNEL); 122 if (!info->key) { 123 ret = -ENOMEM; 124 goto free_info; 125 } 126 127 memcpy(info->key, key, key_len); 128 129 sysfs_bin_attr_init(&info->bin_attr); 130 info->bin_attr.attr.name = info->key; 131 info->bin_attr.attr.mode = 0444; 132 info->bin_attr.size = value_len; 133 info->bin_attr.read = vpd_attrib_read; 134 info->bin_attr.private = info; 135 136 info->value = value; 137 138 INIT_LIST_HEAD(&info->list); 139 list_add_tail(&info->list, &sec->attribs); 140 141 ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr); 142 if (ret) 143 goto free_info_key; 144 145 return 0; 146 147 free_info_key: 148 kfree(info->key); 149 free_info: 150 kfree(info); 151 152 return ret; 153 } 154 155 static void vpd_section_attrib_destroy(struct vpd_section *sec) 156 { 157 struct vpd_attrib_info *info; 158 struct vpd_attrib_info *temp; 159 160 list_for_each_entry_safe(info, temp, &sec->attribs, list) { 161 kfree(info->key); 162 sysfs_remove_bin_file(sec->kobj, &info->bin_attr); 163 kfree(info); 164 } 165 } 166 167 static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp, 168 struct bin_attribute *bin_attr, char *buf, 169 loff_t pos, size_t count) 170 { 171 struct vpd_section *sec = bin_attr->private; 172 173 return memory_read_from_buffer(buf, count, &pos, sec->baseaddr, 174 sec->bin_attr.size); 175 } 176 177 static int vpd_section_create_attribs(struct vpd_section *sec) 178 { 179 s32 consumed; 180 int ret; 181 182 consumed = 0; 183 do { 184 ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr, 185 &consumed, vpd_section_attrib_add, sec); 186 } while (ret == VPD_OK); 187 188 return 0; 189 } 190 191 static int vpd_section_init(const char *name, struct vpd_section *sec, 192 phys_addr_t physaddr, size_t size) 193 { 194 int ret; 195 int raw_len; 196 197 sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB); 198 if (!sec->baseaddr) 199 return -ENOMEM; 200 201 sec->name = name; 202 203 /* We want to export the raw partion with name ${name}_raw */ 204 raw_len = strlen(name) + 5; 205 sec->raw_name = kzalloc(raw_len, GFP_KERNEL); 206 strncpy(sec->raw_name, name, raw_len); 207 strncat(sec->raw_name, "_raw", raw_len); 208 209 sysfs_bin_attr_init(&sec->bin_attr); 210 sec->bin_attr.attr.name = sec->raw_name; 211 sec->bin_attr.attr.mode = 0444; 212 sec->bin_attr.size = size; 213 sec->bin_attr.read = vpd_section_read; 214 sec->bin_attr.private = sec; 215 216 ret = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr); 217 if (ret) 218 goto free_sec; 219 220 sec->kobj = kobject_create_and_add(name, vpd_kobj); 221 if (!sec->kobj) { 222 ret = -EINVAL; 223 goto sysfs_remove; 224 } 225 226 INIT_LIST_HEAD(&sec->attribs); 227 vpd_section_create_attribs(sec); 228 229 sec->enabled = true; 230 231 return 0; 232 233 sysfs_remove: 234 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); 235 236 free_sec: 237 kfree(sec->raw_name); 238 iounmap(sec->baseaddr); 239 240 return ret; 241 } 242 243 static int vpd_section_destroy(struct vpd_section *sec) 244 { 245 if (sec->enabled) { 246 vpd_section_attrib_destroy(sec); 247 kobject_del(sec->kobj); 248 sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); 249 kfree(sec->raw_name); 250 iounmap(sec->baseaddr); 251 } 252 253 return 0; 254 } 255 256 static int vpd_sections_init(phys_addr_t physaddr) 257 { 258 struct vpd_cbmem __iomem *temp; 259 struct vpd_cbmem header; 260 int ret = 0; 261 262 temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB); 263 if (!temp) 264 return -ENOMEM; 265 266 memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem)); 267 iounmap(temp); 268 269 if (header.magic != VPD_CBMEM_MAGIC) 270 return -ENODEV; 271 272 if (header.ro_size) { 273 ret = vpd_section_init("ro", &ro_vpd, 274 physaddr + sizeof(struct vpd_cbmem), 275 header.ro_size); 276 if (ret) 277 return ret; 278 } 279 280 if (header.rw_size) { 281 ret = vpd_section_init("rw", &rw_vpd, 282 physaddr + sizeof(struct vpd_cbmem) + 283 header.ro_size, header.rw_size); 284 if (ret) 285 return ret; 286 } 287 288 return 0; 289 } 290 291 static int vpd_probe(struct platform_device *pdev) 292 { 293 int ret; 294 struct lb_cbmem_ref entry; 295 296 ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry)); 297 if (ret) 298 return ret; 299 300 return vpd_sections_init(entry.cbmem_addr); 301 } 302 303 static struct platform_driver vpd_driver = { 304 .probe = vpd_probe, 305 .driver = { 306 .name = "vpd", 307 }, 308 }; 309 310 static int __init vpd_platform_init(void) 311 { 312 struct platform_device *pdev; 313 314 pdev = platform_device_register_simple("vpd", -1, NULL, 0); 315 if (IS_ERR(pdev)) 316 return PTR_ERR(pdev); 317 318 vpd_kobj = kobject_create_and_add("vpd", firmware_kobj); 319 if (!vpd_kobj) 320 return -ENOMEM; 321 322 memset(&ro_vpd, 0, sizeof(ro_vpd)); 323 memset(&rw_vpd, 0, sizeof(rw_vpd)); 324 325 platform_driver_register(&vpd_driver); 326 327 return 0; 328 } 329 330 static void __exit vpd_platform_exit(void) 331 { 332 vpd_section_destroy(&ro_vpd); 333 vpd_section_destroy(&rw_vpd); 334 kobject_del(vpd_kobj); 335 } 336 337 module_init(vpd_platform_init); 338 module_exit(vpd_platform_exit); 339 340 MODULE_AUTHOR("Google, Inc."); 341 MODULE_LICENSE("GPL"); 342