diff options
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r-- | drivers/thunderbolt/switch.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 4b47e0999cda..1524edf42ee8 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c | |||
@@ -9,6 +9,9 @@ | |||
9 | 9 | ||
10 | #include "tb.h" | 10 | #include "tb.h" |
11 | 11 | ||
12 | /* Switch authorization from userspace is serialized by this lock */ | ||
13 | static DEFINE_MUTEX(switch_lock); | ||
14 | |||
12 | /* port utility functions */ | 15 | /* port utility functions */ |
13 | 16 | ||
14 | static const char *tb_port_type(struct tb_regs_port_header *port) | 17 | static const char *tb_port_type(struct tb_regs_port_header *port) |
@@ -310,6 +313,75 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active) | |||
310 | sw->cap_plug_events + 1, 1); | 313 | sw->cap_plug_events + 1, 1); |
311 | } | 314 | } |
312 | 315 | ||
316 | static ssize_t authorized_show(struct device *dev, | ||
317 | struct device_attribute *attr, | ||
318 | char *buf) | ||
319 | { | ||
320 | struct tb_switch *sw = tb_to_switch(dev); | ||
321 | |||
322 | return sprintf(buf, "%u\n", sw->authorized); | ||
323 | } | ||
324 | |||
325 | static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) | ||
326 | { | ||
327 | int ret = -EINVAL; | ||
328 | |||
329 | if (mutex_lock_interruptible(&switch_lock)) | ||
330 | return -ERESTARTSYS; | ||
331 | |||
332 | if (sw->authorized) | ||
333 | goto unlock; | ||
334 | |||
335 | switch (val) { | ||
336 | /* Approve switch */ | ||
337 | case 1: | ||
338 | if (sw->key) | ||
339 | ret = tb_domain_approve_switch_key(sw->tb, sw); | ||
340 | else | ||
341 | ret = tb_domain_approve_switch(sw->tb, sw); | ||
342 | break; | ||
343 | |||
344 | /* Challenge switch */ | ||
345 | case 2: | ||
346 | if (sw->key) | ||
347 | ret = tb_domain_challenge_switch_key(sw->tb, sw); | ||
348 | break; | ||
349 | |||
350 | default: | ||
351 | break; | ||
352 | } | ||
353 | |||
354 | if (!ret) { | ||
355 | sw->authorized = val; | ||
356 | /* Notify status change to the userspace */ | ||
357 | kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); | ||
358 | } | ||
359 | |||
360 | unlock: | ||
361 | mutex_unlock(&switch_lock); | ||
362 | return ret; | ||
363 | } | ||
364 | |||
365 | static ssize_t authorized_store(struct device *dev, | ||
366 | struct device_attribute *attr, | ||
367 | const char *buf, size_t count) | ||
368 | { | ||
369 | struct tb_switch *sw = tb_to_switch(dev); | ||
370 | unsigned int val; | ||
371 | ssize_t ret; | ||
372 | |||
373 | ret = kstrtouint(buf, 0, &val); | ||
374 | if (ret) | ||
375 | return ret; | ||
376 | if (val > 2) | ||
377 | return -EINVAL; | ||
378 | |||
379 | ret = tb_switch_set_authorized(sw, val); | ||
380 | |||
381 | return ret ? ret : count; | ||
382 | } | ||
383 | static DEVICE_ATTR_RW(authorized); | ||
384 | |||
313 | static ssize_t device_show(struct device *dev, struct device_attribute *attr, | 385 | static ssize_t device_show(struct device *dev, struct device_attribute *attr, |
314 | char *buf) | 386 | char *buf) |
315 | { | 387 | { |
@@ -328,6 +400,54 @@ device_name_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
328 | } | 400 | } |
329 | static DEVICE_ATTR_RO(device_name); | 401 | static DEVICE_ATTR_RO(device_name); |
330 | 402 | ||
403 | static ssize_t key_show(struct device *dev, struct device_attribute *attr, | ||
404 | char *buf) | ||
405 | { | ||
406 | struct tb_switch *sw = tb_to_switch(dev); | ||
407 | ssize_t ret; | ||
408 | |||
409 | if (mutex_lock_interruptible(&switch_lock)) | ||
410 | return -ERESTARTSYS; | ||
411 | |||
412 | if (sw->key) | ||
413 | ret = sprintf(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key); | ||
414 | else | ||
415 | ret = sprintf(buf, "\n"); | ||
416 | |||
417 | mutex_unlock(&switch_lock); | ||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | static ssize_t key_store(struct device *dev, struct device_attribute *attr, | ||
422 | const char *buf, size_t count) | ||
423 | { | ||
424 | struct tb_switch *sw = tb_to_switch(dev); | ||
425 | u8 key[TB_SWITCH_KEY_SIZE]; | ||
426 | ssize_t ret = count; | ||
427 | |||
428 | if (count < 64) | ||
429 | return -EINVAL; | ||
430 | |||
431 | if (hex2bin(key, buf, sizeof(key))) | ||
432 | return -EINVAL; | ||
433 | |||
434 | if (mutex_lock_interruptible(&switch_lock)) | ||
435 | return -ERESTARTSYS; | ||
436 | |||
437 | if (sw->authorized) { | ||
438 | ret = -EBUSY; | ||
439 | } else { | ||
440 | kfree(sw->key); | ||
441 | sw->key = kmemdup(key, sizeof(key), GFP_KERNEL); | ||
442 | if (!sw->key) | ||
443 | ret = -ENOMEM; | ||
444 | } | ||
445 | |||
446 | mutex_unlock(&switch_lock); | ||
447 | return ret; | ||
448 | } | ||
449 | static DEVICE_ATTR_RW(key); | ||
450 | |||
331 | static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, | 451 | static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, |
332 | char *buf) | 452 | char *buf) |
333 | { | 453 | { |
@@ -356,15 +476,35 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, | |||
356 | static DEVICE_ATTR_RO(unique_id); | 476 | static DEVICE_ATTR_RO(unique_id); |
357 | 477 | ||
358 | static struct attribute *switch_attrs[] = { | 478 | static struct attribute *switch_attrs[] = { |
479 | &dev_attr_authorized.attr, | ||
359 | &dev_attr_device.attr, | 480 | &dev_attr_device.attr, |
360 | &dev_attr_device_name.attr, | 481 | &dev_attr_device_name.attr, |
482 | &dev_attr_key.attr, | ||
361 | &dev_attr_vendor.attr, | 483 | &dev_attr_vendor.attr, |
362 | &dev_attr_vendor_name.attr, | 484 | &dev_attr_vendor_name.attr, |
363 | &dev_attr_unique_id.attr, | 485 | &dev_attr_unique_id.attr, |
364 | NULL, | 486 | NULL, |
365 | }; | 487 | }; |
366 | 488 | ||
489 | static umode_t switch_attr_is_visible(struct kobject *kobj, | ||
490 | struct attribute *attr, int n) | ||
491 | { | ||
492 | struct device *dev = container_of(kobj, struct device, kobj); | ||
493 | struct tb_switch *sw = tb_to_switch(dev); | ||
494 | |||
495 | if (attr == &dev_attr_key.attr) { | ||
496 | if (tb_route(sw) && | ||
497 | sw->tb->security_level == TB_SECURITY_SECURE && | ||
498 | sw->security_level == TB_SECURITY_SECURE) | ||
499 | return attr->mode; | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | return attr->mode; | ||
504 | } | ||
505 | |||
367 | static struct attribute_group switch_group = { | 506 | static struct attribute_group switch_group = { |
507 | .is_visible = switch_attr_is_visible, | ||
368 | .attrs = switch_attrs, | 508 | .attrs = switch_attrs, |
369 | }; | 509 | }; |
370 | 510 | ||
@@ -384,6 +524,7 @@ static void tb_switch_release(struct device *dev) | |||
384 | kfree(sw->vendor_name); | 524 | kfree(sw->vendor_name); |
385 | kfree(sw->ports); | 525 | kfree(sw->ports); |
386 | kfree(sw->drom); | 526 | kfree(sw->drom); |
527 | kfree(sw->key); | ||
387 | kfree(sw); | 528 | kfree(sw); |
388 | } | 529 | } |
389 | 530 | ||
@@ -490,6 +631,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, | |||
490 | } | 631 | } |
491 | sw->cap_plug_events = cap; | 632 | sw->cap_plug_events = cap; |
492 | 633 | ||
634 | /* Root switch is always authorized */ | ||
635 | if (!route) | ||
636 | sw->authorized = true; | ||
637 | |||
493 | device_initialize(&sw->dev); | 638 | device_initialize(&sw->dev); |
494 | sw->dev.parent = parent; | 639 | sw->dev.parent = parent; |
495 | sw->dev.bus = &tb_bus_type; | 640 | sw->dev.bus = &tb_bus_type; |
@@ -754,3 +899,80 @@ void tb_switch_suspend(struct tb_switch *sw) | |||
754 | * effect? | 899 | * effect? |
755 | */ | 900 | */ |
756 | } | 901 | } |
902 | |||
903 | struct tb_sw_lookup { | ||
904 | struct tb *tb; | ||
905 | u8 link; | ||
906 | u8 depth; | ||
907 | const uuid_be *uuid; | ||
908 | }; | ||
909 | |||
910 | static int tb_switch_match(struct device *dev, void *data) | ||
911 | { | ||
912 | struct tb_switch *sw = tb_to_switch(dev); | ||
913 | struct tb_sw_lookup *lookup = data; | ||
914 | |||
915 | if (!sw) | ||
916 | return 0; | ||
917 | if (sw->tb != lookup->tb) | ||
918 | return 0; | ||
919 | |||
920 | if (lookup->uuid) | ||
921 | return !memcmp(sw->uuid, lookup->uuid, sizeof(*lookup->uuid)); | ||
922 | |||
923 | /* Root switch is matched only by depth */ | ||
924 | if (!lookup->depth) | ||
925 | return !sw->depth; | ||
926 | |||
927 | return sw->link == lookup->link && sw->depth == lookup->depth; | ||
928 | } | ||
929 | |||
930 | /** | ||
931 | * tb_switch_find_by_link_depth() - Find switch by link and depth | ||
932 | * @tb: Domain the switch belongs | ||
933 | * @link: Link number the switch is connected | ||
934 | * @depth: Depth of the switch in link | ||
935 | * | ||
936 | * Returned switch has reference count increased so the caller needs to | ||
937 | * call tb_switch_put() when done with the switch. | ||
938 | */ | ||
939 | struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth) | ||
940 | { | ||
941 | struct tb_sw_lookup lookup; | ||
942 | struct device *dev; | ||
943 | |||
944 | memset(&lookup, 0, sizeof(lookup)); | ||
945 | lookup.tb = tb; | ||
946 | lookup.link = link; | ||
947 | lookup.depth = depth; | ||
948 | |||
949 | dev = bus_find_device(&tb_bus_type, NULL, &lookup, tb_switch_match); | ||
950 | if (dev) | ||
951 | return tb_to_switch(dev); | ||
952 | |||
953 | return NULL; | ||
954 | } | ||
955 | |||
956 | /** | ||
957 | * tb_switch_find_by_link_depth() - Find switch by UUID | ||
958 | * @tb: Domain the switch belongs | ||
959 | * @uuid: UUID to look for | ||
960 | * | ||
961 | * Returned switch has reference count increased so the caller needs to | ||
962 | * call tb_switch_put() when done with the switch. | ||
963 | */ | ||
964 | struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_be *uuid) | ||
965 | { | ||
966 | struct tb_sw_lookup lookup; | ||
967 | struct device *dev; | ||
968 | |||
969 | memset(&lookup, 0, sizeof(lookup)); | ||
970 | lookup.tb = tb; | ||
971 | lookup.uuid = uuid; | ||
972 | |||
973 | dev = bus_find_device(&tb_bus_type, NULL, &lookup, tb_switch_match); | ||
974 | if (dev) | ||
975 | return tb_to_switch(dev); | ||
976 | |||
977 | return NULL; | ||
978 | } | ||