File Index
Files and directories
Sign in
FILE

property.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thunderbolt/property.c C
  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 *
215 * 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 *
220 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
221 */
222struct tb_property_dir *tb_property_parse_dir(const u32 *block,
223					      size_t block_len)
224{
225	const struct tb_property_rootdir_entry *rootdir =
226		(const struct tb_property_rootdir_entry *)block;
227
228	if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
229		return NULL;
230	if (rootdir->length > block_len)
231		return NULL;
232
233	return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
234				       true);
235}
236
237/**
238 * tb_property_create_dir() - Creates new property directory
239 * @uuid: UUID used to identify the particular directory
240 *
241 * Creates new, empty property directory. If @uuid is %NULL then the
242 * directory is assumed to be root directory.
243 *
244 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
245 */
246struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
247{
248	struct tb_property_dir *dir;
249
250	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
251	if (!dir)
252		return NULL;
253
254	INIT_LIST_HEAD(&dir->properties);
255	if (uuid) {
256		dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
257		if (!dir->uuid) {
258			kfree(dir);
259			return NULL;
260		}
261	}
262
263	return dir;
264}
265EXPORT_SYMBOL_GPL(tb_property_create_dir);
266
267static void tb_property_free(struct tb_property *property)
268{
269	switch (property->type) {
270	case TB_PROPERTY_TYPE_DIRECTORY:
271		tb_property_free_dir(property->value.dir);
272		break;
273
274	case TB_PROPERTY_TYPE_DATA:
275		kfree(property->value.data);
276		break;
277
278	case TB_PROPERTY_TYPE_TEXT:
279		kfree(property->value.text);
280		break;
281
282	default:
283		break;
284	}
285
286	kfree(property);
287}
288
289/**
290 * tb_property_free_dir() - Release memory allocated for property directory
291 * @dir: Directory to release
292 *
293 * This will release all the memory the directory occupies including all
294 * descendants. It is OK to pass %NULL @dir, then the function does
295 * nothing.
296 */
297void tb_property_free_dir(struct tb_property_dir *dir)
298{
299	struct tb_property *property, *tmp;
300
301	if (!dir)
302		return;
303
304	list_for_each_entry_safe(property, tmp, &dir->properties, list) {
305		list_del(&property->list);
306		tb_property_free(property);
307	}
308	kfree(dir->uuid);
309	kfree(dir);
310}
311EXPORT_SYMBOL_GPL(tb_property_free_dir);
312
313static size_t tb_property_dir_length(const struct tb_property_dir *dir,
314				     bool recurse, size_t *data_len)
315{
316	const struct tb_property *property;
317	size_t len = 0;
318
319	if (dir->uuid)
320		len += sizeof(*dir->uuid) / 4;
321	else
322		len += sizeof(struct tb_property_rootdir_entry) / 4;
323
324	list_for_each_entry(property, &dir->properties, list) {
325		len += sizeof(struct tb_property_entry) / 4;
326
327		switch (property->type) {
328		case TB_PROPERTY_TYPE_DIRECTORY:
329			if (recurse) {
330				len += tb_property_dir_length(
331					property->value.dir, recurse, data_len);
332			}
333			/* Reserve dword padding after each directory */
334			if (data_len)
335				*data_len += 1;
336			break;
337
338		case TB_PROPERTY_TYPE_DATA:
339		case TB_PROPERTY_TYPE_TEXT:
340			if (data_len)
341				*data_len += property->length;
342			break;
343
344		default:
345			break;
346		}
347	}
348
349	return len;
350}
351
352static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
353	u32 *block, unsigned int start_offset, size_t block_len)
354{
355	unsigned int data_offset, dir_end;
356	const struct tb_property *property;
357	struct tb_property_entry *entry;
358	size_t dir_len, data_len = 0;
359	int ret;
360
361	/*
362	 * The structure of property block looks like following. Leaf
363	 * data/text is included right after the directory and each
364	 * directory follows each other (even nested ones).
365	 *
366	 * +----------+ <-- start_offset
367	 * |  header  | <-- root directory header
368	 * +----------+ ---
369	 * |  entry 0 | -^--------------------.
370	 * +----------+  |                    |
371	 * |  entry 1 | -|--------------------|--.
372	 * +----------+  |                    |  |
373	 * |  entry 2 | -|-----------------.  |  |
374	 * +----------+  |                 |  |  |
375	 * :          :  |  dir_len        |  |  |
376	 * .          .  |                 |  |  |
377	 * :          :  |                 |  |  |
378	 * +----------+  |                 |  |  |
379	 * |  entry n |  v                 |  |  |
380	 * +----------+ <-- data_offset    |  |  |
381	 * |  data 0  | <------------------|--'  |
382	 * +----------+                    |     |
383	 * |  data 1  | <------------------|-----'
384	 * +----------+                    |
385	 * | 00000000 | padding            |
386	 * +----------+ <-- dir_end <------'
387	 * |   UUID   | <-- directory UUID (child directory)
388	 * +----------+
389	 * |  entry 0 |
390	 * +----------+
391	 * |  entry 1 |
392	 * +----------+
393	 * :          :
394	 * .          .
395	 * :          :
396	 * +----------+
397	 * |  entry n |
398	 * +----------+
399	 * |  data 0  |
400	 * +----------+
401	 *
402	 * We use dir_end to hold pointer to the end of the directory. It
403	 * will increase as we add directories and each directory should be
404	 * added starting from previous dir_end.
405	 */
406	dir_len = tb_property_dir_length(dir, false, &data_len);
407	data_offset = start_offset + dir_len;
408	dir_end = start_offset + data_len + dir_len;
409
410	if (data_offset > dir_end)
411		return -EINVAL;
412	if (dir_end > block_len)
413		return -EINVAL;
414
415	/* Write headers first */
416	if (dir->uuid) {
417		struct tb_property_dir_entry *pe;
418
419		pe = (struct tb_property_dir_entry *)&block[start_offset];
420		memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
421		entry = pe->entries;
422	} else {
423		struct tb_property_rootdir_entry *re;
424
425		re = (struct tb_property_rootdir_entry *)&block[start_offset];
426		re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
427		re->length = dir_len - sizeof(*re) / 4;
428		entry = re->entries;
429	}
430
431	list_for_each_entry(property, &dir->properties, list) {
432		const struct tb_property_dir *child;
433
434		format_dwdata(entry, property->key, 2);
435		entry->type = property->type;
436
437		switch (property->type) {
438		case TB_PROPERTY_TYPE_DIRECTORY:
439			child = property->value.dir;
440			ret = __tb_property_format_dir(child, block, dir_end,
441						       block_len);
442			if (ret < 0)
443				return ret;
444			entry->length = tb_property_dir_length(child, false,
445							       NULL);
446			entry->value = dir_end;
447			dir_end = ret;
448			break;
449
450		case TB_PROPERTY_TYPE_DATA:
451			format_dwdata(&block[data_offset], property->value.data,
452				      property->length);
453			entry->length = property->length;
454			entry->value = data_offset;
455			data_offset += entry->length;
456			break;
457
458		case TB_PROPERTY_TYPE_TEXT:
459			format_dwdata(&block[data_offset], property->value.text,
460				      property->length);
461			entry->length = property->length;
462			entry->value = data_offset;
463			data_offset += entry->length;
464			break;
465
466		case TB_PROPERTY_TYPE_VALUE:
467			entry->length = property->length;
468			entry->value = property->value.immediate;
469			break;
470
471		default:
472			break;
473		}
474
475		entry++;
476	}
477
478	return dir_end;
479}
480
481/**
482 * tb_property_format_dir() - Formats directory to the packed XDomain format
483 * @dir: Directory to format
484 * @block: Property block where the packed data is placed
485 * @block_len: Length of the property block
486 *
487 * This function formats the directory to the packed format that can be
488 * then sent over the thunderbolt fabric to receiving host.
489 *
490 * Passing %NULL in @block returns number of entries the block takes.
491 *
492 * Return: %0 on success, negative errno otherwise.
493 */
494ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
495			       size_t block_len)
496{
497	ssize_t ret;
498
499	if (!block) {
500		size_t dir_len, data_len = 0;
501
502		dir_len = tb_property_dir_length(dir, true, &data_len);
503		return dir_len + data_len;
504	}
505
506	ret = __tb_property_format_dir(dir, block, 0, block_len);
507	return ret < 0 ? ret : 0;
508}
509
510/**
511 * tb_property_copy_dir() - Take a deep copy of directory
512 * @dir: Directory to copy
513 *
514 * The resulting directory needs to be released by calling tb_property_free_dir().
515 *
516 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
517 */
518struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
519{
520	struct tb_property *property, *p = NULL;
521	struct tb_property_dir *d;
522
523	if (!dir)
524		return NULL;
525
526	d = tb_property_create_dir(dir->uuid);
527	if (!d)
528		return NULL;
529
530	list_for_each_entry(property, &dir->properties, list) {
531		struct tb_property *p;
532
533		p = tb_property_alloc(property->key, property->type);
534		if (!p)
535			goto err_free;
536
537		p->length = property->length;
538
539		switch (property->type) {
540		case TB_PROPERTY_TYPE_DIRECTORY:
541			p->value.dir = tb_property_copy_dir(property->value.dir);
542			if (!p->value.dir)
543				goto err_free;
544			break;
545
546		case TB_PROPERTY_TYPE_DATA:
547			p->value.data = kmemdup(property->value.data,
548						property->length * 4,
549						GFP_KERNEL);
550			if (!p->value.data)
551				goto err_free;
552			break;
553
554		case TB_PROPERTY_TYPE_TEXT:
555			p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
556			if (!p->value.text)
557				goto err_free;
558			strcpy(p->value.text, property->value.text);
559			break;
560
561		case TB_PROPERTY_TYPE_VALUE:
562			p->value.immediate = property->value.immediate;
563			break;
564
565		default:
566			break;
567		}
568
569		list_add_tail(&p->list, &d->properties);
570	}
571
572	return d;
573
574err_free:
575	kfree(p);
576	tb_property_free_dir(d);
577
578	return NULL;
579}
580
581/**
582 * tb_property_add_immediate() - Add immediate property to directory
583 * @parent: Directory to add the property
584 * @key: Key for the property
585 * @value: Immediate value to store with the property
586 *
587 * Return: %0 on success, negative errno otherwise.
588 */
589int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
590			      u32 value)
591{
592	struct tb_property *property;
593
594	if (!tb_property_key_valid(key))
595		return -EINVAL;
596
597	property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
598	if (!property)
599		return -ENOMEM;
600
601	property->length = 1;
602	property->value.immediate = value;
603
604	list_add_tail(&property->list, &parent->properties);
605	return 0;
606}
607EXPORT_SYMBOL_GPL(tb_property_add_immediate);
608
609/**
610 * tb_property_add_data() - Adds arbitrary data property to directory
611 * @parent: Directory to add the property
612 * @key: Key for the property
613 * @buf: Data buffer to add
614 * @buflen: Number of bytes in the data buffer
615 *
616 * Function takes a copy of @buf and adds it to the directory.
617 *
618 * Return: %0 on success, negative errno otherwise.
619 */
620int tb_property_add_data(struct tb_property_dir *parent, const char *key,
621			 const void *buf, size_t buflen)
622{
623	/* Need to pad to dword boundary */
624	size_t size = round_up(buflen, 4);
625	struct tb_property *property;
626
627	if (!tb_property_key_valid(key))
628		return -EINVAL;
629
630	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
631	if (!property)
632		return -ENOMEM;
633
634	property->length = size / 4;
635	property->value.data = kzalloc(size, GFP_KERNEL);
636	if (!property->value.data) {
637		kfree(property);
638		return -ENOMEM;
639	}
640
641	memcpy(property->value.data, buf, buflen);
642
643	list_add_tail(&property->list, &parent->properties);
644	return 0;
645}
646EXPORT_SYMBOL_GPL(tb_property_add_data);
647
648/**
649 * tb_property_add_text() - Adds string property to directory
650 * @parent: Directory to add the property
651 * @key: Key for the property
652 * @text: String to add
653 *
654 * Function takes a copy of @text and adds it to the directory.
655 *
656 * Return: %0 on success, negative errno otherwise.
657 */
658int tb_property_add_text(struct tb_property_dir *parent, const char *key,
659			 const char *text)
660{
661	/* Need to pad to dword boundary */
662	size_t size = round_up(strlen(text) + 1, 4);
663	struct tb_property *property;
664
665	if (!tb_property_key_valid(key))
666		return -EINVAL;
667
668	property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
669	if (!property)
670		return -ENOMEM;
671
672	property->length = size / 4;
673	property->value.text = kzalloc(size, GFP_KERNEL);
674	if (!property->value.text) {
675		kfree(property);
676		return -ENOMEM;
677	}
678
679	strcpy(property->value.text, text);
680
681	list_add_tail(&property->list, &parent->properties);
682	return 0;
683}
684EXPORT_SYMBOL_GPL(tb_property_add_text);
685
686/**
687 * tb_property_add_dir() - Adds a directory to the parent directory
688 * @parent: Directory to add the property
689 * @key: Key for the property
690 * @dir: Directory to add
691 *
692 * Return: %0 on success, negative errno otherwise.
693 */
694int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
695			struct tb_property_dir *dir)
696{
697	struct tb_property *property;
698
699	if (!tb_property_key_valid(key))
700		return -EINVAL;
701
702	property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
703	if (!property)
704		return -ENOMEM;
705
706	property->value.dir = dir;
707
708	list_add_tail(&property->list, &parent->properties);
709	return 0;
710}
711EXPORT_SYMBOL_GPL(tb_property_add_dir);
712
713/**
714 * tb_property_remove() - Removes property from a parent directory
715 * @property: Property to remove
716 *
717 * Note memory for @property is released as well so it is not allowed to
718 * touch the object after call to this function.
719 */
720void tb_property_remove(struct tb_property *property)
721{
722	list_del(&property->list);
723	kfree(property);
724}
725EXPORT_SYMBOL_GPL(tb_property_remove);
726
727/**
728 * tb_property_find() - Find a property from a directory
729 * @dir: Directory where the property is searched
730 * @key: Key to look for
731 * @type: Type of the property
732 *
733 * Finds and returns property from the given directory. Does not
734 * recurse into sub-directories.
735 *
736 * Return: Pointer to &struct tb_property, %NULL if the property was not found.
737 */
738struct tb_property *tb_property_find(struct tb_property_dir *dir,
739	const char *key, enum tb_property_type type)
740{
741	struct tb_property *property;
742
743	list_for_each_entry(property, &dir->properties, list) {
744		if (property->type == type && !strcmp(property->key, key))
745			return property;
746	}
747
748	return NULL;
749}
750EXPORT_SYMBOL_GPL(tb_property_find);
751
752/**
753 * tb_property_get_next() - Get next property from directory
754 * @dir: Directory holding properties
755 * @prev: Previous property in the directory (%NULL returns the first)
756 *
757 * Return: Pointer to &struct tb_property, %NULL if property was not found.
758 */
759struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
760					 struct tb_property *prev)
761{
762	if (prev) {
763		if (list_is_last(&prev->list, &dir->properties))
764			return NULL;
765		return list_next_entry(prev, list);
766	}
767	return list_first_entry_or_null(&dir->properties, struct tb_property,
768					list);
769}
770EXPORT_SYMBOL_GPL(tb_property_get_next);
~/ Read-only file preview.