GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / thunderbolt / property.c
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
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
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
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
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 *
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
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
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  */
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  */
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
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  */
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
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
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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);