// SPDX-License-Identifier: GPL-2.0/* * Thunderbolt driver - switch/port utility functions * * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> * Copyright (C) 2018, Intel Corporation */ #include <linux/delay.h>#include <linux/hex.h>#include <linux/idr.h>#include <linux/module.h>#include <linux/nvmem-provider.h>#include <linux/pm_runtime.h>#include <linux/sched/signal.h>#include <linux/sizes.h>#include <linux/slab.h>#include <linux/string_helpers.h> #include "tb.h" /* Switch NVM support */ struct nvm_auth_status { struct list_head list; uuid_t uuid; u32 status;}; /* * Hold NVM authentication failure status per switch This information * needs to stay around even when the switch gets power cycled so we * keep it separately. */static LIST_HEAD(nvm_auth_status_cache);static DEFINE_MUTEX(nvm_auth_status_lock); static struct nvm_auth_status *__nvm_get_auth_status(const struct tb_switch *sw){ struct nvm_auth_status *st; list_for_each_entry(st, &nvm_auth_status_cache, list) { if (uuid_equal(&st->uuid, sw->uuid)) return st; } return NULL;} static void nvm_get_auth_status(const struct tb_switch *sw, u32 *status){ struct nvm_auth_status *st; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); mutex_unlock(&nvm_auth_status_lock); *status = st ? st->status : 0;} static void nvm_set_auth_status(const struct tb_switch *sw, u32 status){ struct nvm_auth_status *st; if (WARN_ON(!sw->uuid)) return; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); if (!st) { st = kzalloc_obj(*st); if (!st) goto unlock; memcpy(&st->uuid, sw->uuid, sizeof(st->uuid)); INIT_LIST_HEAD(&st->list); list_add_tail(&st->list, &nvm_auth_status_cache); } st->status = status;unlock: mutex_unlock(&nvm_auth_status_lock);} static void nvm_clear_auth_status(const struct tb_switch *sw){ struct nvm_auth_status *st; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); if (st) { list_del(&st->list); kfree(st); } mutex_unlock(&nvm_auth_status_lock);} static int nvm_validate_and_write(struct tb_switch *sw){ unsigned int image_size; const u8 *buf; int ret; ret = tb_nvm_validate(sw->nvm); if (ret) return ret; ret = tb_nvm_write_headers(sw->nvm); if (ret) return ret; buf = sw->nvm->buf_data_start; image_size = sw->nvm->buf_data_size; if (tb_switch_is_usb4(sw)) ret = usb4_switch_nvm_write(sw, 0, buf, image_size); else ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size); if (ret) return ret; sw->nvm->flushed = true; return 0;} static int nvm_authenticate_host_dma_port(struct tb_switch *sw){ int ret = 0; /* * Root switch NVM upgrade requires that we disconnect the * existing paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { u32 status; ret = tb_domain_disconnect_all_paths(sw->tb); if (ret) return ret; /* * The host controller goes away pretty soon after this if * everything goes well so getting timeout is expected. */ ret = dma_port_flash_update_auth(sw->dma_port); if (!ret || ret == -ETIMEDOUT) return 0; /* * Any error from update auth operation requires power * cycling of the host router. */ tb_sw_warn(sw, "failed to authenticate NVM, power cycling\n"); if (dma_port_flash_update_auth_status(sw->dma_port, &status) > 0) nvm_set_auth_status(sw, status); } /* * From safe mode we can get out by just power cycling the * switch. */ dma_port_power_cycle(sw->dma_port); return ret;} static int nvm_authenticate_device_dma_port(struct tb_switch *sw){ int ret, retries = 10; ret = dma_port_flash_update_auth(sw->dma_port); switch (ret) { case 0: case -ETIMEDOUT: case -EACCES: case -EINVAL: /* Power cycle is required */ break; default: return ret; } /* * Poll here for the authentication status. It takes some time * for the device to respond (we get timeout for a while). Once * we get response the device needs to be power cycled in order * to the new NVM to be taken into use. */ do { u32 status; ret = dma_port_flash_update_auth_status(sw->dma_port, &status); if (ret < 0 && ret != -ETIMEDOUT) return ret; if (ret > 0) { if (status) { tb_sw_warn(sw, "failed to authenticate NVM\n"); nvm_set_auth_status(sw, status); } tb_sw_info(sw, "power cycling the switch now\n"); dma_port_power_cycle(sw->dma_port); return 0; } msleep(500); } while (--retries); return -ETIMEDOUT;} static inline bool nvm_readable(struct tb_switch *sw){ if (tb_switch_is_usb4(sw)) { /* * USB4 devices must support NVM operations but it is * optional for hosts. Therefore we query the NVM sector * size here and if it is supported assume NVM * operations are implemented. */ return usb4_switch_nvm_sector_size(sw) > 0; } /* Thunderbolt 2 and 3 devices support NVM through DMA port */ return !!sw->dma_port;} static inline bool nvm_upgradeable(struct tb_switch *sw){ if (sw->no_nvm_upgrade) return false; return nvm_readable(sw);} static int nvm_authenticate(struct tb_switch *sw, bool auth_only){ struct tb_nhi *nhi = sw->tb->nhi; int ret; if (tb_switch_is_usb4(sw)) { if (auth_only) { ret = usb4_switch_nvm_set_offset(sw, 0); if (ret) return ret; } sw->nvm->authenticating = true; return usb4_switch_nvm_authenticate(sw); } if (auth_only) return -EOPNOTSUPP; sw->nvm->authenticating = true; if (!tb_route(sw)) { if (nhi->ops->pre_nvm_auth) nhi->ops->pre_nvm_auth(nhi); ret = nvm_authenticate_host_dma_port(sw); } else { ret = nvm_authenticate_device_dma_port(sw); } return ret;} /** * tb_switch_nvm_read() - Read router NVM * @sw: Router whose NVM to read * @address: Start address on the NVM * @buf: Buffer where the read data is copied * @size: Size of the buffer in bytes * * Reads from router NVM and returns the requested data in @buf. Locking * is up to the caller. * * Return: %0 on success, negative errno otherwise. */int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, size_t size){ if (tb_switch_is_usb4(sw)) return usb4_switch_nvm_read(sw, address, buf, size); return dma_port_flash_read(sw->dma_port, address, buf, size);} static int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes){ struct tb_nvm *nvm = priv; struct tb_switch *sw = tb_to_switch(nvm->dev); int ret; pm_runtime_get_sync(&sw->dev); if (!mutex_trylock(&sw->tb->lock)) { ret = restart_syscall(); goto out; } ret = tb_switch_nvm_read(sw, offset, val, bytes); mutex_unlock(&sw->tb->lock); out: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); return ret;} static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes){ struct tb_nvm *nvm = priv; struct tb_switch *sw = tb_to_switch(nvm->dev); int ret; if (!mutex_trylock(&sw->tb->lock)) return restart_syscall(); /* * Since writing the NVM image might require some special steps, * for example when CSS headers are written, we cache the image * locally here and handle the special cases when the user asks * us to authenticate the image. */ ret = tb_nvm_write_buf(nvm, offset, val, bytes); mutex_unlock(&sw->tb->lock); return ret;} static int tb_switch_nvm_init(struct tb_switch *sw){ struct tb_nvm *nvm; int ret; if (!nvm_readable(sw)) return 0; nvm = tb_nvm_alloc(&sw->dev); if (IS_ERR(nvm)) { ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm); goto err_nvm; } ret = tb_nvm_read_version(nvm); if (ret) goto err_nvm; sw->nvm = nvm; return 0; err_nvm: tb_sw_dbg(sw, "NVM upgrade disabled\n"); sw->no_nvm_upgrade = true; if (!IS_ERR(nvm)) tb_nvm_free(nvm); return ret;} static int tb_switch_nvm_add(struct tb_switch *sw){ struct tb_nvm *nvm = sw->nvm; int ret; if (!nvm) return 0; /* * If the switch is in safe-mode the only accessible portion of * the NVM is the non-active one where userspace is expected to * write new functional NVM. */ if (!sw->safe_mode) { ret = tb_nvm_add_active(nvm, nvm_read); if (ret) goto err_nvm; tb_sw_dbg(sw, "NVM version %x.%x\n", nvm->major, nvm->minor); } if (!sw->no_nvm_upgrade) { ret = tb_nvm_add_non_active(nvm, nvm_write); if (ret) goto err_nvm; } return 0; err_nvm: tb_sw_dbg(sw, "NVM upgrade disabled\n"); sw->no_nvm_upgrade = true; tb_nvm_free(nvm); return ret;} static void tb_switch_nvm_remove(struct tb_switch *sw){ struct tb_nvm *nvm; nvm = sw->nvm; sw->nvm = NULL; if (!nvm) return; /* Remove authentication status in case the switch is unplugged */ if (!nvm->authenticating) nvm_clear_auth_status(sw); tb_nvm_free(nvm);} /* port utility functions */ static const char *tb_port_type(const struct tb_regs_port_header *port){ switch (port->type >> 16) { case 0: switch ((u8) port->type) { case 0: return "Inactive"; case 1: return "Port"; case 2: return "NHI"; default: return "unknown"; } case 0x2: return "Ethernet"; case 0x8: return "SATA"; case 0xe: return "DP/HDMI"; case 0x10: return "PCIe"; case 0x20: return "USB"; default: return "unknown"; }} static void tb_dump_port(struct tb *tb, const struct tb_port *port){ const struct tb_regs_port_header *regs = &port->config; tb_dbg(tb, " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", regs->port_number, regs->vendor_id, regs->device_id, regs->revision, regs->thunderbolt_version, tb_port_type(regs), regs->type); tb_dbg(tb, " Max hop id (in/out): %d/%d\n", regs->max_in_hop_id, regs->max_out_hop_id); tb_dbg(tb, " Max counters: %d\n", regs->max_counters); tb_dbg(tb, " NFC Credits: %#x\n", regs->nfc_credits); tb_dbg(tb, " Credits (total/control): %u/%u\n", port->total_credits, port->ctl_credits);} /** * tb_port_state() - get connectedness state of a port * @port: the port to check * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: &enum tb_port_state or negative error code on failure. */int tb_port_state(struct tb_port *port){ struct tb_cap_phy phy; int res; if (port->cap_phy == 0) { tb_port_WARN(port, "does not have a PHY\n"); return -EINVAL; } res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2); if (res) return res; return phy.state;} /** * tb_wait_for_port() - wait for a port to become ready * @port: Port to wait * @wait_if_unplugged: Wait also when port is unplugged * * Wait up to 1 second for a port to reach state TB_PORT_UP. If * wait_if_unplugged is set then we also wait if the port is in state * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after * switch resume). Otherwise we only wait if a device is registered but the link * has not yet been established. * * Return: * * %0 - If the port is not connected or failed to reach * state %TB_PORT_UP within one second. * * %1 - If the port is connected and in state %TB_PORT_UP. * * Negative errno - An error occurred. */int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged){ int retries = 10;Lines 1–500 of 4044. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt driver - switch/port utility functions * * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> * Copyright (C) 2018, Intel Corporation */ #include <linux/delay.h> #include <linux/hex.h> #include <linux/idr.h> #include <linux/module.h> #include <linux/nvmem-provider.h> #include <linux/pm_runtime.h> #include <linux/sched/signal.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/string_helpers.h> #include "tb.h" /* Switch NVM support */ struct nvm_auth_status { struct list_head list; uuid_t uuid; u32 status; }; /* * Hold NVM authentication failure status per switch This information * needs to stay around even when the switch gets power cycled so we * keep it separately. */ static LIST_HEAD(nvm_auth_status_cache); static DEFINE_MUTEX(nvm_auth_status_lock); static struct nvm_auth_status *__nvm_get_auth_status(const struct tb_switch *sw) { struct nvm_auth_status *st; list_for_each_entry(st, &nvm_auth_status_cache, list) { if (uuid_equal(&st->uuid, sw->uuid)) return st; } return NULL; } static void nvm_get_auth_status(const struct tb_switch *sw, u32 *status) { struct nvm_auth_status *st; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); mutex_unlock(&nvm_auth_status_lock); *status = st ? st->status : 0; } static void nvm_set_auth_status(const struct tb_switch *sw, u32 status) { struct nvm_auth_status *st; if (WARN_ON(!sw->uuid)) return; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); if (!st) { st = kzalloc_obj(*st); if (!st) goto unlock; memcpy(&st->uuid, sw->uuid, sizeof(st->uuid)); INIT_LIST_HEAD(&st->list); list_add_tail(&st->list, &nvm_auth_status_cache); } st->status = status; unlock: mutex_unlock(&nvm_auth_status_lock); } static void nvm_clear_auth_status(const struct tb_switch *sw) { struct nvm_auth_status *st; mutex_lock(&nvm_auth_status_lock); st = __nvm_get_auth_status(sw); if (st) { list_del(&st->list); kfree(st); } mutex_unlock(&nvm_auth_status_lock); } static int nvm_validate_and_write(struct tb_switch *sw) { unsigned int image_size; const u8 *buf; int ret; ret = tb_nvm_validate(sw->nvm); if (ret) return ret; ret = tb_nvm_write_headers(sw->nvm); if (ret) return ret; buf = sw->nvm->buf_data_start; image_size = sw->nvm->buf_data_size; if (tb_switch_is_usb4(sw)) ret = usb4_switch_nvm_write(sw, 0, buf, image_size); else ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size); if (ret) return ret; sw->nvm->flushed = true; return 0; } static int nvm_authenticate_host_dma_port(struct tb_switch *sw) { int ret = 0; /* * Root switch NVM upgrade requires that we disconnect the * existing paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { u32 status; ret = tb_domain_disconnect_all_paths(sw->tb); if (ret) return ret; /* * The host controller goes away pretty soon after this if * everything goes well so getting timeout is expected. */ ret = dma_port_flash_update_auth(sw->dma_port); if (!ret || ret == -ETIMEDOUT) return 0; /* * Any error from update auth operation requires power * cycling of the host router. */ tb_sw_warn(sw, "failed to authenticate NVM, power cycling\n"); if (dma_port_flash_update_auth_status(sw->dma_port, &status) > 0) nvm_set_auth_status(sw, status); } /* * From safe mode we can get out by just power cycling the * switch. */ dma_port_power_cycle(sw->dma_port); return ret; } static int nvm_authenticate_device_dma_port(struct tb_switch *sw) { int ret, retries = 10; ret = dma_port_flash_update_auth(sw->dma_port); switch (ret) { case 0: case -ETIMEDOUT: case -EACCES: case -EINVAL: /* Power cycle is required */ break; default: return ret; } /* * Poll here for the authentication status. It takes some time * for the device to respond (we get timeout for a while). Once * we get response the device needs to be power cycled in order * to the new NVM to be taken into use. */ do { u32 status; ret = dma_port_flash_update_auth_status(sw->dma_port, &status); if (ret < 0 && ret != -ETIMEDOUT) return ret; if (ret > 0) { if (status) { tb_sw_warn(sw, "failed to authenticate NVM\n"); nvm_set_auth_status(sw, status); } tb_sw_info(sw, "power cycling the switch now\n"); dma_port_power_cycle(sw->dma_port); return 0; } msleep(500); } while (--retries); return -ETIMEDOUT; } static inline bool nvm_readable(struct tb_switch *sw) { if (tb_switch_is_usb4(sw)) { /* * USB4 devices must support NVM operations but it is * optional for hosts. Therefore we query the NVM sector * size here and if it is supported assume NVM * operations are implemented. */ return usb4_switch_nvm_sector_size(sw) > 0; } /* Thunderbolt 2 and 3 devices support NVM through DMA port */ return !!sw->dma_port; } static inline bool nvm_upgradeable(struct tb_switch *sw) { if (sw->no_nvm_upgrade) return false; return nvm_readable(sw); } static int nvm_authenticate(struct tb_switch *sw, bool auth_only) { struct tb_nhi *nhi = sw->tb->nhi; int ret; if (tb_switch_is_usb4(sw)) { if (auth_only) { ret = usb4_switch_nvm_set_offset(sw, 0); if (ret) return ret; } sw->nvm->authenticating = true; return usb4_switch_nvm_authenticate(sw); } if (auth_only) return -EOPNOTSUPP; sw->nvm->authenticating = true; if (!tb_route(sw)) { if (nhi->ops->pre_nvm_auth) nhi->ops->pre_nvm_auth(nhi); ret = nvm_authenticate_host_dma_port(sw); } else { ret = nvm_authenticate_device_dma_port(sw); } return ret; } /** * tb_switch_nvm_read() - Read router NVM * @sw: Router whose NVM to read * @address: Start address on the NVM * @buf: Buffer where the read data is copied * @size: Size of the buffer in bytes * * Reads from router NVM and returns the requested data in @buf. Locking * is up to the caller. * * Return: %0 on success, negative errno otherwise. */ int tb_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, size_t size) { if (tb_switch_is_usb4(sw)) return usb4_switch_nvm_read(sw, address, buf, size); return dma_port_flash_read(sw->dma_port, address, buf, size); } static int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct tb_nvm *nvm = priv; struct tb_switch *sw = tb_to_switch(nvm->dev); int ret; pm_runtime_get_sync(&sw->dev); if (!mutex_trylock(&sw->tb->lock)) { ret = restart_syscall(); goto out; } ret = tb_switch_nvm_read(sw, offset, val, bytes); mutex_unlock(&sw->tb->lock); out: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); return ret; } static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) { struct tb_nvm *nvm = priv; struct tb_switch *sw = tb_to_switch(nvm->dev); int ret; if (!mutex_trylock(&sw->tb->lock)) return restart_syscall(); /* * Since writing the NVM image might require some special steps, * for example when CSS headers are written, we cache the image * locally here and handle the special cases when the user asks * us to authenticate the image. */ ret = tb_nvm_write_buf(nvm, offset, val, bytes); mutex_unlock(&sw->tb->lock); return ret; } static int tb_switch_nvm_init(struct tb_switch *sw) { struct tb_nvm *nvm; int ret; if (!nvm_readable(sw)) return 0; nvm = tb_nvm_alloc(&sw->dev); if (IS_ERR(nvm)) { ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm); goto err_nvm; } ret = tb_nvm_read_version(nvm); if (ret) goto err_nvm; sw->nvm = nvm; return 0; err_nvm: tb_sw_dbg(sw, "NVM upgrade disabled\n"); sw->no_nvm_upgrade = true; if (!IS_ERR(nvm)) tb_nvm_free(nvm); return ret; } static int tb_switch_nvm_add(struct tb_switch *sw) { struct tb_nvm *nvm = sw->nvm; int ret; if (!nvm) return 0; /* * If the switch is in safe-mode the only accessible portion of * the NVM is the non-active one where userspace is expected to * write new functional NVM. */ if (!sw->safe_mode) { ret = tb_nvm_add_active(nvm, nvm_read); if (ret) goto err_nvm; tb_sw_dbg(sw, "NVM version %x.%x\n", nvm->major, nvm->minor); } if (!sw->no_nvm_upgrade) { ret = tb_nvm_add_non_active(nvm, nvm_write); if (ret) goto err_nvm; } return 0; err_nvm: tb_sw_dbg(sw, "NVM upgrade disabled\n"); sw->no_nvm_upgrade = true; tb_nvm_free(nvm); return ret; } static void tb_switch_nvm_remove(struct tb_switch *sw) { struct tb_nvm *nvm; nvm = sw->nvm; sw->nvm = NULL; if (!nvm) return; /* Remove authentication status in case the switch is unplugged */ if (!nvm->authenticating) nvm_clear_auth_status(sw); tb_nvm_free(nvm); } /* port utility functions */ static const char *tb_port_type(const struct tb_regs_port_header *port) { switch (port->type >> 16) { case 0: switch ((u8) port->type) { case 0: return "Inactive"; case 1: return "Port"; case 2: return "NHI"; default: return "unknown"; } case 0x2: return "Ethernet"; case 0x8: return "SATA"; case 0xe: return "DP/HDMI"; case 0x10: return "PCIe"; case 0x20: return "USB"; default: return "unknown"; } } static void tb_dump_port(struct tb *tb, const struct tb_port *port) { const struct tb_regs_port_header *regs = &port->config; tb_dbg(tb, " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", regs->port_number, regs->vendor_id, regs->device_id, regs->revision, regs->thunderbolt_version, tb_port_type(regs), regs->type); tb_dbg(tb, " Max hop id (in/out): %d/%d\n", regs->max_in_hop_id, regs->max_out_hop_id); tb_dbg(tb, " Max counters: %d\n", regs->max_counters); tb_dbg(tb, " NFC Credits: %#x\n", regs->nfc_credits); tb_dbg(tb, " Credits (total/control): %u/%u\n", port->total_credits, port->ctl_credits); } /** * tb_port_state() - get connectedness state of a port * @port: the port to check * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: &enum tb_port_state or negative error code on failure. */ int tb_port_state(struct tb_port *port) { struct tb_cap_phy phy; int res; if (port->cap_phy == 0) { tb_port_WARN(port, "does not have a PHY\n"); return -EINVAL; } res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2); if (res) return res; return phy.state; } /** * tb_wait_for_port() - wait for a port to become ready * @port: Port to wait * @wait_if_unplugged: Wait also when port is unplugged * * Wait up to 1 second for a port to reach state TB_PORT_UP. If * wait_if_unplugged is set then we also wait if the port is in state * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after * switch resume). Otherwise we only wait if a device is registered but the link * has not yet been established. * * Return: * * %0 - If the port is not connected or failed to reach * state %TB_PORT_UP within one second. * * %1 - If the port is connected and in state %TB_PORT_UP. * * Negative errno - An error occurred. */ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) { int retries = 10;