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_add_immediate() - Add immediate property to directory
506 * @parent: Directory to add the property
507 * @key: Key for the property
508 * @value: Immediate value to store with the property
509 */
510int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
511			      u32 value)
512{
513	struct tb_property *property;
514
515	if (!tb_property_key_valid(key))
516		return -EINVAL;
517
518	property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
519	if (!property)
520		return -ENOMEM;
521
522	property->length = 1;
523	property->value.immediate = value;
524
525	list_add_tail(&property->list, &parent->properties);
526	return 0;
527}
528EXPORT_SYMBOL_GPL(tb_property_add_immediate);
529
530/**
531 * tb_property_add_data() - Adds arbitrary data property to directory
532 * @parent: Directory to add the property
533 * @key: Key for the property
534 * @buf: Data buffer to add
535 * @buflen: Number of bytes in the data buffer
536 *
537 * Function takes a copy of @buf and adds it to the directory.
538 */
539int tb_property_add_data(struct tb_property_dir *parent, const char *key,
540			 const void *buf, size_t buflen)
541{
542	/* Need to pad to dword boundary */
543	size_t size = round_up(buflen, 4);
544	struct tb_property *property;
545
546	if (!tb_property_key_valid(key))
547		return -EINVAL;
548
549	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
550	if (!property)
551		return -ENOMEM;
552
553	property->length = size / 4;
554	property->value.data = kzalloc(size, GFP_KERNEL);
555	if (!property->value.data) {
556		kfree(property);
557		return -ENOMEM;
558	}
559
560	memcpy(property->value.data, buf, buflen);
561
562	list_add_tail(&property->list, &parent->properties);
563	return 0;
564}
565EXPORT_SYMBOL_GPL(tb_property_add_data);
566
567/**
568 * tb_property_add_text() - Adds string property to directory
569 * @parent: Directory to add the property
570 * @key: Key for the property
571 * @text: String to add
572 *
573 * Function takes a copy of @text and adds it to the directory.
574 */
575int tb_property_add_text(struct tb_property_dir *parent, const char *key,
576			 const char *text)
577{
578	/* Need to pad to dword boundary */
579	size_t size = round_up(strlen(text) + 1, 4);
580	struct tb_property *property;
581
582	if (!tb_property_key_valid(key))
583		return -EINVAL;
584
585	property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
586	if (!property)
587		return -ENOMEM;
588
589	property->length = size / 4;
590	property->value.text = kzalloc(size, GFP_KERNEL);
591	if (!property->value.text) {
592		kfree(property);
593		return -ENOMEM;
594	}
595
596	strcpy(property->value.text, text);
597
598	list_add_tail(&property->list, &parent->properties);
599	return 0;
600}
601EXPORT_SYMBOL_GPL(tb_property_add_text);
602
603/**
604 * tb_property_add_dir() - Adds a directory to the parent directory
605 * @parent: Directory to add the property
606 * @key: Key for the property
607 * @dir: Directory to add
608 */
609int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
610			struct tb_property_dir *dir)
611{
612	struct tb_property *property;
613
614	if (!tb_property_key_valid(key))
615		return -EINVAL;
616
617	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
618	if (!property)
619		return -ENOMEM;
620
621	property->value.dir = dir;
622
623	list_add_tail(&property->list, &parent->properties);
624	return 0;
625}
626EXPORT_SYMBOL_GPL(tb_property_add_dir);
627
628/**
629 * tb_property_remove() - Removes property from a parent directory
630 * @property: Property to remove
631 *
632 * Note memory for @property is released as well so it is not allowed to
633 * touch the object after call to this function.
634 */
635void tb_property_remove(struct tb_property *property)
636{
637	list_del(&property->list);
638	kfree(property);
639}
640EXPORT_SYMBOL_GPL(tb_property_remove);
641
642/**
643 * tb_property_find() - Find a property from a directory
644 * @dir: Directory where the property is searched
645 * @key: Key to look for
646 * @type: Type of the property
647 *
648 * Finds and returns property from the given directory. Does not recurse
649 * into sub-directories. Returns %NULL if the property was not found.
650 */
651struct tb_property *tb_property_find(struct tb_property_dir *dir,
652	const char *key, enum tb_property_type type)
653{
654	struct tb_property *property;
655
656	list_for_each_entry(property, &dir->properties, list) {
657		if (property->type == type && !strcmp(property->key, key))
658			return property;
659	}
660
661	return NULL;
662}
663EXPORT_SYMBOL_GPL(tb_property_find);
664
665/**
666 * tb_property_get_next() - Get next property from directory
667 * @dir: Directory holding properties
668 * @prev: Previous property in the directory (%NULL returns the first)
669 */
670struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
671					 struct tb_property *prev)
672{
673	if (prev) {
674		if (list_is_last(&prev->list, &dir->properties))
675			return NULL;
676		return list_next_entry(prev, list);
677	}
678	return list_first_entry_or_null(&dir->properties, struct tb_property,
679					list);
680}
681EXPORT_SYMBOL_GPL(tb_property_get_next);
682