brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 1749529 Raw
328 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * vpd.c4 *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      0x2c26#define VPD_CBMEM_MAGIC 0x43524f5327 28static struct kobject *vpd_kobj;29 30struct vpd_cbmem {31	u32 magic;32	u32 version;33	u32 ro_size;34	u32 rw_size;35	u8  blob[];36};37 38struct 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 48struct vpd_attrib_info {49	char *key;50	const char *value;51	struct bin_attribute bin_attr;52	struct list_head list;53};54 55static struct vpd_section ro_vpd;56static struct vpd_section rw_vpd;57 58static 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 but72 * old firmware versions may have entries like "S/N" which are problematic when73 * exporting them as sysfs attributes. These keys present in old firmwares are74 * ignored.75 *76 * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.77 *78 * @key: The key name to check79 * @key_len: key name length80 */81static 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 95static 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 key105	 * 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 138free_info_key:139	kfree(info->key);140free_info:141	kfree(info);142 143	return ret;144}145 146static 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 158static 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 168static 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 182static 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 224err_sysfs_remove:225	sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);226err_free_raw_name:227	kfree(sec->raw_name);228err_memunmap:229	memunmap(sec->baseaddr);230	return err;231}232 233static 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 247static 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 284static 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 301static 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 309static const struct coreboot_device_id vpd_ids[] = {310	{ .tag = CB_TAG_VPD },311	{ /* sentinel */ }312};313MODULE_DEVICE_TABLE(coreboot, vpd_ids);314 315static 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};323module_coreboot_driver(vpd_driver);324 325MODULE_AUTHOR("Google, Inc.");326MODULE_DESCRIPTION("Driver for exporting Vital Product Data content to sysfs");327MODULE_LICENSE("GPL");328