vpd.c 7.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * vpd.c
 *
 * Driver for exporting VPD content to sysfs.
 *
 * Copyright 2017 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License v2.0 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/module.h>
25 26
#include <linux/of_address.h>
#include <linux/platform_device.h>
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
#include <linux/slab.h>
#include <linux/sysfs.h>

#include "coreboot_table.h"
#include "vpd_decode.h"

#define CB_TAG_VPD      0x2c
#define VPD_CBMEM_MAGIC 0x43524f53

static struct kobject *vpd_kobj;

struct vpd_cbmem {
	u32 magic;
	u32 version;
	u32 ro_size;
	u32 rw_size;
	u8  blob[0];
};

struct vpd_section {
	bool enabled;
	const char *name;
	char *raw_name;                /* the string name_raw */
	struct kobject *kobj;          /* vpd/name directory */
	char *baseaddr;
	struct bin_attribute bin_attr; /* vpd/name_raw bin_attribute */
	struct list_head attribs;      /* key/value in vpd_attrib_info list */
};

struct vpd_attrib_info {
	char *key;
	const char *value;
	struct bin_attribute bin_attr;
	struct list_head list;
};

static struct vpd_section ro_vpd;
static struct vpd_section rw_vpd;

static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
			       struct bin_attribute *bin_attr, char *buf,
			       loff_t pos, size_t count)
{
	struct vpd_attrib_info *info = bin_attr->private;

	return memory_read_from_buffer(buf, count, &pos, info->value,
				       info->bin_attr.size);
}

/*
 * vpd_section_check_key_name()
 *
 * The VPD specification supports only [a-zA-Z0-9_]+ characters in key names but
 * old firmware versions may have entries like "S/N" which are problematic when
 * exporting them as sysfs attributes. These keys present in old firmwares are
 * ignored.
 *
 * Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
 *
 * @key: The key name to check
 * @key_len: key name length
 */
static int vpd_section_check_key_name(const u8 *key, s32 key_len)
{
	int c;

	while (key_len-- > 0) {
		c = *key++;

		if (!isalnum(c) && c != '_')
			return VPD_FAIL;
	}

	return VPD_OK;
}

103 104
static int vpd_section_attrib_add(const u8 *key, u32 key_len,
				  const u8 *value, u32 value_len,
105 106 107 108 109 110 111 112 113 114 115 116 117 118
				  void *arg)
{
	int ret;
	struct vpd_section *sec = arg;
	struct vpd_attrib_info *info;

	/*
	 * Return VPD_OK immediately to decode next entry if the current key
	 * name contains invalid characters.
	 */
	if (vpd_section_check_key_name(key, key_len) != VPD_OK)
		return VPD_OK;

	info = kzalloc(sizeof(*info), GFP_KERNEL);
119
	if (!info)
120
		return -ENOMEM;
121 122

	info->key = kstrndup(key, key_len, GFP_KERNEL);
123 124 125 126
	if (!info->key) {
		ret = -ENOMEM;
		goto free_info;
	}
127 128 129 130 131 132 133 134 135 136 137 138 139

	sysfs_bin_attr_init(&info->bin_attr);
	info->bin_attr.attr.name = info->key;
	info->bin_attr.attr.mode = 0444;
	info->bin_attr.size = value_len;
	info->bin_attr.read = vpd_attrib_read;
	info->bin_attr.private = info;

	info->value = value;

	INIT_LIST_HEAD(&info->list);

	ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
140 141
	if (ret)
		goto free_info_key;
142

143
	list_add_tail(&info->list, &sec->attribs);
144
	return 0;
145 146 147 148 149 150 151

free_info_key:
	kfree(info->key);
free_info:
	kfree(info);

	return ret;
152 153 154 155 156 157 158 159 160
}

static void vpd_section_attrib_destroy(struct vpd_section *sec)
{
	struct vpd_attrib_info *info;
	struct vpd_attrib_info *temp;

	list_for_each_entry_safe(info, temp, &sec->attribs, list) {
		sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
161
		kfree(info->key);
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
		kfree(info);
	}
}

static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
				struct bin_attribute *bin_attr, char *buf,
				loff_t pos, size_t count)
{
	struct vpd_section *sec = bin_attr->private;

	return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
				       sec->bin_attr.size);
}

static int vpd_section_create_attribs(struct vpd_section *sec)
{
	s32 consumed;
	int ret;

	consumed = 0;
	do {
		ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
					&consumed, vpd_section_attrib_add, sec);
	} while (ret == VPD_OK);

	return 0;
}

static int vpd_section_init(const char *name, struct vpd_section *sec,
			    phys_addr_t physaddr, size_t size)
{
193
	int err;
194 195 196 197 198 199 200 201

	sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
	if (!sec->baseaddr)
		return -ENOMEM;

	sec->name = name;

	/* We want to export the raw partion with name ${name}_raw */
202 203 204
	sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
	if (!sec->raw_name) {
		err = -ENOMEM;
205
		goto err_memunmap;
206
	}
207 208 209 210 211 212 213 214

	sysfs_bin_attr_init(&sec->bin_attr);
	sec->bin_attr.attr.name = sec->raw_name;
	sec->bin_attr.attr.mode = 0444;
	sec->bin_attr.size = size;
	sec->bin_attr.read = vpd_section_read;
	sec->bin_attr.private = sec;

215 216 217
	err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
	if (err)
		goto err_free_raw_name;
218 219 220

	sec->kobj = kobject_create_and_add(name, vpd_kobj);
	if (!sec->kobj) {
221 222
		err = -EINVAL;
		goto err_sysfs_remove;
223 224 225 226 227 228 229 230 231
	}

	INIT_LIST_HEAD(&sec->attribs);
	vpd_section_create_attribs(sec);

	sec->enabled = true;

	return 0;

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 237
err_memunmap:
	memunmap(sec->baseaddr);
238
	return err;
239 240 241 242 243 244
}

static int vpd_section_destroy(struct vpd_section *sec)
{
	if (sec->enabled) {
		vpd_section_attrib_destroy(sec);
245
		kobject_put(sec->kobj);
246 247
		sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
		kfree(sec->raw_name);
248
		memunmap(sec->baseaddr);
249
		sec->enabled = false;
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	}

	return 0;
}

static int vpd_sections_init(phys_addr_t physaddr)
{
	struct vpd_cbmem __iomem *temp;
	struct vpd_cbmem header;
	int ret = 0;

	temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
	if (!temp)
		return -ENOMEM;

	memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem));
266
	memunmap(temp);
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

	if (header.magic != VPD_CBMEM_MAGIC)
		return -ENODEV;

	if (header.ro_size) {
		ret = vpd_section_init("ro", &ro_vpd,
				       physaddr + sizeof(struct vpd_cbmem),
				       header.ro_size);
		if (ret)
			return ret;
	}

	if (header.rw_size) {
		ret = vpd_section_init("rw", &rw_vpd,
				       physaddr + sizeof(struct vpd_cbmem) +
				       header.ro_size, header.rw_size);
283 284
		if (ret) {
			vpd_section_destroy(&ro_vpd);
285
			return ret;
286
		}
287 288 289 290 291
	}

	return 0;
}

292
static int vpd_probe(struct coreboot_device *dev)
293
{
294 295
	int ret;

296 297 298 299
	vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
	if (!vpd_kobj)
		return -ENOMEM;

300
	ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
301 302 303 304 305 306
	if (ret) {
		kobject_put(vpd_kobj);
		return ret;
	}

	return 0;
307 308
}

309
static int vpd_remove(struct coreboot_device *dev)
310 311 312 313
{
	vpd_section_destroy(&ro_vpd);
	vpd_section_destroy(&rw_vpd);

314 315
	kobject_put(vpd_kobj);

316 317 318
	return 0;
}

319
static struct coreboot_driver vpd_driver = {
320
	.probe = vpd_probe,
321
	.remove = vpd_remove,
322
	.drv = {
323 324
		.name = "vpd",
	},
325
	.tag = CB_TAG_VPD,
326 327
};

328
static int __init coreboot_vpd_init(void)
329
{
330
	return coreboot_driver_register(&vpd_driver);
331 332
}

333
static void __exit coreboot_vpd_exit(void)
334
{
335
	coreboot_driver_unregister(&vpd_driver);
336 337
}

338 339
module_init(coreboot_vpd_init);
module_exit(coreboot_vpd_exit);
340 341 342

MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");