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