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"); 321