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