0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/err.h>
0011 #include <linux/slab.h>
0012 #include <linux/string.h>
0013 #include <linux/uuid.h>
0014 #include <linux/thunderbolt.h>
0015
0016 struct tb_property_entry {
0017 u32 key_hi;
0018 u32 key_lo;
0019 u16 length;
0020 u8 reserved;
0021 u8 type;
0022 u32 value;
0023 };
0024
0025 struct tb_property_rootdir_entry {
0026 u32 magic;
0027 u32 length;
0028 struct tb_property_entry entries[];
0029 };
0030
0031 struct tb_property_dir_entry {
0032 u32 uuid[4];
0033 struct tb_property_entry entries[];
0034 };
0035
0036 #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
0037
0038 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
0039 size_t block_len, unsigned int dir_offset, size_t dir_len,
0040 bool is_root);
0041
0042 static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
0043 {
0044 be32_to_cpu_array(dst, src, dwords);
0045 }
0046
0047 static inline void format_dwdata(void *dst, const void *src, size_t dwords)
0048 {
0049 cpu_to_be32_array(dst, src, dwords);
0050 }
0051
0052 static bool tb_property_entry_valid(const struct tb_property_entry *entry,
0053 size_t block_len)
0054 {
0055 switch (entry->type) {
0056 case TB_PROPERTY_TYPE_DIRECTORY:
0057 case TB_PROPERTY_TYPE_DATA:
0058 case TB_PROPERTY_TYPE_TEXT:
0059 if (entry->length > block_len)
0060 return false;
0061 if (entry->value + entry->length > block_len)
0062 return false;
0063 break;
0064
0065 case TB_PROPERTY_TYPE_VALUE:
0066 if (entry->length != 1)
0067 return false;
0068 break;
0069 }
0070
0071 return true;
0072 }
0073
0074 static bool tb_property_key_valid(const char *key)
0075 {
0076 return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
0077 }
0078
0079 static struct tb_property *
0080 tb_property_alloc(const char *key, enum tb_property_type type)
0081 {
0082 struct tb_property *property;
0083
0084 property = kzalloc(sizeof(*property), GFP_KERNEL);
0085 if (!property)
0086 return NULL;
0087
0088 strcpy(property->key, key);
0089 property->type = type;
0090 INIT_LIST_HEAD(&property->list);
0091
0092 return property;
0093 }
0094
0095 static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
0096 const struct tb_property_entry *entry)
0097 {
0098 char key[TB_PROPERTY_KEY_SIZE + 1];
0099 struct tb_property *property;
0100 struct tb_property_dir *dir;
0101
0102 if (!tb_property_entry_valid(entry, block_len))
0103 return NULL;
0104
0105 parse_dwdata(key, entry, 2);
0106 key[TB_PROPERTY_KEY_SIZE] = '\0';
0107
0108 property = tb_property_alloc(key, entry->type);
0109 if (!property)
0110 return NULL;
0111
0112 property->length = entry->length;
0113
0114 switch (property->type) {
0115 case TB_PROPERTY_TYPE_DIRECTORY:
0116 dir = __tb_property_parse_dir(block, block_len, entry->value,
0117 entry->length, false);
0118 if (!dir) {
0119 kfree(property);
0120 return NULL;
0121 }
0122 property->value.dir = dir;
0123 break;
0124
0125 case TB_PROPERTY_TYPE_DATA:
0126 property->value.data = kcalloc(property->length, sizeof(u32),
0127 GFP_KERNEL);
0128 if (!property->value.data) {
0129 kfree(property);
0130 return NULL;
0131 }
0132 parse_dwdata(property->value.data, block + entry->value,
0133 entry->length);
0134 break;
0135
0136 case TB_PROPERTY_TYPE_TEXT:
0137 property->value.text = kcalloc(property->length, sizeof(u32),
0138 GFP_KERNEL);
0139 if (!property->value.text) {
0140 kfree(property);
0141 return NULL;
0142 }
0143 parse_dwdata(property->value.text, block + entry->value,
0144 entry->length);
0145
0146 property->value.text[property->length * 4 - 1] = '\0';
0147 break;
0148
0149 case TB_PROPERTY_TYPE_VALUE:
0150 property->value.immediate = entry->value;
0151 break;
0152
0153 default:
0154 property->type = TB_PROPERTY_TYPE_UNKNOWN;
0155 break;
0156 }
0157
0158 return property;
0159 }
0160
0161 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
0162 size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
0163 {
0164 const struct tb_property_entry *entries;
0165 size_t i, content_len, nentries;
0166 unsigned int content_offset;
0167 struct tb_property_dir *dir;
0168
0169 dir = kzalloc(sizeof(*dir), GFP_KERNEL);
0170 if (!dir)
0171 return NULL;
0172
0173 if (is_root) {
0174 content_offset = dir_offset + 2;
0175 content_len = dir_len;
0176 } else {
0177 dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
0178 GFP_KERNEL);
0179 if (!dir->uuid) {
0180 tb_property_free_dir(dir);
0181 return NULL;
0182 }
0183 content_offset = dir_offset + 4;
0184 content_len = dir_len - 4;
0185 }
0186
0187 entries = (const struct tb_property_entry *)&block[content_offset];
0188 nentries = content_len / (sizeof(*entries) / 4);
0189
0190 INIT_LIST_HEAD(&dir->properties);
0191
0192 for (i = 0; i < nentries; i++) {
0193 struct tb_property *property;
0194
0195 property = tb_property_parse(block, block_len, &entries[i]);
0196 if (!property) {
0197 tb_property_free_dir(dir);
0198 return NULL;
0199 }
0200
0201 list_add_tail(&property->list, &dir->properties);
0202 }
0203
0204 return dir;
0205 }
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220 struct tb_property_dir *tb_property_parse_dir(const u32 *block,
0221 size_t block_len)
0222 {
0223 const struct tb_property_rootdir_entry *rootdir =
0224 (const struct tb_property_rootdir_entry *)block;
0225
0226 if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
0227 return NULL;
0228 if (rootdir->length > block_len)
0229 return NULL;
0230
0231 return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
0232 true);
0233 }
0234
0235
0236
0237
0238
0239
0240
0241
0242 struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
0243 {
0244 struct tb_property_dir *dir;
0245
0246 dir = kzalloc(sizeof(*dir), GFP_KERNEL);
0247 if (!dir)
0248 return NULL;
0249
0250 INIT_LIST_HEAD(&dir->properties);
0251 if (uuid) {
0252 dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
0253 if (!dir->uuid) {
0254 kfree(dir);
0255 return NULL;
0256 }
0257 }
0258
0259 return dir;
0260 }
0261 EXPORT_SYMBOL_GPL(tb_property_create_dir);
0262
0263 static void tb_property_free(struct tb_property *property)
0264 {
0265 switch (property->type) {
0266 case TB_PROPERTY_TYPE_DIRECTORY:
0267 tb_property_free_dir(property->value.dir);
0268 break;
0269
0270 case TB_PROPERTY_TYPE_DATA:
0271 kfree(property->value.data);
0272 break;
0273
0274 case TB_PROPERTY_TYPE_TEXT:
0275 kfree(property->value.text);
0276 break;
0277
0278 default:
0279 break;
0280 }
0281
0282 kfree(property);
0283 }
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293 void tb_property_free_dir(struct tb_property_dir *dir)
0294 {
0295 struct tb_property *property, *tmp;
0296
0297 if (!dir)
0298 return;
0299
0300 list_for_each_entry_safe(property, tmp, &dir->properties, list) {
0301 list_del(&property->list);
0302 tb_property_free(property);
0303 }
0304 kfree(dir->uuid);
0305 kfree(dir);
0306 }
0307 EXPORT_SYMBOL_GPL(tb_property_free_dir);
0308
0309 static size_t tb_property_dir_length(const struct tb_property_dir *dir,
0310 bool recurse, size_t *data_len)
0311 {
0312 const struct tb_property *property;
0313 size_t len = 0;
0314
0315 if (dir->uuid)
0316 len += sizeof(*dir->uuid) / 4;
0317 else
0318 len += sizeof(struct tb_property_rootdir_entry) / 4;
0319
0320 list_for_each_entry(property, &dir->properties, list) {
0321 len += sizeof(struct tb_property_entry) / 4;
0322
0323 switch (property->type) {
0324 case TB_PROPERTY_TYPE_DIRECTORY:
0325 if (recurse) {
0326 len += tb_property_dir_length(
0327 property->value.dir, recurse, data_len);
0328 }
0329
0330 if (data_len)
0331 *data_len += 1;
0332 break;
0333
0334 case TB_PROPERTY_TYPE_DATA:
0335 case TB_PROPERTY_TYPE_TEXT:
0336 if (data_len)
0337 *data_len += property->length;
0338 break;
0339
0340 default:
0341 break;
0342 }
0343 }
0344
0345 return len;
0346 }
0347
0348 static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
0349 u32 *block, unsigned int start_offset, size_t block_len)
0350 {
0351 unsigned int data_offset, dir_end;
0352 const struct tb_property *property;
0353 struct tb_property_entry *entry;
0354 size_t dir_len, data_len = 0;
0355 int ret;
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402 dir_len = tb_property_dir_length(dir, false, &data_len);
0403 data_offset = start_offset + dir_len;
0404 dir_end = start_offset + data_len + dir_len;
0405
0406 if (data_offset > dir_end)
0407 return -EINVAL;
0408 if (dir_end > block_len)
0409 return -EINVAL;
0410
0411
0412 if (dir->uuid) {
0413 struct tb_property_dir_entry *pe;
0414
0415 pe = (struct tb_property_dir_entry *)&block[start_offset];
0416 memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
0417 entry = pe->entries;
0418 } else {
0419 struct tb_property_rootdir_entry *re;
0420
0421 re = (struct tb_property_rootdir_entry *)&block[start_offset];
0422 re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
0423 re->length = dir_len - sizeof(*re) / 4;
0424 entry = re->entries;
0425 }
0426
0427 list_for_each_entry(property, &dir->properties, list) {
0428 const struct tb_property_dir *child;
0429
0430 format_dwdata(entry, property->key, 2);
0431 entry->type = property->type;
0432
0433 switch (property->type) {
0434 case TB_PROPERTY_TYPE_DIRECTORY:
0435 child = property->value.dir;
0436 ret = __tb_property_format_dir(child, block, dir_end,
0437 block_len);
0438 if (ret < 0)
0439 return ret;
0440 entry->length = tb_property_dir_length(child, false,
0441 NULL);
0442 entry->value = dir_end;
0443 dir_end = ret;
0444 break;
0445
0446 case TB_PROPERTY_TYPE_DATA:
0447 format_dwdata(&block[data_offset], property->value.data,
0448 property->length);
0449 entry->length = property->length;
0450 entry->value = data_offset;
0451 data_offset += entry->length;
0452 break;
0453
0454 case TB_PROPERTY_TYPE_TEXT:
0455 format_dwdata(&block[data_offset], property->value.text,
0456 property->length);
0457 entry->length = property->length;
0458 entry->value = data_offset;
0459 data_offset += entry->length;
0460 break;
0461
0462 case TB_PROPERTY_TYPE_VALUE:
0463 entry->length = property->length;
0464 entry->value = property->value.immediate;
0465 break;
0466
0467 default:
0468 break;
0469 }
0470
0471 entry++;
0472 }
0473
0474 return dir_end;
0475 }
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488 ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
0489 size_t block_len)
0490 {
0491 ssize_t ret;
0492
0493 if (!block) {
0494 size_t dir_len, data_len = 0;
0495
0496 dir_len = tb_property_dir_length(dir, true, &data_len);
0497 return dir_len + data_len;
0498 }
0499
0500 ret = __tb_property_format_dir(dir, block, 0, block_len);
0501 return ret < 0 ? ret : 0;
0502 }
0503
0504
0505
0506
0507
0508
0509
0510
0511
0512 struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
0513 {
0514 struct tb_property *property, *p = NULL;
0515 struct tb_property_dir *d;
0516
0517 if (!dir)
0518 return NULL;
0519
0520 d = tb_property_create_dir(dir->uuid);
0521 if (!d)
0522 return NULL;
0523
0524 list_for_each_entry(property, &dir->properties, list) {
0525 struct tb_property *p;
0526
0527 p = tb_property_alloc(property->key, property->type);
0528 if (!p)
0529 goto err_free;
0530
0531 p->length = property->length;
0532
0533 switch (property->type) {
0534 case TB_PROPERTY_TYPE_DIRECTORY:
0535 p->value.dir = tb_property_copy_dir(property->value.dir);
0536 if (!p->value.dir)
0537 goto err_free;
0538 break;
0539
0540 case TB_PROPERTY_TYPE_DATA:
0541 p->value.data = kmemdup(property->value.data,
0542 property->length * 4,
0543 GFP_KERNEL);
0544 if (!p->value.data)
0545 goto err_free;
0546 break;
0547
0548 case TB_PROPERTY_TYPE_TEXT:
0549 p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
0550 if (!p->value.text)
0551 goto err_free;
0552 strcpy(p->value.text, property->value.text);
0553 break;
0554
0555 case TB_PROPERTY_TYPE_VALUE:
0556 p->value.immediate = property->value.immediate;
0557 break;
0558
0559 default:
0560 break;
0561 }
0562
0563 list_add_tail(&p->list, &d->properties);
0564 }
0565
0566 return d;
0567
0568 err_free:
0569 kfree(p);
0570 tb_property_free_dir(d);
0571
0572 return NULL;
0573 }
0574
0575
0576
0577
0578
0579
0580
0581 int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
0582 u32 value)
0583 {
0584 struct tb_property *property;
0585
0586 if (!tb_property_key_valid(key))
0587 return -EINVAL;
0588
0589 property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
0590 if (!property)
0591 return -ENOMEM;
0592
0593 property->length = 1;
0594 property->value.immediate = value;
0595
0596 list_add_tail(&property->list, &parent->properties);
0597 return 0;
0598 }
0599 EXPORT_SYMBOL_GPL(tb_property_add_immediate);
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610 int tb_property_add_data(struct tb_property_dir *parent, const char *key,
0611 const void *buf, size_t buflen)
0612 {
0613
0614 size_t size = round_up(buflen, 4);
0615 struct tb_property *property;
0616
0617 if (!tb_property_key_valid(key))
0618 return -EINVAL;
0619
0620 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
0621 if (!property)
0622 return -ENOMEM;
0623
0624 property->length = size / 4;
0625 property->value.data = kzalloc(size, GFP_KERNEL);
0626 if (!property->value.data) {
0627 kfree(property);
0628 return -ENOMEM;
0629 }
0630
0631 memcpy(property->value.data, buf, buflen);
0632
0633 list_add_tail(&property->list, &parent->properties);
0634 return 0;
0635 }
0636 EXPORT_SYMBOL_GPL(tb_property_add_data);
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646 int tb_property_add_text(struct tb_property_dir *parent, const char *key,
0647 const char *text)
0648 {
0649
0650 size_t size = round_up(strlen(text) + 1, 4);
0651 struct tb_property *property;
0652
0653 if (!tb_property_key_valid(key))
0654 return -EINVAL;
0655
0656 property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
0657 if (!property)
0658 return -ENOMEM;
0659
0660 property->length = size / 4;
0661 property->value.text = kzalloc(size, GFP_KERNEL);
0662 if (!property->value.text) {
0663 kfree(property);
0664 return -ENOMEM;
0665 }
0666
0667 strcpy(property->value.text, text);
0668
0669 list_add_tail(&property->list, &parent->properties);
0670 return 0;
0671 }
0672 EXPORT_SYMBOL_GPL(tb_property_add_text);
0673
0674
0675
0676
0677
0678
0679
0680 int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
0681 struct tb_property_dir *dir)
0682 {
0683 struct tb_property *property;
0684
0685 if (!tb_property_key_valid(key))
0686 return -EINVAL;
0687
0688 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
0689 if (!property)
0690 return -ENOMEM;
0691
0692 property->value.dir = dir;
0693
0694 list_add_tail(&property->list, &parent->properties);
0695 return 0;
0696 }
0697 EXPORT_SYMBOL_GPL(tb_property_add_dir);
0698
0699
0700
0701
0702
0703
0704
0705
0706 void tb_property_remove(struct tb_property *property)
0707 {
0708 list_del(&property->list);
0709 kfree(property);
0710 }
0711 EXPORT_SYMBOL_GPL(tb_property_remove);
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722 struct tb_property *tb_property_find(struct tb_property_dir *dir,
0723 const char *key, enum tb_property_type type)
0724 {
0725 struct tb_property *property;
0726
0727 list_for_each_entry(property, &dir->properties, list) {
0728 if (property->type == type && !strcmp(property->key, key))
0729 return property;
0730 }
0731
0732 return NULL;
0733 }
0734 EXPORT_SYMBOL_GPL(tb_property_find);
0735
0736
0737
0738
0739
0740
0741 struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
0742 struct tb_property *prev)
0743 {
0744 if (prev) {
0745 if (list_is_last(&prev->list, &dir->properties))
0746 return NULL;
0747 return list_next_entry(prev, list);
0748 }
0749 return list_first_entry_or_null(&dir->properties, struct tb_property,
0750 list);
0751 }
0752 EXPORT_SYMBOL_GPL(tb_property_get_next);