cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

property.c (18697B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Thunderbolt XDomain property support
      4 *
      5 * Copyright (C) 2017, Intel Corporation
      6 * Authors: Michael Jamet <michael.jamet@intel.com>
      7 *          Mika Westerberg <mika.westerberg@linux.intel.com>
      8 */
      9
     10#include <linux/err.h>
     11#include <linux/slab.h>
     12#include <linux/string.h>
     13#include <linux/uuid.h>
     14#include <linux/thunderbolt.h>
     15
     16struct tb_property_entry {
     17	u32 key_hi;
     18	u32 key_lo;
     19	u16 length;
     20	u8 reserved;
     21	u8 type;
     22	u32 value;
     23};
     24
     25struct tb_property_rootdir_entry {
     26	u32 magic;
     27	u32 length;
     28	struct tb_property_entry entries[];
     29};
     30
     31struct tb_property_dir_entry {
     32	u32 uuid[4];
     33	struct tb_property_entry entries[];
     34};
     35
     36#define TB_PROPERTY_ROOTDIR_MAGIC	0x55584401
     37
     38static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
     39	size_t block_len, unsigned int dir_offset, size_t dir_len,
     40	bool is_root);
     41
     42static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
     43{
     44	be32_to_cpu_array(dst, src, dwords);
     45}
     46
     47static inline void format_dwdata(void *dst, const void *src, size_t dwords)
     48{
     49	cpu_to_be32_array(dst, src, dwords);
     50}
     51
     52static bool tb_property_entry_valid(const struct tb_property_entry *entry,
     53				  size_t block_len)
     54{
     55	switch (entry->type) {
     56	case TB_PROPERTY_TYPE_DIRECTORY:
     57	case TB_PROPERTY_TYPE_DATA:
     58	case TB_PROPERTY_TYPE_TEXT:
     59		if (entry->length > block_len)
     60			return false;
     61		if (entry->value + entry->length > block_len)
     62			return false;
     63		break;
     64
     65	case TB_PROPERTY_TYPE_VALUE:
     66		if (entry->length != 1)
     67			return false;
     68		break;
     69	}
     70
     71	return true;
     72}
     73
     74static bool tb_property_key_valid(const char *key)
     75{
     76	return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
     77}
     78
     79static struct tb_property *
     80tb_property_alloc(const char *key, enum tb_property_type type)
     81{
     82	struct tb_property *property;
     83
     84	property = kzalloc(sizeof(*property), GFP_KERNEL);
     85	if (!property)
     86		return NULL;
     87
     88	strcpy(property->key, key);
     89	property->type = type;
     90	INIT_LIST_HEAD(&property->list);
     91
     92	return property;
     93}
     94
     95static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
     96					const struct tb_property_entry *entry)
     97{
     98	char key[TB_PROPERTY_KEY_SIZE + 1];
     99	struct tb_property *property;
    100	struct tb_property_dir *dir;
    101
    102	if (!tb_property_entry_valid(entry, block_len))
    103		return NULL;
    104
    105	parse_dwdata(key, entry, 2);
    106	key[TB_PROPERTY_KEY_SIZE] = '\0';
    107
    108	property = tb_property_alloc(key, entry->type);
    109	if (!property)
    110		return NULL;
    111
    112	property->length = entry->length;
    113
    114	switch (property->type) {
    115	case TB_PROPERTY_TYPE_DIRECTORY:
    116		dir = __tb_property_parse_dir(block, block_len, entry->value,
    117					      entry->length, false);
    118		if (!dir) {
    119			kfree(property);
    120			return NULL;
    121		}
    122		property->value.dir = dir;
    123		break;
    124
    125	case TB_PROPERTY_TYPE_DATA:
    126		property->value.data = kcalloc(property->length, sizeof(u32),
    127					       GFP_KERNEL);
    128		if (!property->value.data) {
    129			kfree(property);
    130			return NULL;
    131		}
    132		parse_dwdata(property->value.data, block + entry->value,
    133			     entry->length);
    134		break;
    135
    136	case TB_PROPERTY_TYPE_TEXT:
    137		property->value.text = kcalloc(property->length, sizeof(u32),
    138					       GFP_KERNEL);
    139		if (!property->value.text) {
    140			kfree(property);
    141			return NULL;
    142		}
    143		parse_dwdata(property->value.text, block + entry->value,
    144			     entry->length);
    145		/* Force null termination */
    146		property->value.text[property->length * 4 - 1] = '\0';
    147		break;
    148
    149	case TB_PROPERTY_TYPE_VALUE:
    150		property->value.immediate = entry->value;
    151		break;
    152
    153	default:
    154		property->type = TB_PROPERTY_TYPE_UNKNOWN;
    155		break;
    156	}
    157
    158	return property;
    159}
    160
    161static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
    162	size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
    163{
    164	const struct tb_property_entry *entries;
    165	size_t i, content_len, nentries;
    166	unsigned int content_offset;
    167	struct tb_property_dir *dir;
    168
    169	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
    170	if (!dir)
    171		return NULL;
    172
    173	if (is_root) {
    174		content_offset = dir_offset + 2;
    175		content_len = dir_len;
    176	} else {
    177		dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
    178				    GFP_KERNEL);
    179		if (!dir->uuid) {
    180			tb_property_free_dir(dir);
    181			return NULL;
    182		}
    183		content_offset = dir_offset + 4;
    184		content_len = dir_len - 4; /* Length includes UUID */
    185	}
    186
    187	entries = (const struct tb_property_entry *)&block[content_offset];
    188	nentries = content_len / (sizeof(*entries) / 4);
    189
    190	INIT_LIST_HEAD(&dir->properties);
    191
    192	for (i = 0; i < nentries; i++) {
    193		struct tb_property *property;
    194
    195		property = tb_property_parse(block, block_len, &entries[i]);
    196		if (!property) {
    197			tb_property_free_dir(dir);
    198			return NULL;
    199		}
    200
    201		list_add_tail(&property->list, &dir->properties);
    202	}
    203
    204	return dir;
    205}
    206
    207/**
    208 * tb_property_parse_dir() - Parses properties from given property block
    209 * @block: Property block to parse
    210 * @block_len: Number of dword elements in the property block
    211 *
    212 * This function parses the XDomain properties data block into format that
    213 * can be traversed using the helper functions provided by this module.
    214 * Upon success returns the parsed directory. In case of error returns
    215 * %NULL. The resulting &struct tb_property_dir needs to be released by
    216 * calling tb_property_free_dir() when not needed anymore.
    217 *
    218 * The @block is expected to be root directory.
    219 */
    220struct tb_property_dir *tb_property_parse_dir(const u32 *block,
    221					      size_t block_len)
    222{
    223	const struct tb_property_rootdir_entry *rootdir =
    224		(const struct tb_property_rootdir_entry *)block;
    225
    226	if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
    227		return NULL;
    228	if (rootdir->length > block_len)
    229		return NULL;
    230
    231	return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
    232				       true);
    233}
    234
    235/**
    236 * tb_property_create_dir() - Creates new property directory
    237 * @uuid: UUID used to identify the particular directory
    238 *
    239 * Creates new, empty property directory. If @uuid is %NULL then the
    240 * directory is assumed to be root directory.
    241 */
    242struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
    243{
    244	struct tb_property_dir *dir;
    245
    246	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
    247	if (!dir)
    248		return NULL;
    249
    250	INIT_LIST_HEAD(&dir->properties);
    251	if (uuid) {
    252		dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
    253		if (!dir->uuid) {
    254			kfree(dir);
    255			return NULL;
    256		}
    257	}
    258
    259	return dir;
    260}
    261EXPORT_SYMBOL_GPL(tb_property_create_dir);
    262
    263static void tb_property_free(struct tb_property *property)
    264{
    265	switch (property->type) {
    266	case TB_PROPERTY_TYPE_DIRECTORY:
    267		tb_property_free_dir(property->value.dir);
    268		break;
    269
    270	case TB_PROPERTY_TYPE_DATA:
    271		kfree(property->value.data);
    272		break;
    273
    274	case TB_PROPERTY_TYPE_TEXT:
    275		kfree(property->value.text);
    276		break;
    277
    278	default:
    279		break;
    280	}
    281
    282	kfree(property);
    283}
    284
    285/**
    286 * tb_property_free_dir() - Release memory allocated for property directory
    287 * @dir: Directory to release
    288 *
    289 * This will release all the memory the directory occupies including all
    290 * descendants. It is OK to pass %NULL @dir, then the function does
    291 * nothing.
    292 */
    293void tb_property_free_dir(struct tb_property_dir *dir)
    294{
    295	struct tb_property *property, *tmp;
    296
    297	if (!dir)
    298		return;
    299
    300	list_for_each_entry_safe(property, tmp, &dir->properties, list) {
    301		list_del(&property->list);
    302		tb_property_free(property);
    303	}
    304	kfree(dir->uuid);
    305	kfree(dir);
    306}
    307EXPORT_SYMBOL_GPL(tb_property_free_dir);
    308
    309static size_t tb_property_dir_length(const struct tb_property_dir *dir,
    310				     bool recurse, size_t *data_len)
    311{
    312	const struct tb_property *property;
    313	size_t len = 0;
    314
    315	if (dir->uuid)
    316		len += sizeof(*dir->uuid) / 4;
    317	else
    318		len += sizeof(struct tb_property_rootdir_entry) / 4;
    319
    320	list_for_each_entry(property, &dir->properties, list) {
    321		len += sizeof(struct tb_property_entry) / 4;
    322
    323		switch (property->type) {
    324		case TB_PROPERTY_TYPE_DIRECTORY:
    325			if (recurse) {
    326				len += tb_property_dir_length(
    327					property->value.dir, recurse, data_len);
    328			}
    329			/* Reserve dword padding after each directory */
    330			if (data_len)
    331				*data_len += 1;
    332			break;
    333
    334		case TB_PROPERTY_TYPE_DATA:
    335		case TB_PROPERTY_TYPE_TEXT:
    336			if (data_len)
    337				*data_len += property->length;
    338			break;
    339
    340		default:
    341			break;
    342		}
    343	}
    344
    345	return len;
    346}
    347
    348static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
    349	u32 *block, unsigned int start_offset, size_t block_len)
    350{
    351	unsigned int data_offset, dir_end;
    352	const struct tb_property *property;
    353	struct tb_property_entry *entry;
    354	size_t dir_len, data_len = 0;
    355	int ret;
    356
    357	/*
    358	 * The structure of property block looks like following. Leaf
    359	 * data/text is included right after the directory and each
    360	 * directory follows each other (even nested ones).
    361	 *
    362	 * +----------+ <-- start_offset
    363	 * |  header  | <-- root directory header
    364	 * +----------+ ---
    365	 * |  entry 0 | -^--------------------.
    366	 * +----------+  |                    |
    367	 * |  entry 1 | -|--------------------|--.
    368	 * +----------+  |                    |  |
    369	 * |  entry 2 | -|-----------------.  |  |
    370	 * +----------+  |                 |  |  |
    371	 * :          :  |  dir_len        |  |  |
    372	 * .          .  |                 |  |  |
    373	 * :          :  |                 |  |  |
    374	 * +----------+  |                 |  |  |
    375	 * |  entry n |  v                 |  |  |
    376	 * +----------+ <-- data_offset    |  |  |
    377	 * |  data 0  | <------------------|--'  |
    378	 * +----------+                    |     |
    379	 * |  data 1  | <------------------|-----'
    380	 * +----------+                    |
    381	 * | 00000000 | padding            |
    382	 * +----------+ <-- dir_end <------'
    383	 * |   UUID   | <-- directory UUID (child directory)
    384	 * +----------+
    385	 * |  entry 0 |
    386	 * +----------+
    387	 * |  entry 1 |
    388	 * +----------+
    389	 * :          :
    390	 * .          .
    391	 * :          :
    392	 * +----------+
    393	 * |  entry n |
    394	 * +----------+
    395	 * |  data 0  |
    396	 * +----------+
    397	 *
    398	 * We use dir_end to hold pointer to the end of the directory. It
    399	 * will increase as we add directories and each directory should be
    400	 * added starting from previous dir_end.
    401	 */
    402	dir_len = tb_property_dir_length(dir, false, &data_len);
    403	data_offset = start_offset + dir_len;
    404	dir_end = start_offset + data_len + dir_len;
    405
    406	if (data_offset > dir_end)
    407		return -EINVAL;
    408	if (dir_end > block_len)
    409		return -EINVAL;
    410
    411	/* Write headers first */
    412	if (dir->uuid) {
    413		struct tb_property_dir_entry *pe;
    414
    415		pe = (struct tb_property_dir_entry *)&block[start_offset];
    416		memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
    417		entry = pe->entries;
    418	} else {
    419		struct tb_property_rootdir_entry *re;
    420
    421		re = (struct tb_property_rootdir_entry *)&block[start_offset];
    422		re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
    423		re->length = dir_len - sizeof(*re) / 4;
    424		entry = re->entries;
    425	}
    426
    427	list_for_each_entry(property, &dir->properties, list) {
    428		const struct tb_property_dir *child;
    429
    430		format_dwdata(entry, property->key, 2);
    431		entry->type = property->type;
    432
    433		switch (property->type) {
    434		case TB_PROPERTY_TYPE_DIRECTORY:
    435			child = property->value.dir;
    436			ret = __tb_property_format_dir(child, block, dir_end,
    437						       block_len);
    438			if (ret < 0)
    439				return ret;
    440			entry->length = tb_property_dir_length(child, false,
    441							       NULL);
    442			entry->value = dir_end;
    443			dir_end = ret;
    444			break;
    445
    446		case TB_PROPERTY_TYPE_DATA:
    447			format_dwdata(&block[data_offset], property->value.data,
    448				      property->length);
    449			entry->length = property->length;
    450			entry->value = data_offset;
    451			data_offset += entry->length;
    452			break;
    453
    454		case TB_PROPERTY_TYPE_TEXT:
    455			format_dwdata(&block[data_offset], property->value.text,
    456				      property->length);
    457			entry->length = property->length;
    458			entry->value = data_offset;
    459			data_offset += entry->length;
    460			break;
    461
    462		case TB_PROPERTY_TYPE_VALUE:
    463			entry->length = property->length;
    464			entry->value = property->value.immediate;
    465			break;
    466
    467		default:
    468			break;
    469		}
    470
    471		entry++;
    472	}
    473
    474	return dir_end;
    475}
    476
    477/**
    478 * tb_property_format_dir() - Formats directory to the packed XDomain format
    479 * @dir: Directory to format
    480 * @block: Property block where the packed data is placed
    481 * @block_len: Length of the property block
    482 *
    483 * This function formats the directory to the packed format that can be
    484 * then send over the thunderbolt fabric to receiving host. Returns %0 in
    485 * case of success and negative errno on faulure. Passing %NULL in @block
    486 * returns number of entries the block takes.
    487 */
    488ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
    489			       size_t block_len)
    490{
    491	ssize_t ret;
    492
    493	if (!block) {
    494		size_t dir_len, data_len = 0;
    495
    496		dir_len = tb_property_dir_length(dir, true, &data_len);
    497		return dir_len + data_len;
    498	}
    499
    500	ret = __tb_property_format_dir(dir, block, 0, block_len);
    501	return ret < 0 ? ret : 0;
    502}
    503
    504/**
    505 * tb_property_copy_dir() - Take a deep copy of directory
    506 * @dir: Directory to copy
    507 *
    508 * This function takes a deep copy of @dir and returns back the copy. In
    509 * case of error returns %NULL. The resulting directory needs to be
    510 * released by calling tb_property_free_dir().
    511 */
    512struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
    513{
    514	struct tb_property *property, *p = NULL;
    515	struct tb_property_dir *d;
    516
    517	if (!dir)
    518		return NULL;
    519
    520	d = tb_property_create_dir(dir->uuid);
    521	if (!d)
    522		return NULL;
    523
    524	list_for_each_entry(property, &dir->properties, list) {
    525		struct tb_property *p;
    526
    527		p = tb_property_alloc(property->key, property->type);
    528		if (!p)
    529			goto err_free;
    530
    531		p->length = property->length;
    532
    533		switch (property->type) {
    534		case TB_PROPERTY_TYPE_DIRECTORY:
    535			p->value.dir = tb_property_copy_dir(property->value.dir);
    536			if (!p->value.dir)
    537				goto err_free;
    538			break;
    539
    540		case TB_PROPERTY_TYPE_DATA:
    541			p->value.data = kmemdup(property->value.data,
    542						property->length * 4,
    543						GFP_KERNEL);
    544			if (!p->value.data)
    545				goto err_free;
    546			break;
    547
    548		case TB_PROPERTY_TYPE_TEXT:
    549			p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
    550			if (!p->value.text)
    551				goto err_free;
    552			strcpy(p->value.text, property->value.text);
    553			break;
    554
    555		case TB_PROPERTY_TYPE_VALUE:
    556			p->value.immediate = property->value.immediate;
    557			break;
    558
    559		default:
    560			break;
    561		}
    562
    563		list_add_tail(&p->list, &d->properties);
    564	}
    565
    566	return d;
    567
    568err_free:
    569	kfree(p);
    570	tb_property_free_dir(d);
    571
    572	return NULL;
    573}
    574
    575/**
    576 * tb_property_add_immediate() - Add immediate property to directory
    577 * @parent: Directory to add the property
    578 * @key: Key for the property
    579 * @value: Immediate value to store with the property
    580 */
    581int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
    582			      u32 value)
    583{
    584	struct tb_property *property;
    585
    586	if (!tb_property_key_valid(key))
    587		return -EINVAL;
    588
    589	property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
    590	if (!property)
    591		return -ENOMEM;
    592
    593	property->length = 1;
    594	property->value.immediate = value;
    595
    596	list_add_tail(&property->list, &parent->properties);
    597	return 0;
    598}
    599EXPORT_SYMBOL_GPL(tb_property_add_immediate);
    600
    601/**
    602 * tb_property_add_data() - Adds arbitrary data property to directory
    603 * @parent: Directory to add the property
    604 * @key: Key for the property
    605 * @buf: Data buffer to add
    606 * @buflen: Number of bytes in the data buffer
    607 *
    608 * Function takes a copy of @buf and adds it to the directory.
    609 */
    610int tb_property_add_data(struct tb_property_dir *parent, const char *key,
    611			 const void *buf, size_t buflen)
    612{
    613	/* Need to pad to dword boundary */
    614	size_t size = round_up(buflen, 4);
    615	struct tb_property *property;
    616
    617	if (!tb_property_key_valid(key))
    618		return -EINVAL;
    619
    620	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
    621	if (!property)
    622		return -ENOMEM;
    623
    624	property->length = size / 4;
    625	property->value.data = kzalloc(size, GFP_KERNEL);
    626	if (!property->value.data) {
    627		kfree(property);
    628		return -ENOMEM;
    629	}
    630
    631	memcpy(property->value.data, buf, buflen);
    632
    633	list_add_tail(&property->list, &parent->properties);
    634	return 0;
    635}
    636EXPORT_SYMBOL_GPL(tb_property_add_data);
    637
    638/**
    639 * tb_property_add_text() - Adds string property to directory
    640 * @parent: Directory to add the property
    641 * @key: Key for the property
    642 * @text: String to add
    643 *
    644 * Function takes a copy of @text and adds it to the directory.
    645 */
    646int tb_property_add_text(struct tb_property_dir *parent, const char *key,
    647			 const char *text)
    648{
    649	/* Need to pad to dword boundary */
    650	size_t size = round_up(strlen(text) + 1, 4);
    651	struct tb_property *property;
    652
    653	if (!tb_property_key_valid(key))
    654		return -EINVAL;
    655
    656	property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
    657	if (!property)
    658		return -ENOMEM;
    659
    660	property->length = size / 4;
    661	property->value.text = kzalloc(size, GFP_KERNEL);
    662	if (!property->value.text) {
    663		kfree(property);
    664		return -ENOMEM;
    665	}
    666
    667	strcpy(property->value.text, text);
    668
    669	list_add_tail(&property->list, &parent->properties);
    670	return 0;
    671}
    672EXPORT_SYMBOL_GPL(tb_property_add_text);
    673
    674/**
    675 * tb_property_add_dir() - Adds a directory to the parent directory
    676 * @parent: Directory to add the property
    677 * @key: Key for the property
    678 * @dir: Directory to add
    679 */
    680int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
    681			struct tb_property_dir *dir)
    682{
    683	struct tb_property *property;
    684
    685	if (!tb_property_key_valid(key))
    686		return -EINVAL;
    687
    688	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
    689	if (!property)
    690		return -ENOMEM;
    691
    692	property->value.dir = dir;
    693
    694	list_add_tail(&property->list, &parent->properties);
    695	return 0;
    696}
    697EXPORT_SYMBOL_GPL(tb_property_add_dir);
    698
    699/**
    700 * tb_property_remove() - Removes property from a parent directory
    701 * @property: Property to remove
    702 *
    703 * Note memory for @property is released as well so it is not allowed to
    704 * touch the object after call to this function.
    705 */
    706void tb_property_remove(struct tb_property *property)
    707{
    708	list_del(&property->list);
    709	kfree(property);
    710}
    711EXPORT_SYMBOL_GPL(tb_property_remove);
    712
    713/**
    714 * tb_property_find() - Find a property from a directory
    715 * @dir: Directory where the property is searched
    716 * @key: Key to look for
    717 * @type: Type of the property
    718 *
    719 * Finds and returns property from the given directory. Does not recurse
    720 * into sub-directories. Returns %NULL if the property was not found.
    721 */
    722struct tb_property *tb_property_find(struct tb_property_dir *dir,
    723	const char *key, enum tb_property_type type)
    724{
    725	struct tb_property *property;
    726
    727	list_for_each_entry(property, &dir->properties, list) {
    728		if (property->type == type && !strcmp(property->key, key))
    729			return property;
    730	}
    731
    732	return NULL;
    733}
    734EXPORT_SYMBOL_GPL(tb_property_find);
    735
    736/**
    737 * tb_property_get_next() - Get next property from directory
    738 * @dir: Directory holding properties
    739 * @prev: Previous property in the directory (%NULL returns the first)
    740 */
    741struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
    742					 struct tb_property *prev)
    743{
    744	if (prev) {
    745		if (list_is_last(&prev->list, &dir->properties))
    746			return NULL;
    747		return list_next_entry(prev, list);
    748	}
    749	return list_first_entry_or_null(&dir->properties, struct tb_property,
    750					list);
    751}
    752EXPORT_SYMBOL_GPL(tb_property_get_next);