This source file includes following definitions.
- parse_dwdata
- format_dwdata
- tb_property_entry_valid
- tb_property_key_valid
- tb_property_alloc
- tb_property_parse
- __tb_property_parse_dir
- tb_property_parse_dir
- tb_property_create_dir
- tb_property_free
- tb_property_free_dir
- tb_property_dir_length
- __tb_property_format_dir
- tb_property_format_dir
- tb_property_add_immediate
- tb_property_add_data
- tb_property_add_text
- tb_property_add_dir
- tb_property_remove
- tb_property_find
- tb_property_get_next
1
2
3
4
5
6
7
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
16 struct 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
25 struct tb_property_rootdir_entry {
26 u32 magic;
27 u32 length;
28 struct tb_property_entry entries[];
29 };
30
31 struct tb_property_dir_entry {
32 u32 uuid[4];
33 struct tb_property_entry entries[];
34 };
35
36 #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
37
38 static 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
42 static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
43 {
44 be32_to_cpu_array(dst, src, dwords);
45 }
46
47 static inline void format_dwdata(void *dst, const void *src, size_t dwords)
48 {
49 cpu_to_be32_array(dst, src, dwords);
50 }
51
52 static 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
74 static bool tb_property_key_valid(const char *key)
75 {
76 return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
77 }
78
79 static struct tb_property *
80 tb_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
95 static 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
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
161 static 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;
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
209
210
211
212
213
214
215
216
217
218
219
220 struct 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
237
238
239
240
241
242 struct 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 }
261 EXPORT_SYMBOL_GPL(tb_property_create_dir);
262
263 static 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
287
288
289
290
291
292
293 void 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 }
307 EXPORT_SYMBOL_GPL(tb_property_free_dir);
308
309 static 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
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
348 static 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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
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
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
479
480
481
482
483
484
485
486
487
488 ssize_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
506
507
508
509
510 int 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 }
528 EXPORT_SYMBOL_GPL(tb_property_add_immediate);
529
530
531
532
533
534
535
536
537
538
539 int tb_property_add_data(struct tb_property_dir *parent, const char *key,
540 const void *buf, size_t buflen)
541 {
542
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 }
565 EXPORT_SYMBOL_GPL(tb_property_add_data);
566
567
568
569
570
571
572
573
574
575 int tb_property_add_text(struct tb_property_dir *parent, const char *key,
576 const char *text)
577 {
578
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 }
601 EXPORT_SYMBOL_GPL(tb_property_add_text);
602
603
604
605
606
607
608
609 int 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 }
626 EXPORT_SYMBOL_GPL(tb_property_add_dir);
627
628
629
630
631
632
633
634
635 void tb_property_remove(struct tb_property *property)
636 {
637 list_del(&property->list);
638 kfree(property);
639 }
640 EXPORT_SYMBOL_GPL(tb_property_remove);
641
642
643
644
645
646
647
648
649
650
651 struct 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 }
663 EXPORT_SYMBOL_GPL(tb_property_find);
664
665
666
667
668
669
670 struct 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 }
681 EXPORT_SYMBOL_GPL(tb_property_get_next);