162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Thunderbolt XDomain property support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation
662306a36Sopenharmony_ci * Authors: Michael Jamet <michael.jamet@intel.com>
762306a36Sopenharmony_ci *          Mika Westerberg <mika.westerberg@linux.intel.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/uuid.h>
1462306a36Sopenharmony_ci#include <linux/thunderbolt.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct tb_property_entry {
1762306a36Sopenharmony_ci	u32 key_hi;
1862306a36Sopenharmony_ci	u32 key_lo;
1962306a36Sopenharmony_ci	u16 length;
2062306a36Sopenharmony_ci	u8 reserved;
2162306a36Sopenharmony_ci	u8 type;
2262306a36Sopenharmony_ci	u32 value;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct tb_property_rootdir_entry {
2662306a36Sopenharmony_ci	u32 magic;
2762306a36Sopenharmony_ci	u32 length;
2862306a36Sopenharmony_ci	struct tb_property_entry entries[];
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct tb_property_dir_entry {
3262306a36Sopenharmony_ci	u32 uuid[4];
3362306a36Sopenharmony_ci	struct tb_property_entry entries[];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define TB_PROPERTY_ROOTDIR_MAGIC	0x55584401
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
3962306a36Sopenharmony_ci	size_t block_len, unsigned int dir_offset, size_t dir_len,
4062306a36Sopenharmony_ci	bool is_root);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline void parse_dwdata(void *dst, const void *src, size_t dwords)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	be32_to_cpu_array(dst, src, dwords);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline void format_dwdata(void *dst, const void *src, size_t dwords)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	cpu_to_be32_array(dst, src, dwords);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic bool tb_property_entry_valid(const struct tb_property_entry *entry,
5362306a36Sopenharmony_ci				  size_t block_len)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	switch (entry->type) {
5662306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DIRECTORY:
5762306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DATA:
5862306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_TEXT:
5962306a36Sopenharmony_ci		if (entry->length > block_len)
6062306a36Sopenharmony_ci			return false;
6162306a36Sopenharmony_ci		if (entry->value + entry->length > block_len)
6262306a36Sopenharmony_ci			return false;
6362306a36Sopenharmony_ci		break;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_VALUE:
6662306a36Sopenharmony_ci		if (entry->length != 1)
6762306a36Sopenharmony_ci			return false;
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return true;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic bool tb_property_key_valid(const char *key)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct tb_property *
8062306a36Sopenharmony_citb_property_alloc(const char *key, enum tb_property_type type)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct tb_property *property;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	property = kzalloc(sizeof(*property), GFP_KERNEL);
8562306a36Sopenharmony_ci	if (!property)
8662306a36Sopenharmony_ci		return NULL;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	strcpy(property->key, key);
8962306a36Sopenharmony_ci	property->type = type;
9062306a36Sopenharmony_ci	INIT_LIST_HEAD(&property->list);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return property;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
9662306a36Sopenharmony_ci					const struct tb_property_entry *entry)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	char key[TB_PROPERTY_KEY_SIZE + 1];
9962306a36Sopenharmony_ci	struct tb_property *property;
10062306a36Sopenharmony_ci	struct tb_property_dir *dir;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (!tb_property_entry_valid(entry, block_len))
10362306a36Sopenharmony_ci		return NULL;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	parse_dwdata(key, entry, 2);
10662306a36Sopenharmony_ci	key[TB_PROPERTY_KEY_SIZE] = '\0';
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	property = tb_property_alloc(key, entry->type);
10962306a36Sopenharmony_ci	if (!property)
11062306a36Sopenharmony_ci		return NULL;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	property->length = entry->length;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	switch (property->type) {
11562306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DIRECTORY:
11662306a36Sopenharmony_ci		dir = __tb_property_parse_dir(block, block_len, entry->value,
11762306a36Sopenharmony_ci					      entry->length, false);
11862306a36Sopenharmony_ci		if (!dir) {
11962306a36Sopenharmony_ci			kfree(property);
12062306a36Sopenharmony_ci			return NULL;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci		property->value.dir = dir;
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DATA:
12662306a36Sopenharmony_ci		property->value.data = kcalloc(property->length, sizeof(u32),
12762306a36Sopenharmony_ci					       GFP_KERNEL);
12862306a36Sopenharmony_ci		if (!property->value.data) {
12962306a36Sopenharmony_ci			kfree(property);
13062306a36Sopenharmony_ci			return NULL;
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci		parse_dwdata(property->value.data, block + entry->value,
13362306a36Sopenharmony_ci			     entry->length);
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_TEXT:
13762306a36Sopenharmony_ci		property->value.text = kcalloc(property->length, sizeof(u32),
13862306a36Sopenharmony_ci					       GFP_KERNEL);
13962306a36Sopenharmony_ci		if (!property->value.text) {
14062306a36Sopenharmony_ci			kfree(property);
14162306a36Sopenharmony_ci			return NULL;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		parse_dwdata(property->value.text, block + entry->value,
14462306a36Sopenharmony_ci			     entry->length);
14562306a36Sopenharmony_ci		/* Force null termination */
14662306a36Sopenharmony_ci		property->value.text[property->length * 4 - 1] = '\0';
14762306a36Sopenharmony_ci		break;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_VALUE:
15062306a36Sopenharmony_ci		property->value.immediate = entry->value;
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	default:
15462306a36Sopenharmony_ci		property->type = TB_PROPERTY_TYPE_UNKNOWN;
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return property;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
16262306a36Sopenharmony_ci	size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	const struct tb_property_entry *entries;
16562306a36Sopenharmony_ci	size_t i, content_len, nentries;
16662306a36Sopenharmony_ci	unsigned int content_offset;
16762306a36Sopenharmony_ci	struct tb_property_dir *dir;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
17062306a36Sopenharmony_ci	if (!dir)
17162306a36Sopenharmony_ci		return NULL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (is_root) {
17462306a36Sopenharmony_ci		content_offset = dir_offset + 2;
17562306a36Sopenharmony_ci		content_len = dir_len;
17662306a36Sopenharmony_ci	} else {
17762306a36Sopenharmony_ci		dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
17862306a36Sopenharmony_ci				    GFP_KERNEL);
17962306a36Sopenharmony_ci		if (!dir->uuid) {
18062306a36Sopenharmony_ci			tb_property_free_dir(dir);
18162306a36Sopenharmony_ci			return NULL;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci		content_offset = dir_offset + 4;
18462306a36Sopenharmony_ci		content_len = dir_len - 4; /* Length includes UUID */
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	entries = (const struct tb_property_entry *)&block[content_offset];
18862306a36Sopenharmony_ci	nentries = content_len / (sizeof(*entries) / 4);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	INIT_LIST_HEAD(&dir->properties);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (i = 0; i < nentries; i++) {
19362306a36Sopenharmony_ci		struct tb_property *property;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		property = tb_property_parse(block, block_len, &entries[i]);
19662306a36Sopenharmony_ci		if (!property) {
19762306a36Sopenharmony_ci			tb_property_free_dir(dir);
19862306a36Sopenharmony_ci			return NULL;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		list_add_tail(&property->list, &dir->properties);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return dir;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/**
20862306a36Sopenharmony_ci * tb_property_parse_dir() - Parses properties from given property block
20962306a36Sopenharmony_ci * @block: Property block to parse
21062306a36Sopenharmony_ci * @block_len: Number of dword elements in the property block
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * This function parses the XDomain properties data block into format that
21362306a36Sopenharmony_ci * can be traversed using the helper functions provided by this module.
21462306a36Sopenharmony_ci * Upon success returns the parsed directory. In case of error returns
21562306a36Sopenharmony_ci * %NULL. The resulting &struct tb_property_dir needs to be released by
21662306a36Sopenharmony_ci * calling tb_property_free_dir() when not needed anymore.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * The @block is expected to be root directory.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_cistruct tb_property_dir *tb_property_parse_dir(const u32 *block,
22162306a36Sopenharmony_ci					      size_t block_len)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	const struct tb_property_rootdir_entry *rootdir =
22462306a36Sopenharmony_ci		(const struct tb_property_rootdir_entry *)block;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
22762306a36Sopenharmony_ci		return NULL;
22862306a36Sopenharmony_ci	if (rootdir->length > block_len)
22962306a36Sopenharmony_ci		return NULL;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
23262306a36Sopenharmony_ci				       true);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/**
23662306a36Sopenharmony_ci * tb_property_create_dir() - Creates new property directory
23762306a36Sopenharmony_ci * @uuid: UUID used to identify the particular directory
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * Creates new, empty property directory. If @uuid is %NULL then the
24062306a36Sopenharmony_ci * directory is assumed to be root directory.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistruct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct tb_property_dir *dir;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
24762306a36Sopenharmony_ci	if (!dir)
24862306a36Sopenharmony_ci		return NULL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	INIT_LIST_HEAD(&dir->properties);
25162306a36Sopenharmony_ci	if (uuid) {
25262306a36Sopenharmony_ci		dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
25362306a36Sopenharmony_ci		if (!dir->uuid) {
25462306a36Sopenharmony_ci			kfree(dir);
25562306a36Sopenharmony_ci			return NULL;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return dir;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_create_dir);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void tb_property_free(struct tb_property *property)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	switch (property->type) {
26662306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DIRECTORY:
26762306a36Sopenharmony_ci		tb_property_free_dir(property->value.dir);
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_DATA:
27162306a36Sopenharmony_ci		kfree(property->value.data);
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	case TB_PROPERTY_TYPE_TEXT:
27562306a36Sopenharmony_ci		kfree(property->value.text);
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	default:
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	kfree(property);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * tb_property_free_dir() - Release memory allocated for property directory
28762306a36Sopenharmony_ci * @dir: Directory to release
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * This will release all the memory the directory occupies including all
29062306a36Sopenharmony_ci * descendants. It is OK to pass %NULL @dir, then the function does
29162306a36Sopenharmony_ci * nothing.
29262306a36Sopenharmony_ci */
29362306a36Sopenharmony_civoid tb_property_free_dir(struct tb_property_dir *dir)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct tb_property *property, *tmp;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (!dir)
29862306a36Sopenharmony_ci		return;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	list_for_each_entry_safe(property, tmp, &dir->properties, list) {
30162306a36Sopenharmony_ci		list_del(&property->list);
30262306a36Sopenharmony_ci		tb_property_free(property);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci	kfree(dir->uuid);
30562306a36Sopenharmony_ci	kfree(dir);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_free_dir);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic size_t tb_property_dir_length(const struct tb_property_dir *dir,
31062306a36Sopenharmony_ci				     bool recurse, size_t *data_len)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	const struct tb_property *property;
31362306a36Sopenharmony_ci	size_t len = 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (dir->uuid)
31662306a36Sopenharmony_ci		len += sizeof(*dir->uuid) / 4;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		len += sizeof(struct tb_property_rootdir_entry) / 4;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	list_for_each_entry(property, &dir->properties, list) {
32162306a36Sopenharmony_ci		len += sizeof(struct tb_property_entry) / 4;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		switch (property->type) {
32462306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DIRECTORY:
32562306a36Sopenharmony_ci			if (recurse) {
32662306a36Sopenharmony_ci				len += tb_property_dir_length(
32762306a36Sopenharmony_ci					property->value.dir, recurse, data_len);
32862306a36Sopenharmony_ci			}
32962306a36Sopenharmony_ci			/* Reserve dword padding after each directory */
33062306a36Sopenharmony_ci			if (data_len)
33162306a36Sopenharmony_ci				*data_len += 1;
33262306a36Sopenharmony_ci			break;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DATA:
33562306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_TEXT:
33662306a36Sopenharmony_ci			if (data_len)
33762306a36Sopenharmony_ci				*data_len += property->length;
33862306a36Sopenharmony_ci			break;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		default:
34162306a36Sopenharmony_ci			break;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return len;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
34962306a36Sopenharmony_ci	u32 *block, unsigned int start_offset, size_t block_len)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	unsigned int data_offset, dir_end;
35262306a36Sopenharmony_ci	const struct tb_property *property;
35362306a36Sopenharmony_ci	struct tb_property_entry *entry;
35462306a36Sopenharmony_ci	size_t dir_len, data_len = 0;
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * The structure of property block looks like following. Leaf
35962306a36Sopenharmony_ci	 * data/text is included right after the directory and each
36062306a36Sopenharmony_ci	 * directory follows each other (even nested ones).
36162306a36Sopenharmony_ci	 *
36262306a36Sopenharmony_ci	 * +----------+ <-- start_offset
36362306a36Sopenharmony_ci	 * |  header  | <-- root directory header
36462306a36Sopenharmony_ci	 * +----------+ ---
36562306a36Sopenharmony_ci	 * |  entry 0 | -^--------------------.
36662306a36Sopenharmony_ci	 * +----------+  |                    |
36762306a36Sopenharmony_ci	 * |  entry 1 | -|--------------------|--.
36862306a36Sopenharmony_ci	 * +----------+  |                    |  |
36962306a36Sopenharmony_ci	 * |  entry 2 | -|-----------------.  |  |
37062306a36Sopenharmony_ci	 * +----------+  |                 |  |  |
37162306a36Sopenharmony_ci	 * :          :  |  dir_len        |  |  |
37262306a36Sopenharmony_ci	 * .          .  |                 |  |  |
37362306a36Sopenharmony_ci	 * :          :  |                 |  |  |
37462306a36Sopenharmony_ci	 * +----------+  |                 |  |  |
37562306a36Sopenharmony_ci	 * |  entry n |  v                 |  |  |
37662306a36Sopenharmony_ci	 * +----------+ <-- data_offset    |  |  |
37762306a36Sopenharmony_ci	 * |  data 0  | <------------------|--'  |
37862306a36Sopenharmony_ci	 * +----------+                    |     |
37962306a36Sopenharmony_ci	 * |  data 1  | <------------------|-----'
38062306a36Sopenharmony_ci	 * +----------+                    |
38162306a36Sopenharmony_ci	 * | 00000000 | padding            |
38262306a36Sopenharmony_ci	 * +----------+ <-- dir_end <------'
38362306a36Sopenharmony_ci	 * |   UUID   | <-- directory UUID (child directory)
38462306a36Sopenharmony_ci	 * +----------+
38562306a36Sopenharmony_ci	 * |  entry 0 |
38662306a36Sopenharmony_ci	 * +----------+
38762306a36Sopenharmony_ci	 * |  entry 1 |
38862306a36Sopenharmony_ci	 * +----------+
38962306a36Sopenharmony_ci	 * :          :
39062306a36Sopenharmony_ci	 * .          .
39162306a36Sopenharmony_ci	 * :          :
39262306a36Sopenharmony_ci	 * +----------+
39362306a36Sopenharmony_ci	 * |  entry n |
39462306a36Sopenharmony_ci	 * +----------+
39562306a36Sopenharmony_ci	 * |  data 0  |
39662306a36Sopenharmony_ci	 * +----------+
39762306a36Sopenharmony_ci	 *
39862306a36Sopenharmony_ci	 * We use dir_end to hold pointer to the end of the directory. It
39962306a36Sopenharmony_ci	 * will increase as we add directories and each directory should be
40062306a36Sopenharmony_ci	 * added starting from previous dir_end.
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	dir_len = tb_property_dir_length(dir, false, &data_len);
40362306a36Sopenharmony_ci	data_offset = start_offset + dir_len;
40462306a36Sopenharmony_ci	dir_end = start_offset + data_len + dir_len;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (data_offset > dir_end)
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci	if (dir_end > block_len)
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Write headers first */
41262306a36Sopenharmony_ci	if (dir->uuid) {
41362306a36Sopenharmony_ci		struct tb_property_dir_entry *pe;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		pe = (struct tb_property_dir_entry *)&block[start_offset];
41662306a36Sopenharmony_ci		memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
41762306a36Sopenharmony_ci		entry = pe->entries;
41862306a36Sopenharmony_ci	} else {
41962306a36Sopenharmony_ci		struct tb_property_rootdir_entry *re;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		re = (struct tb_property_rootdir_entry *)&block[start_offset];
42262306a36Sopenharmony_ci		re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
42362306a36Sopenharmony_ci		re->length = dir_len - sizeof(*re) / 4;
42462306a36Sopenharmony_ci		entry = re->entries;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	list_for_each_entry(property, &dir->properties, list) {
42862306a36Sopenharmony_ci		const struct tb_property_dir *child;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		format_dwdata(entry, property->key, 2);
43162306a36Sopenharmony_ci		entry->type = property->type;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		switch (property->type) {
43462306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DIRECTORY:
43562306a36Sopenharmony_ci			child = property->value.dir;
43662306a36Sopenharmony_ci			ret = __tb_property_format_dir(child, block, dir_end,
43762306a36Sopenharmony_ci						       block_len);
43862306a36Sopenharmony_ci			if (ret < 0)
43962306a36Sopenharmony_ci				return ret;
44062306a36Sopenharmony_ci			entry->length = tb_property_dir_length(child, false,
44162306a36Sopenharmony_ci							       NULL);
44262306a36Sopenharmony_ci			entry->value = dir_end;
44362306a36Sopenharmony_ci			dir_end = ret;
44462306a36Sopenharmony_ci			break;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DATA:
44762306a36Sopenharmony_ci			format_dwdata(&block[data_offset], property->value.data,
44862306a36Sopenharmony_ci				      property->length);
44962306a36Sopenharmony_ci			entry->length = property->length;
45062306a36Sopenharmony_ci			entry->value = data_offset;
45162306a36Sopenharmony_ci			data_offset += entry->length;
45262306a36Sopenharmony_ci			break;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_TEXT:
45562306a36Sopenharmony_ci			format_dwdata(&block[data_offset], property->value.text,
45662306a36Sopenharmony_ci				      property->length);
45762306a36Sopenharmony_ci			entry->length = property->length;
45862306a36Sopenharmony_ci			entry->value = data_offset;
45962306a36Sopenharmony_ci			data_offset += entry->length;
46062306a36Sopenharmony_ci			break;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_VALUE:
46362306a36Sopenharmony_ci			entry->length = property->length;
46462306a36Sopenharmony_ci			entry->value = property->value.immediate;
46562306a36Sopenharmony_ci			break;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		default:
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		entry++;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return dir_end;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/**
47862306a36Sopenharmony_ci * tb_property_format_dir() - Formats directory to the packed XDomain format
47962306a36Sopenharmony_ci * @dir: Directory to format
48062306a36Sopenharmony_ci * @block: Property block where the packed data is placed
48162306a36Sopenharmony_ci * @block_len: Length of the property block
48262306a36Sopenharmony_ci *
48362306a36Sopenharmony_ci * This function formats the directory to the packed format that can be
48462306a36Sopenharmony_ci * then send over the thunderbolt fabric to receiving host. Returns %0 in
48562306a36Sopenharmony_ci * case of success and negative errno on faulure. Passing %NULL in @block
48662306a36Sopenharmony_ci * returns number of entries the block takes.
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_cissize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
48962306a36Sopenharmony_ci			       size_t block_len)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	ssize_t ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (!block) {
49462306a36Sopenharmony_ci		size_t dir_len, data_len = 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		dir_len = tb_property_dir_length(dir, true, &data_len);
49762306a36Sopenharmony_ci		return dir_len + data_len;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	ret = __tb_property_format_dir(dir, block, 0, block_len);
50162306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * tb_property_copy_dir() - Take a deep copy of directory
50662306a36Sopenharmony_ci * @dir: Directory to copy
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * This function takes a deep copy of @dir and returns back the copy. In
50962306a36Sopenharmony_ci * case of error returns %NULL. The resulting directory needs to be
51062306a36Sopenharmony_ci * released by calling tb_property_free_dir().
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_cistruct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct tb_property *property, *p = NULL;
51562306a36Sopenharmony_ci	struct tb_property_dir *d;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (!dir)
51862306a36Sopenharmony_ci		return NULL;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	d = tb_property_create_dir(dir->uuid);
52162306a36Sopenharmony_ci	if (!d)
52262306a36Sopenharmony_ci		return NULL;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	list_for_each_entry(property, &dir->properties, list) {
52562306a36Sopenharmony_ci		struct tb_property *p;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		p = tb_property_alloc(property->key, property->type);
52862306a36Sopenharmony_ci		if (!p)
52962306a36Sopenharmony_ci			goto err_free;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		p->length = property->length;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		switch (property->type) {
53462306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DIRECTORY:
53562306a36Sopenharmony_ci			p->value.dir = tb_property_copy_dir(property->value.dir);
53662306a36Sopenharmony_ci			if (!p->value.dir)
53762306a36Sopenharmony_ci				goto err_free;
53862306a36Sopenharmony_ci			break;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_DATA:
54162306a36Sopenharmony_ci			p->value.data = kmemdup(property->value.data,
54262306a36Sopenharmony_ci						property->length * 4,
54362306a36Sopenharmony_ci						GFP_KERNEL);
54462306a36Sopenharmony_ci			if (!p->value.data)
54562306a36Sopenharmony_ci				goto err_free;
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_TEXT:
54962306a36Sopenharmony_ci			p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
55062306a36Sopenharmony_ci			if (!p->value.text)
55162306a36Sopenharmony_ci				goto err_free;
55262306a36Sopenharmony_ci			strcpy(p->value.text, property->value.text);
55362306a36Sopenharmony_ci			break;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		case TB_PROPERTY_TYPE_VALUE:
55662306a36Sopenharmony_ci			p->value.immediate = property->value.immediate;
55762306a36Sopenharmony_ci			break;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		default:
56062306a36Sopenharmony_ci			break;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		list_add_tail(&p->list, &d->properties);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return d;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cierr_free:
56962306a36Sopenharmony_ci	kfree(p);
57062306a36Sopenharmony_ci	tb_property_free_dir(d);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return NULL;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/**
57662306a36Sopenharmony_ci * tb_property_add_immediate() - Add immediate property to directory
57762306a36Sopenharmony_ci * @parent: Directory to add the property
57862306a36Sopenharmony_ci * @key: Key for the property
57962306a36Sopenharmony_ci * @value: Immediate value to store with the property
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ciint tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
58262306a36Sopenharmony_ci			      u32 value)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct tb_property *property;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (!tb_property_key_valid(key))
58762306a36Sopenharmony_ci		return -EINVAL;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
59062306a36Sopenharmony_ci	if (!property)
59162306a36Sopenharmony_ci		return -ENOMEM;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	property->length = 1;
59462306a36Sopenharmony_ci	property->value.immediate = value;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	list_add_tail(&property->list, &parent->properties);
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_immediate);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci/**
60262306a36Sopenharmony_ci * tb_property_add_data() - Adds arbitrary data property to directory
60362306a36Sopenharmony_ci * @parent: Directory to add the property
60462306a36Sopenharmony_ci * @key: Key for the property
60562306a36Sopenharmony_ci * @buf: Data buffer to add
60662306a36Sopenharmony_ci * @buflen: Number of bytes in the data buffer
60762306a36Sopenharmony_ci *
60862306a36Sopenharmony_ci * Function takes a copy of @buf and adds it to the directory.
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_ciint tb_property_add_data(struct tb_property_dir *parent, const char *key,
61162306a36Sopenharmony_ci			 const void *buf, size_t buflen)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	/* Need to pad to dword boundary */
61462306a36Sopenharmony_ci	size_t size = round_up(buflen, 4);
61562306a36Sopenharmony_ci	struct tb_property *property;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (!tb_property_key_valid(key))
61862306a36Sopenharmony_ci		return -EINVAL;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
62162306a36Sopenharmony_ci	if (!property)
62262306a36Sopenharmony_ci		return -ENOMEM;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	property->length = size / 4;
62562306a36Sopenharmony_ci	property->value.data = kzalloc(size, GFP_KERNEL);
62662306a36Sopenharmony_ci	if (!property->value.data) {
62762306a36Sopenharmony_ci		kfree(property);
62862306a36Sopenharmony_ci		return -ENOMEM;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	memcpy(property->value.data, buf, buflen);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	list_add_tail(&property->list, &parent->properties);
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_data);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * tb_property_add_text() - Adds string property to directory
64062306a36Sopenharmony_ci * @parent: Directory to add the property
64162306a36Sopenharmony_ci * @key: Key for the property
64262306a36Sopenharmony_ci * @text: String to add
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Function takes a copy of @text and adds it to the directory.
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_ciint tb_property_add_text(struct tb_property_dir *parent, const char *key,
64762306a36Sopenharmony_ci			 const char *text)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	/* Need to pad to dword boundary */
65062306a36Sopenharmony_ci	size_t size = round_up(strlen(text) + 1, 4);
65162306a36Sopenharmony_ci	struct tb_property *property;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (!tb_property_key_valid(key))
65462306a36Sopenharmony_ci		return -EINVAL;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
65762306a36Sopenharmony_ci	if (!property)
65862306a36Sopenharmony_ci		return -ENOMEM;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	property->length = size / 4;
66162306a36Sopenharmony_ci	property->value.text = kzalloc(size, GFP_KERNEL);
66262306a36Sopenharmony_ci	if (!property->value.text) {
66362306a36Sopenharmony_ci		kfree(property);
66462306a36Sopenharmony_ci		return -ENOMEM;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	strcpy(property->value.text, text);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	list_add_tail(&property->list, &parent->properties);
67062306a36Sopenharmony_ci	return 0;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_text);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/**
67562306a36Sopenharmony_ci * tb_property_add_dir() - Adds a directory to the parent directory
67662306a36Sopenharmony_ci * @parent: Directory to add the property
67762306a36Sopenharmony_ci * @key: Key for the property
67862306a36Sopenharmony_ci * @dir: Directory to add
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_ciint tb_property_add_dir(struct tb_property_dir *parent, const char *key,
68162306a36Sopenharmony_ci			struct tb_property_dir *dir)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct tb_property *property;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (!tb_property_key_valid(key))
68662306a36Sopenharmony_ci		return -EINVAL;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
68962306a36Sopenharmony_ci	if (!property)
69062306a36Sopenharmony_ci		return -ENOMEM;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	property->value.dir = dir;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	list_add_tail(&property->list, &parent->properties);
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_add_dir);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/**
70062306a36Sopenharmony_ci * tb_property_remove() - Removes property from a parent directory
70162306a36Sopenharmony_ci * @property: Property to remove
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * Note memory for @property is released as well so it is not allowed to
70462306a36Sopenharmony_ci * touch the object after call to this function.
70562306a36Sopenharmony_ci */
70662306a36Sopenharmony_civoid tb_property_remove(struct tb_property *property)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	list_del(&property->list);
70962306a36Sopenharmony_ci	kfree(property);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_remove);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci/**
71462306a36Sopenharmony_ci * tb_property_find() - Find a property from a directory
71562306a36Sopenharmony_ci * @dir: Directory where the property is searched
71662306a36Sopenharmony_ci * @key: Key to look for
71762306a36Sopenharmony_ci * @type: Type of the property
71862306a36Sopenharmony_ci *
71962306a36Sopenharmony_ci * Finds and returns property from the given directory. Does not recurse
72062306a36Sopenharmony_ci * into sub-directories. Returns %NULL if the property was not found.
72162306a36Sopenharmony_ci */
72262306a36Sopenharmony_cistruct tb_property *tb_property_find(struct tb_property_dir *dir,
72362306a36Sopenharmony_ci	const char *key, enum tb_property_type type)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct tb_property *property;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	list_for_each_entry(property, &dir->properties, list) {
72862306a36Sopenharmony_ci		if (property->type == type && !strcmp(property->key, key))
72962306a36Sopenharmony_ci			return property;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return NULL;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_find);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/**
73762306a36Sopenharmony_ci * tb_property_get_next() - Get next property from directory
73862306a36Sopenharmony_ci * @dir: Directory holding properties
73962306a36Sopenharmony_ci * @prev: Previous property in the directory (%NULL returns the first)
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistruct tb_property *tb_property_get_next(struct tb_property_dir *dir,
74262306a36Sopenharmony_ci					 struct tb_property *prev)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	if (prev) {
74562306a36Sopenharmony_ci		if (list_is_last(&prev->list, &dir->properties))
74662306a36Sopenharmony_ci			return NULL;
74762306a36Sopenharmony_ci		return list_next_entry(prev, list);
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	return list_first_entry_or_null(&dir->properties, struct tb_property,
75062306a36Sopenharmony_ci					list);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tb_property_get_next);
753