162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Thunderbolt bus support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation 662306a36Sopenharmony_ci * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/idr.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/random.h> 1562306a36Sopenharmony_ci#include <crypto/hash.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "tb.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic DEFINE_IDA(tb_domain_ida); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool match_service_id(const struct tb_service_id *id, 2262306a36Sopenharmony_ci const struct tb_service *svc) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci if (id->match_flags & TBSVC_MATCH_PROTOCOL_KEY) { 2562306a36Sopenharmony_ci if (strcmp(id->protocol_key, svc->key)) 2662306a36Sopenharmony_ci return false; 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (id->match_flags & TBSVC_MATCH_PROTOCOL_ID) { 3062306a36Sopenharmony_ci if (id->protocol_id != svc->prtcid) 3162306a36Sopenharmony_ci return false; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { 3562306a36Sopenharmony_ci if (id->protocol_version != svc->prtcvers) 3662306a36Sopenharmony_ci return false; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { 4062306a36Sopenharmony_ci if (id->protocol_revision != svc->prtcrevs) 4162306a36Sopenharmony_ci return false; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return true; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct tb_service_id *__tb_service_match(struct device *dev, 4862306a36Sopenharmony_ci struct device_driver *drv) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct tb_service_driver *driver; 5162306a36Sopenharmony_ci const struct tb_service_id *ids; 5262306a36Sopenharmony_ci struct tb_service *svc; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci svc = tb_to_service(dev); 5562306a36Sopenharmony_ci if (!svc) 5662306a36Sopenharmony_ci return NULL; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci driver = container_of(drv, struct tb_service_driver, driver); 5962306a36Sopenharmony_ci if (!driver->id_table) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (ids = driver->id_table; ids->match_flags != 0; ids++) { 6362306a36Sopenharmony_ci if (match_service_id(ids, svc)) 6462306a36Sopenharmony_ci return ids; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int tb_service_match(struct device *dev, struct device_driver *drv) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return !!__tb_service_match(dev, drv); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int tb_service_probe(struct device *dev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct tb_service *svc = tb_to_service(dev); 7862306a36Sopenharmony_ci struct tb_service_driver *driver; 7962306a36Sopenharmony_ci const struct tb_service_id *id; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci driver = container_of(dev->driver, struct tb_service_driver, driver); 8262306a36Sopenharmony_ci id = __tb_service_match(dev, &driver->driver); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return driver->probe(svc, id); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void tb_service_remove(struct device *dev) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct tb_service *svc = tb_to_service(dev); 9062306a36Sopenharmony_ci struct tb_service_driver *driver; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci driver = container_of(dev->driver, struct tb_service_driver, driver); 9362306a36Sopenharmony_ci if (driver->remove) 9462306a36Sopenharmony_ci driver->remove(svc); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void tb_service_shutdown(struct device *dev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct tb_service_driver *driver; 10062306a36Sopenharmony_ci struct tb_service *svc; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci svc = tb_to_service(dev); 10362306a36Sopenharmony_ci if (!svc || !dev->driver) 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci driver = container_of(dev->driver, struct tb_service_driver, driver); 10762306a36Sopenharmony_ci if (driver->shutdown) 10862306a36Sopenharmony_ci driver->shutdown(svc); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const char * const tb_security_names[] = { 11262306a36Sopenharmony_ci [TB_SECURITY_NONE] = "none", 11362306a36Sopenharmony_ci [TB_SECURITY_USER] = "user", 11462306a36Sopenharmony_ci [TB_SECURITY_SECURE] = "secure", 11562306a36Sopenharmony_ci [TB_SECURITY_DPONLY] = "dponly", 11662306a36Sopenharmony_ci [TB_SECURITY_USBONLY] = "usbonly", 11762306a36Sopenharmony_ci [TB_SECURITY_NOPCIE] = "nopcie", 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr, 12162306a36Sopenharmony_ci char *buf) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 12462306a36Sopenharmony_ci uuid_t *uuids; 12562306a36Sopenharmony_ci ssize_t ret; 12662306a36Sopenharmony_ci int i; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); 12962306a36Sopenharmony_ci if (!uuids) 13062306a36Sopenharmony_ci return -ENOMEM; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci pm_runtime_get_sync(&tb->dev); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (mutex_lock_interruptible(&tb->lock)) { 13562306a36Sopenharmony_ci ret = -ERESTARTSYS; 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl); 13962306a36Sopenharmony_ci if (ret) { 14062306a36Sopenharmony_ci mutex_unlock(&tb->lock); 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci mutex_unlock(&tb->lock); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (ret = 0, i = 0; i < tb->nboot_acl; i++) { 14662306a36Sopenharmony_ci if (!uuid_is_null(&uuids[i])) 14762306a36Sopenharmony_ci ret += sysfs_emit_at(buf, ret, "%pUb", &uuids[i]); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret += sysfs_emit_at(buf, ret, "%s", i < tb->nboot_acl - 1 ? "," : "\n"); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciout: 15362306a36Sopenharmony_ci pm_runtime_mark_last_busy(&tb->dev); 15462306a36Sopenharmony_ci pm_runtime_put_autosuspend(&tb->dev); 15562306a36Sopenharmony_ci kfree(uuids); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr, 16162306a36Sopenharmony_ci const char *buf, size_t count) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 16462306a36Sopenharmony_ci char *str, *s, *uuid_str; 16562306a36Sopenharmony_ci ssize_t ret = 0; 16662306a36Sopenharmony_ci uuid_t *acl; 16762306a36Sopenharmony_ci int i = 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Make sure the value is not bigger than tb->nboot_acl * UUID 17162306a36Sopenharmony_ci * length + commas and optional "\n". Also the smallest allowable 17262306a36Sopenharmony_ci * string is tb->nboot_acl * ",". 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci if (count < tb->nboot_acl - 1) 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci str = kstrdup(buf, GFP_KERNEL); 18062306a36Sopenharmony_ci if (!str) 18162306a36Sopenharmony_ci return -ENOMEM; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); 18462306a36Sopenharmony_ci if (!acl) { 18562306a36Sopenharmony_ci ret = -ENOMEM; 18662306a36Sopenharmony_ci goto err_free_str; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci uuid_str = strim(str); 19062306a36Sopenharmony_ci while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) { 19162306a36Sopenharmony_ci size_t len = strlen(s); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (len) { 19462306a36Sopenharmony_ci if (len != UUID_STRING_LEN) { 19562306a36Sopenharmony_ci ret = -EINVAL; 19662306a36Sopenharmony_ci goto err_free_acl; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci ret = uuid_parse(s, &acl[i]); 19962306a36Sopenharmony_ci if (ret) 20062306a36Sopenharmony_ci goto err_free_acl; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci i++; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (s || i < tb->nboot_acl) { 20762306a36Sopenharmony_ci ret = -EINVAL; 20862306a36Sopenharmony_ci goto err_free_acl; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci pm_runtime_get_sync(&tb->dev); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (mutex_lock_interruptible(&tb->lock)) { 21462306a36Sopenharmony_ci ret = -ERESTARTSYS; 21562306a36Sopenharmony_ci goto err_rpm_put; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl); 21862306a36Sopenharmony_ci if (!ret) { 21962306a36Sopenharmony_ci /* Notify userspace about the change */ 22062306a36Sopenharmony_ci kobject_uevent(&tb->dev.kobj, KOBJ_CHANGE); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci mutex_unlock(&tb->lock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cierr_rpm_put: 22562306a36Sopenharmony_ci pm_runtime_mark_last_busy(&tb->dev); 22662306a36Sopenharmony_ci pm_runtime_put_autosuspend(&tb->dev); 22762306a36Sopenharmony_cierr_free_acl: 22862306a36Sopenharmony_ci kfree(acl); 22962306a36Sopenharmony_cierr_free_str: 23062306a36Sopenharmony_ci kfree(str); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return ret ?: count; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(boot_acl); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic ssize_t deauthorization_show(struct device *dev, 23762306a36Sopenharmony_ci struct device_attribute *attr, 23862306a36Sopenharmony_ci char *buf) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci const struct tb *tb = container_of(dev, struct tb, dev); 24162306a36Sopenharmony_ci bool deauthorization = false; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Only meaningful if authorization is supported */ 24462306a36Sopenharmony_ci if (tb->security_level == TB_SECURITY_USER || 24562306a36Sopenharmony_ci tb->security_level == TB_SECURITY_SECURE) 24662306a36Sopenharmony_ci deauthorization = !!tb->cm_ops->disapprove_switch; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", deauthorization); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(deauthorization); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic ssize_t iommu_dma_protection_show(struct device *dev, 25362306a36Sopenharmony_ci struct device_attribute *attr, 25462306a36Sopenharmony_ci char *buf) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", tb->nhi->iommu_dma_protection); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(iommu_dma_protection); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic ssize_t security_show(struct device *dev, struct device_attribute *attr, 26362306a36Sopenharmony_ci char *buf) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 26662306a36Sopenharmony_ci const char *name = "unknown"; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (tb->security_level < ARRAY_SIZE(tb_security_names)) 26962306a36Sopenharmony_ci name = tb_security_names[tb->security_level]; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", name); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(security); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic struct attribute *domain_attrs[] = { 27662306a36Sopenharmony_ci &dev_attr_boot_acl.attr, 27762306a36Sopenharmony_ci &dev_attr_deauthorization.attr, 27862306a36Sopenharmony_ci &dev_attr_iommu_dma_protection.attr, 27962306a36Sopenharmony_ci &dev_attr_security.attr, 28062306a36Sopenharmony_ci NULL, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic umode_t domain_attr_is_visible(struct kobject *kobj, 28462306a36Sopenharmony_ci struct attribute *attr, int n) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 28762306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (attr == &dev_attr_boot_acl.attr) { 29062306a36Sopenharmony_ci if (tb->nboot_acl && 29162306a36Sopenharmony_ci tb->cm_ops->get_boot_acl && 29262306a36Sopenharmony_ci tb->cm_ops->set_boot_acl) 29362306a36Sopenharmony_ci return attr->mode; 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return attr->mode; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic const struct attribute_group domain_attr_group = { 30162306a36Sopenharmony_ci .is_visible = domain_attr_is_visible, 30262306a36Sopenharmony_ci .attrs = domain_attrs, 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic const struct attribute_group *domain_attr_groups[] = { 30662306a36Sopenharmony_ci &domain_attr_group, 30762306a36Sopenharmony_ci NULL, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistruct bus_type tb_bus_type = { 31162306a36Sopenharmony_ci .name = "thunderbolt", 31262306a36Sopenharmony_ci .match = tb_service_match, 31362306a36Sopenharmony_ci .probe = tb_service_probe, 31462306a36Sopenharmony_ci .remove = tb_service_remove, 31562306a36Sopenharmony_ci .shutdown = tb_service_shutdown, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void tb_domain_release(struct device *dev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct tb *tb = container_of(dev, struct tb, dev); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci tb_ctl_free(tb->ctl); 32362306a36Sopenharmony_ci destroy_workqueue(tb->wq); 32462306a36Sopenharmony_ci ida_simple_remove(&tb_domain_ida, tb->index); 32562306a36Sopenharmony_ci mutex_destroy(&tb->lock); 32662306a36Sopenharmony_ci kfree(tb); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistruct device_type tb_domain_type = { 33062306a36Sopenharmony_ci .name = "thunderbolt_domain", 33162306a36Sopenharmony_ci .release = tb_domain_release, 33262306a36Sopenharmony_ci}; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, 33562306a36Sopenharmony_ci const void *buf, size_t size) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct tb *tb = data; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (!tb->cm_ops->handle_event) { 34062306a36Sopenharmony_ci tb_warn(tb, "domain does not have event handler\n"); 34162306a36Sopenharmony_ci return true; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci switch (type) { 34562306a36Sopenharmony_ci case TB_CFG_PKG_XDOMAIN_REQ: 34662306a36Sopenharmony_ci case TB_CFG_PKG_XDOMAIN_RESP: 34762306a36Sopenharmony_ci if (tb_is_xdomain_enabled()) 34862306a36Sopenharmony_ci return tb_xdomain_handle_request(tb, type, buf, size); 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci default: 35262306a36Sopenharmony_ci tb->cm_ops->handle_event(tb, type, buf, size); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return true; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/** 35962306a36Sopenharmony_ci * tb_domain_alloc() - Allocate a domain 36062306a36Sopenharmony_ci * @nhi: Pointer to the host controller 36162306a36Sopenharmony_ci * @timeout_msec: Control channel timeout for non-raw messages 36262306a36Sopenharmony_ci * @privsize: Size of the connection manager private data 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Allocates and initializes a new Thunderbolt domain. Connection 36562306a36Sopenharmony_ci * managers are expected to call this and then fill in @cm_ops 36662306a36Sopenharmony_ci * accordingly. 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Call tb_domain_put() to release the domain before it has been added 36962306a36Sopenharmony_ci * to the system. 37062306a36Sopenharmony_ci * 37162306a36Sopenharmony_ci * Return: allocated domain structure on %NULL in case of error 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistruct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct tb *tb; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * Make sure the structure sizes map with that the hardware 37962306a36Sopenharmony_ci * expects because bit-fields are being used. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4); 38262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4); 38362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci tb = kzalloc(sizeof(*tb) + privsize, GFP_KERNEL); 38662306a36Sopenharmony_ci if (!tb) 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci tb->nhi = nhi; 39062306a36Sopenharmony_ci mutex_init(&tb->lock); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci tb->index = ida_simple_get(&tb_domain_ida, 0, 0, GFP_KERNEL); 39362306a36Sopenharmony_ci if (tb->index < 0) 39462306a36Sopenharmony_ci goto err_free; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci tb->wq = alloc_ordered_workqueue("thunderbolt%d", 0, tb->index); 39762306a36Sopenharmony_ci if (!tb->wq) 39862306a36Sopenharmony_ci goto err_remove_ida; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci tb->ctl = tb_ctl_alloc(nhi, timeout_msec, tb_domain_event_cb, tb); 40162306a36Sopenharmony_ci if (!tb->ctl) 40262306a36Sopenharmony_ci goto err_destroy_wq; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci tb->dev.parent = &nhi->pdev->dev; 40562306a36Sopenharmony_ci tb->dev.bus = &tb_bus_type; 40662306a36Sopenharmony_ci tb->dev.type = &tb_domain_type; 40762306a36Sopenharmony_ci tb->dev.groups = domain_attr_groups; 40862306a36Sopenharmony_ci dev_set_name(&tb->dev, "domain%d", tb->index); 40962306a36Sopenharmony_ci device_initialize(&tb->dev); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return tb; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cierr_destroy_wq: 41462306a36Sopenharmony_ci destroy_workqueue(tb->wq); 41562306a36Sopenharmony_cierr_remove_ida: 41662306a36Sopenharmony_ci ida_simple_remove(&tb_domain_ida, tb->index); 41762306a36Sopenharmony_cierr_free: 41862306a36Sopenharmony_ci kfree(tb); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return NULL; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/** 42462306a36Sopenharmony_ci * tb_domain_add() - Add domain to the system 42562306a36Sopenharmony_ci * @tb: Domain to add 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * Starts the domain and adds it to the system. Hotplugging devices will 42862306a36Sopenharmony_ci * work after this has been returned successfully. In order to remove 42962306a36Sopenharmony_ci * and release the domain after this function has been called, call 43062306a36Sopenharmony_ci * tb_domain_remove(). 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * Return: %0 in case of success and negative errno in case of error 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ciint tb_domain_add(struct tb *tb) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (WARN_ON(!tb->cm_ops)) 43962306a36Sopenharmony_ci return -EINVAL; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci mutex_lock(&tb->lock); 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * tb_schedule_hotplug_handler may be called as soon as the config 44462306a36Sopenharmony_ci * channel is started. Thats why we have to hold the lock here. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci tb_ctl_start(tb->ctl); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (tb->cm_ops->driver_ready) { 44962306a36Sopenharmony_ci ret = tb->cm_ops->driver_ready(tb); 45062306a36Sopenharmony_ci if (ret) 45162306a36Sopenharmony_ci goto err_ctl_stop; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci tb_dbg(tb, "security level set to %s\n", 45562306a36Sopenharmony_ci tb_security_names[tb->security_level]); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = device_add(&tb->dev); 45862306a36Sopenharmony_ci if (ret) 45962306a36Sopenharmony_ci goto err_ctl_stop; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Start the domain */ 46262306a36Sopenharmony_ci if (tb->cm_ops->start) { 46362306a36Sopenharmony_ci ret = tb->cm_ops->start(tb); 46462306a36Sopenharmony_ci if (ret) 46562306a36Sopenharmony_ci goto err_domain_del; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* This starts event processing */ 46962306a36Sopenharmony_ci mutex_unlock(&tb->lock); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci device_init_wakeup(&tb->dev, true); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci pm_runtime_no_callbacks(&tb->dev); 47462306a36Sopenharmony_ci pm_runtime_set_active(&tb->dev); 47562306a36Sopenharmony_ci pm_runtime_enable(&tb->dev); 47662306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&tb->dev, TB_AUTOSUSPEND_DELAY); 47762306a36Sopenharmony_ci pm_runtime_mark_last_busy(&tb->dev); 47862306a36Sopenharmony_ci pm_runtime_use_autosuspend(&tb->dev); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cierr_domain_del: 48362306a36Sopenharmony_ci device_del(&tb->dev); 48462306a36Sopenharmony_cierr_ctl_stop: 48562306a36Sopenharmony_ci tb_ctl_stop(tb->ctl); 48662306a36Sopenharmony_ci mutex_unlock(&tb->lock); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/** 49262306a36Sopenharmony_ci * tb_domain_remove() - Removes and releases a domain 49362306a36Sopenharmony_ci * @tb: Domain to remove 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * Stops the domain, removes it from the system and releases all 49662306a36Sopenharmony_ci * resources once the last reference has been released. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_civoid tb_domain_remove(struct tb *tb) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci mutex_lock(&tb->lock); 50162306a36Sopenharmony_ci if (tb->cm_ops->stop) 50262306a36Sopenharmony_ci tb->cm_ops->stop(tb); 50362306a36Sopenharmony_ci /* Stop the domain control traffic */ 50462306a36Sopenharmony_ci tb_ctl_stop(tb->ctl); 50562306a36Sopenharmony_ci mutex_unlock(&tb->lock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci flush_workqueue(tb->wq); 50862306a36Sopenharmony_ci device_unregister(&tb->dev); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/** 51262306a36Sopenharmony_ci * tb_domain_suspend_noirq() - Suspend a domain 51362306a36Sopenharmony_ci * @tb: Domain to suspend 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * Suspends all devices in the domain and stops the control channel. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ciint tb_domain_suspend_noirq(struct tb *tb) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int ret = 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * The control channel interrupt is left enabled during suspend 52362306a36Sopenharmony_ci * and taking the lock here prevents any events happening before 52462306a36Sopenharmony_ci * we actually have stopped the domain and the control channel. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci mutex_lock(&tb->lock); 52762306a36Sopenharmony_ci if (tb->cm_ops->suspend_noirq) 52862306a36Sopenharmony_ci ret = tb->cm_ops->suspend_noirq(tb); 52962306a36Sopenharmony_ci if (!ret) 53062306a36Sopenharmony_ci tb_ctl_stop(tb->ctl); 53162306a36Sopenharmony_ci mutex_unlock(&tb->lock); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return ret; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * tb_domain_resume_noirq() - Resume a domain 53862306a36Sopenharmony_ci * @tb: Domain to resume 53962306a36Sopenharmony_ci * 54062306a36Sopenharmony_ci * Re-starts the control channel, and resumes all devices connected to 54162306a36Sopenharmony_ci * the domain. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ciint tb_domain_resume_noirq(struct tb *tb) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci int ret = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci mutex_lock(&tb->lock); 54862306a36Sopenharmony_ci tb_ctl_start(tb->ctl); 54962306a36Sopenharmony_ci if (tb->cm_ops->resume_noirq) 55062306a36Sopenharmony_ci ret = tb->cm_ops->resume_noirq(tb); 55162306a36Sopenharmony_ci mutex_unlock(&tb->lock); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciint tb_domain_suspend(struct tb *tb) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci return tb->cm_ops->suspend ? tb->cm_ops->suspend(tb) : 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciint tb_domain_freeze_noirq(struct tb *tb) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int ret = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mutex_lock(&tb->lock); 56662306a36Sopenharmony_ci if (tb->cm_ops->freeze_noirq) 56762306a36Sopenharmony_ci ret = tb->cm_ops->freeze_noirq(tb); 56862306a36Sopenharmony_ci if (!ret) 56962306a36Sopenharmony_ci tb_ctl_stop(tb->ctl); 57062306a36Sopenharmony_ci mutex_unlock(&tb->lock); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return ret; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciint tb_domain_thaw_noirq(struct tb *tb) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci int ret = 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci mutex_lock(&tb->lock); 58062306a36Sopenharmony_ci tb_ctl_start(tb->ctl); 58162306a36Sopenharmony_ci if (tb->cm_ops->thaw_noirq) 58262306a36Sopenharmony_ci ret = tb->cm_ops->thaw_noirq(tb); 58362306a36Sopenharmony_ci mutex_unlock(&tb->lock); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_civoid tb_domain_complete(struct tb *tb) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci if (tb->cm_ops->complete) 59162306a36Sopenharmony_ci tb->cm_ops->complete(tb); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ciint tb_domain_runtime_suspend(struct tb *tb) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci if (tb->cm_ops->runtime_suspend) { 59762306a36Sopenharmony_ci int ret = tb->cm_ops->runtime_suspend(tb); 59862306a36Sopenharmony_ci if (ret) 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci tb_ctl_stop(tb->ctl); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciint tb_domain_runtime_resume(struct tb *tb) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci tb_ctl_start(tb->ctl); 60862306a36Sopenharmony_ci if (tb->cm_ops->runtime_resume) { 60962306a36Sopenharmony_ci int ret = tb->cm_ops->runtime_resume(tb); 61062306a36Sopenharmony_ci if (ret) 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/** 61762306a36Sopenharmony_ci * tb_domain_disapprove_switch() - Disapprove switch 61862306a36Sopenharmony_ci * @tb: Domain the switch belongs to 61962306a36Sopenharmony_ci * @sw: Switch to disapprove 62062306a36Sopenharmony_ci * 62162306a36Sopenharmony_ci * This will disconnect PCIe tunnel from parent to this @sw. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * Return: %0 on success and negative errno in case of failure. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ciint tb_domain_disapprove_switch(struct tb *tb, struct tb_switch *sw) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci if (!tb->cm_ops->disapprove_switch) 62862306a36Sopenharmony_ci return -EPERM; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return tb->cm_ops->disapprove_switch(tb, sw); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/** 63462306a36Sopenharmony_ci * tb_domain_approve_switch() - Approve switch 63562306a36Sopenharmony_ci * @tb: Domain the switch belongs to 63662306a36Sopenharmony_ci * @sw: Switch to approve 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * This will approve switch by connection manager specific means. In 63962306a36Sopenharmony_ci * case of success the connection manager will create PCIe tunnel from 64062306a36Sopenharmony_ci * parent to @sw. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ciint tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct tb_switch *parent_sw; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (!tb->cm_ops->approve_switch) 64762306a36Sopenharmony_ci return -EPERM; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* The parent switch must be authorized before this one */ 65062306a36Sopenharmony_ci parent_sw = tb_to_switch(sw->dev.parent); 65162306a36Sopenharmony_ci if (!parent_sw || !parent_sw->authorized) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return tb->cm_ops->approve_switch(tb, sw); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/** 65862306a36Sopenharmony_ci * tb_domain_approve_switch_key() - Approve switch and add key 65962306a36Sopenharmony_ci * @tb: Domain the switch belongs to 66062306a36Sopenharmony_ci * @sw: Switch to approve 66162306a36Sopenharmony_ci * 66262306a36Sopenharmony_ci * For switches that support secure connect, this function first adds 66362306a36Sopenharmony_ci * key to the switch NVM using connection manager specific means. If 66462306a36Sopenharmony_ci * adding the key is successful, the switch is approved and connected. 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * Return: %0 on success and negative errno in case of failure. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ciint tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct tb_switch *parent_sw; 67162306a36Sopenharmony_ci int ret; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (!tb->cm_ops->approve_switch || !tb->cm_ops->add_switch_key) 67462306a36Sopenharmony_ci return -EPERM; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* The parent switch must be authorized before this one */ 67762306a36Sopenharmony_ci parent_sw = tb_to_switch(sw->dev.parent); 67862306a36Sopenharmony_ci if (!parent_sw || !parent_sw->authorized) 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci ret = tb->cm_ops->add_switch_key(tb, sw); 68262306a36Sopenharmony_ci if (ret) 68362306a36Sopenharmony_ci return ret; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return tb->cm_ops->approve_switch(tb, sw); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * tb_domain_challenge_switch_key() - Challenge and approve switch 69062306a36Sopenharmony_ci * @tb: Domain the switch belongs to 69162306a36Sopenharmony_ci * @sw: Switch to approve 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * For switches that support secure connect, this function generates 69462306a36Sopenharmony_ci * random challenge and sends it to the switch. The switch responds to 69562306a36Sopenharmony_ci * this and if the response matches our random challenge, the switch is 69662306a36Sopenharmony_ci * approved and connected. 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Return: %0 on success and negative errno in case of failure. 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ciint tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci u8 challenge[TB_SWITCH_KEY_SIZE]; 70362306a36Sopenharmony_ci u8 response[TB_SWITCH_KEY_SIZE]; 70462306a36Sopenharmony_ci u8 hmac[TB_SWITCH_KEY_SIZE]; 70562306a36Sopenharmony_ci struct tb_switch *parent_sw; 70662306a36Sopenharmony_ci struct crypto_shash *tfm; 70762306a36Sopenharmony_ci struct shash_desc *shash; 70862306a36Sopenharmony_ci int ret; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (!tb->cm_ops->approve_switch || !tb->cm_ops->challenge_switch_key) 71162306a36Sopenharmony_ci return -EPERM; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* The parent switch must be authorized before this one */ 71462306a36Sopenharmony_ci parent_sw = tb_to_switch(sw->dev.parent); 71562306a36Sopenharmony_ci if (!parent_sw || !parent_sw->authorized) 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci get_random_bytes(challenge, sizeof(challenge)); 71962306a36Sopenharmony_ci ret = tb->cm_ops->challenge_switch_key(tb, sw, challenge, response); 72062306a36Sopenharmony_ci if (ret) 72162306a36Sopenharmony_ci return ret; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); 72462306a36Sopenharmony_ci if (IS_ERR(tfm)) 72562306a36Sopenharmony_ci return PTR_ERR(tfm); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ret = crypto_shash_setkey(tfm, sw->key, TB_SWITCH_KEY_SIZE); 72862306a36Sopenharmony_ci if (ret) 72962306a36Sopenharmony_ci goto err_free_tfm; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), 73262306a36Sopenharmony_ci GFP_KERNEL); 73362306a36Sopenharmony_ci if (!shash) { 73462306a36Sopenharmony_ci ret = -ENOMEM; 73562306a36Sopenharmony_ci goto err_free_tfm; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci shash->tfm = tfm; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci memset(hmac, 0, sizeof(hmac)); 74162306a36Sopenharmony_ci ret = crypto_shash_digest(shash, challenge, sizeof(hmac), hmac); 74262306a36Sopenharmony_ci if (ret) 74362306a36Sopenharmony_ci goto err_free_shash; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* The returned HMAC must match the one we calculated */ 74662306a36Sopenharmony_ci if (memcmp(response, hmac, sizeof(hmac))) { 74762306a36Sopenharmony_ci ret = -EKEYREJECTED; 74862306a36Sopenharmony_ci goto err_free_shash; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci crypto_free_shash(tfm); 75262306a36Sopenharmony_ci kfree(shash); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return tb->cm_ops->approve_switch(tb, sw); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cierr_free_shash: 75762306a36Sopenharmony_ci kfree(shash); 75862306a36Sopenharmony_cierr_free_tfm: 75962306a36Sopenharmony_ci crypto_free_shash(tfm); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/** 76562306a36Sopenharmony_ci * tb_domain_disconnect_pcie_paths() - Disconnect all PCIe paths 76662306a36Sopenharmony_ci * @tb: Domain whose PCIe paths to disconnect 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * This needs to be called in preparation for NVM upgrade of the host 76962306a36Sopenharmony_ci * controller. Makes sure all PCIe paths are disconnected. 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * Return %0 on success and negative errno in case of error. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ciint tb_domain_disconnect_pcie_paths(struct tb *tb) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci if (!tb->cm_ops->disconnect_pcie_paths) 77662306a36Sopenharmony_ci return -EPERM; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return tb->cm_ops->disconnect_pcie_paths(tb); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/** 78262306a36Sopenharmony_ci * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain 78362306a36Sopenharmony_ci * @tb: Domain enabling the DMA paths 78462306a36Sopenharmony_ci * @xd: XDomain DMA paths are created to 78562306a36Sopenharmony_ci * @transmit_path: HopID we are using to send out packets 78662306a36Sopenharmony_ci * @transmit_ring: DMA ring used to send out packets 78762306a36Sopenharmony_ci * @receive_path: HopID the other end is using to send packets to us 78862306a36Sopenharmony_ci * @receive_ring: DMA ring used to receive packets from @receive_path 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * Calls connection manager specific method to enable DMA paths to the 79162306a36Sopenharmony_ci * XDomain in question. 79262306a36Sopenharmony_ci * 79362306a36Sopenharmony_ci * Return: 0% in case of success and negative errno otherwise. In 79462306a36Sopenharmony_ci * particular returns %-ENOTSUPP if the connection manager 79562306a36Sopenharmony_ci * implementation does not support XDomains. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ciint tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 79862306a36Sopenharmony_ci int transmit_path, int transmit_ring, 79962306a36Sopenharmony_ci int receive_path, int receive_ring) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci if (!tb->cm_ops->approve_xdomain_paths) 80262306a36Sopenharmony_ci return -ENOTSUPP; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return tb->cm_ops->approve_xdomain_paths(tb, xd, transmit_path, 80562306a36Sopenharmony_ci transmit_ring, receive_path, receive_ring); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci/** 80962306a36Sopenharmony_ci * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain 81062306a36Sopenharmony_ci * @tb: Domain disabling the DMA paths 81162306a36Sopenharmony_ci * @xd: XDomain whose DMA paths are disconnected 81262306a36Sopenharmony_ci * @transmit_path: HopID we are using to send out packets 81362306a36Sopenharmony_ci * @transmit_ring: DMA ring used to send out packets 81462306a36Sopenharmony_ci * @receive_path: HopID the other end is using to send packets to us 81562306a36Sopenharmony_ci * @receive_ring: DMA ring used to receive packets from @receive_path 81662306a36Sopenharmony_ci * 81762306a36Sopenharmony_ci * Calls connection manager specific method to disconnect DMA paths to 81862306a36Sopenharmony_ci * the XDomain in question. 81962306a36Sopenharmony_ci * 82062306a36Sopenharmony_ci * Return: 0% in case of success and negative errno otherwise. In 82162306a36Sopenharmony_ci * particular returns %-ENOTSUPP if the connection manager 82262306a36Sopenharmony_ci * implementation does not support XDomains. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ciint tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 82562306a36Sopenharmony_ci int transmit_path, int transmit_ring, 82662306a36Sopenharmony_ci int receive_path, int receive_ring) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci if (!tb->cm_ops->disconnect_xdomain_paths) 82962306a36Sopenharmony_ci return -ENOTSUPP; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, 83262306a36Sopenharmony_ci transmit_ring, receive_path, receive_ring); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int disconnect_xdomain(struct device *dev, void *data) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct tb_xdomain *xd; 83862306a36Sopenharmony_ci struct tb *tb = data; 83962306a36Sopenharmony_ci int ret = 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci xd = tb_to_xdomain(dev); 84262306a36Sopenharmony_ci if (xd && xd->tb == tb) 84362306a36Sopenharmony_ci ret = tb_xdomain_disable_all_paths(xd); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return ret; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/** 84962306a36Sopenharmony_ci * tb_domain_disconnect_all_paths() - Disconnect all paths for the domain 85062306a36Sopenharmony_ci * @tb: Domain whose paths are disconnected 85162306a36Sopenharmony_ci * 85262306a36Sopenharmony_ci * This function can be used to disconnect all paths (PCIe, XDomain) for 85362306a36Sopenharmony_ci * example in preparation for host NVM firmware upgrade. After this is 85462306a36Sopenharmony_ci * called the paths cannot be established without resetting the switch. 85562306a36Sopenharmony_ci * 85662306a36Sopenharmony_ci * Return: %0 in case of success and negative errno otherwise. 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_ciint tb_domain_disconnect_all_paths(struct tb *tb) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci int ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci ret = tb_domain_disconnect_pcie_paths(tb); 86362306a36Sopenharmony_ci if (ret) 86462306a36Sopenharmony_ci return ret; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ciint tb_domain_init(void) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci int ret; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci tb_debugfs_init(); 87462306a36Sopenharmony_ci tb_acpi_init(); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ret = tb_xdomain_init(); 87762306a36Sopenharmony_ci if (ret) 87862306a36Sopenharmony_ci goto err_acpi; 87962306a36Sopenharmony_ci ret = bus_register(&tb_bus_type); 88062306a36Sopenharmony_ci if (ret) 88162306a36Sopenharmony_ci goto err_xdomain; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cierr_xdomain: 88662306a36Sopenharmony_ci tb_xdomain_exit(); 88762306a36Sopenharmony_cierr_acpi: 88862306a36Sopenharmony_ci tb_acpi_exit(); 88962306a36Sopenharmony_ci tb_debugfs_exit(); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_civoid tb_domain_exit(void) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci bus_unregister(&tb_bus_type); 89762306a36Sopenharmony_ci ida_destroy(&tb_domain_ida); 89862306a36Sopenharmony_ci tb_nvm_exit(); 89962306a36Sopenharmony_ci tb_xdomain_exit(); 90062306a36Sopenharmony_ci tb_acpi_exit(); 90162306a36Sopenharmony_ci tb_debugfs_exit(); 90262306a36Sopenharmony_ci} 903