Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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);