/* * Thunderbolt Cactus Ridge driver - switch/port utility functions * * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> */ #include <linux/delay.h>#include <linux/idr.h>#include <linux/nvmem-provider.h>#include <linux/sizes.h>#include <linux/slab.h>#include <linux/vmalloc.h> #include "tb.h" /* Switch authorization from userspace is serialized by this lock */static DEFINE_MUTEX(switch_lock); /* Switch NVM support */ #define NVM_DEVID 0x05#define NVM_VERSION 0x08#define NVM_CSS 0x10#define NVM_FLASH_SIZE 0x45 #define NVM_MIN_SIZE SZ_32K#define NVM_MAX_SIZE SZ_512K static DEFINE_IDA(nvm_ida); 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(sizeof(*st), GFP_KERNEL); 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, hdr_size; const u8 *buf = sw->nvm->buf; u16 ds_size; int ret; if (!buf) return -EINVAL; image_size = sw->nvm->buf_data_size; if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) return -EINVAL; /* * FARB pointer must point inside the image and must at least * contain parts of the digital section we will be reading here. */ hdr_size = (*(u32 *)buf) & 0xffffff; if (hdr_size + NVM_DEVID + 2 >= image_size) return -EINVAL; /* Digital section start should be aligned to 4k page */ if (!IS_ALIGNED(hdr_size, SZ_4K)) return -EINVAL; /* * Read digital section size and check that it also fits inside * the image. */ ds_size = *(u16 *)(buf + hdr_size); if (ds_size >= image_size) return -EINVAL; if (!sw->safe_mode) { u16 device_id; /* * Make sure the device ID in the image matches the one * we read from the switch config space. */ device_id = *(u16 *)(buf + hdr_size + NVM_DEVID); if (device_id != sw->config.device_id) return -EINVAL; if (sw->generation < 3) { /* Write CSS headers first */ ret = dma_port_flash_write(sw->dma_port, DMA_PORT_CSS_ADDRESS, buf + NVM_CSS, DMA_PORT_CSS_MAX_SIZE); if (ret) return ret; } /* Skip headers in the image */ buf += hdr_size; image_size -= hdr_size; } return dma_port_flash_write(sw->dma_port, 0, buf, image_size);} static int nvm_authenticate_host(struct tb_switch *sw){ int ret; /* * Root switch NVM upgrade requires that we disconnect the * existing PCIe paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { ret = tb_domain_disconnect_pcie_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); return ret == -ETIMEDOUT ? 0 : ret; } /* * From safe mode we can get out by just power cycling the * switch. */ dma_port_power_cycle(sw->dma_port); return 0;} static int nvm_authenticate_device(struct tb_switch *sw){ int ret, retries = 10; ret = dma_port_flash_update_auth(sw->dma_port); if (ret && ret != -ETIMEDOUT) 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 int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, size_t bytes){ struct tb_switch *sw = priv; return dma_port_flash_read(sw->dma_port, offset, val, bytes);} static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, size_t bytes){ struct tb_switch *sw = priv; int ret = 0; if (mutex_lock_interruptible(&switch_lock)) return -ERESTARTSYS; /* * 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. */ if (!sw->nvm->buf) { sw->nvm->buf = vmalloc(NVM_MAX_SIZE); if (!sw->nvm->buf) { ret = -ENOMEM; goto unlock; } } sw->nvm->buf_data_size = offset + bytes; memcpy(sw->nvm->buf + offset, val, bytes); unlock: mutex_unlock(&switch_lock); return ret;} static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, size_t size, bool active){ struct nvmem_config config; memset(&config, 0, sizeof(config)); if (active) { config.name = "nvm_active"; config.reg_read = tb_switch_nvm_read; config.read_only = true; } else { config.name = "nvm_non_active"; config.reg_write = tb_switch_nvm_write; config.root_only = true; } config.id = id; config.stride = 4; config.word_size = 4; config.size = size; config.dev = &sw->dev; config.owner = THIS_MODULE; config.priv = sw; return nvmem_register(&config);} static int tb_switch_nvm_add(struct tb_switch *sw){ struct nvmem_device *nvm_dev; struct tb_switch_nvm *nvm; u32 val; int ret; if (!sw->dma_port) return 0; nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); if (!nvm) return -ENOMEM; nvm->id = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL); /* * 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) { u32 nvm_size, hdr_size; ret = dma_port_flash_read(sw->dma_port, NVM_FLASH_SIZE, &val, sizeof(val)); if (ret) goto err_ida; hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K; nvm_size = (SZ_1M << (val & 7)) / 8; nvm_size = (nvm_size - hdr_size) / 2; ret = dma_port_flash_read(sw->dma_port, NVM_VERSION, &val, sizeof(val)); if (ret) goto err_ida; nvm->major = val >> 16; nvm->minor = val >> 8; nvm_dev = register_nvmem(sw, nvm->id, nvm_size, true); if (IS_ERR(nvm_dev)) { ret = PTR_ERR(nvm_dev); goto err_ida; } nvm->active = nvm_dev; } nvm_dev = register_nvmem(sw, nvm->id, NVM_MAX_SIZE, false); if (IS_ERR(nvm_dev)) { ret = PTR_ERR(nvm_dev); goto err_nvm_active; } nvm->non_active = nvm_dev; mutex_lock(&switch_lock); sw->nvm = nvm; mutex_unlock(&switch_lock); return 0; err_nvm_active: if (nvm->active) nvmem_unregister(nvm->active);err_ida: ida_simple_remove(&nvm_ida, nvm->id); kfree(nvm); return ret;} static void tb_switch_nvm_remove(struct tb_switch *sw){ struct tb_switch_nvm *nvm; mutex_lock(&switch_lock); nvm = sw->nvm; sw->nvm = NULL; mutex_unlock(&switch_lock); if (!nvm) return; /* Remove authentication status in case the switch is unplugged */ if (!nvm->authenticating) nvm_clear_auth_status(sw); nvmem_unregister(nvm->non_active); if (nvm->active) nvmem_unregister(nvm->active); ida_simple_remove(&nvm_ida, nvm->id); vfree(nvm->buf); kfree(nvm);} /* port utility functions */ static const char *tb_port_type(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, struct tb_regs_port_header *port){ tb_info(tb, " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", port->port_number, port->vendor_id, port->device_id, port->revision, port->thunderbolt_version, tb_port_type(port), port->type); tb_info(tb, " Max hop id (in/out): %d/%d\n", port->max_in_hop_id, port->max_out_hop_id); tb_info(tb, " Max counters: %d\n", port->max_counters); tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits);} /** * tb_port_state() - get connectedness state of a port * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: Returns an enum tb_port_state on success or an error code on failure. */static 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 * * 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: Returns an error code on failure. Returns 0 if the port is not * connected or failed to reach state TB_PORT_UP within one second. Returns 1 * if the port is connected and in state TB_PORT_UP. */int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged){ int retries = 10; int state; if (!port->cap_phy) { tb_port_WARN(port, "does not have PHY\n"); return -EINVAL; } if (tb_is_upstream_port(port)) { tb_port_WARN(port, "is the upstream port\n"); return -EINVAL; } while (retries--) { state = tb_port_state(port); if (state < 0) return state; if (state == TB_PORT_DISABLED) { tb_port_info(port, "is disabled (state: 0)\n"); return 0; } if (state == TB_PORT_UNPLUGGED) { if (wait_if_unplugged) { /* used during resume */ tb_port_info(port,Lines 1–500 of 1540. Load more
/* * Thunderbolt Cactus Ridge driver - switch/port utility functions * * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> */ #include <linux/delay.h> #include <linux/idr.h> #include <linux/nvmem-provider.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include "tb.h" /* Switch authorization from userspace is serialized by this lock */ static DEFINE_MUTEX(switch_lock); /* Switch NVM support */ #define NVM_DEVID 0x05 #define NVM_VERSION 0x08 #define NVM_CSS 0x10 #define NVM_FLASH_SIZE 0x45 #define NVM_MIN_SIZE SZ_32K #define NVM_MAX_SIZE SZ_512K static DEFINE_IDA(nvm_ida); 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(sizeof(*st), GFP_KERNEL); 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, hdr_size; const u8 *buf = sw->nvm->buf; u16 ds_size; int ret; if (!buf) return -EINVAL; image_size = sw->nvm->buf_data_size; if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) return -EINVAL; /* * FARB pointer must point inside the image and must at least * contain parts of the digital section we will be reading here. */ hdr_size = (*(u32 *)buf) & 0xffffff; if (hdr_size + NVM_DEVID + 2 >= image_size) return -EINVAL; /* Digital section start should be aligned to 4k page */ if (!IS_ALIGNED(hdr_size, SZ_4K)) return -EINVAL; /* * Read digital section size and check that it also fits inside * the image. */ ds_size = *(u16 *)(buf + hdr_size); if (ds_size >= image_size) return -EINVAL; if (!sw->safe_mode) { u16 device_id; /* * Make sure the device ID in the image matches the one * we read from the switch config space. */ device_id = *(u16 *)(buf + hdr_size + NVM_DEVID); if (device_id != sw->config.device_id) return -EINVAL; if (sw->generation < 3) { /* Write CSS headers first */ ret = dma_port_flash_write(sw->dma_port, DMA_PORT_CSS_ADDRESS, buf + NVM_CSS, DMA_PORT_CSS_MAX_SIZE); if (ret) return ret; } /* Skip headers in the image */ buf += hdr_size; image_size -= hdr_size; } return dma_port_flash_write(sw->dma_port, 0, buf, image_size); } static int nvm_authenticate_host(struct tb_switch *sw) { int ret; /* * Root switch NVM upgrade requires that we disconnect the * existing PCIe paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { ret = tb_domain_disconnect_pcie_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); return ret == -ETIMEDOUT ? 0 : ret; } /* * From safe mode we can get out by just power cycling the * switch. */ dma_port_power_cycle(sw->dma_port); return 0; } static int nvm_authenticate_device(struct tb_switch *sw) { int ret, retries = 10; ret = dma_port_flash_update_auth(sw->dma_port); if (ret && ret != -ETIMEDOUT) 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 int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct tb_switch *sw = priv; return dma_port_flash_read(sw->dma_port, offset, val, bytes); } static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) { struct tb_switch *sw = priv; int ret = 0; if (mutex_lock_interruptible(&switch_lock)) return -ERESTARTSYS; /* * 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. */ if (!sw->nvm->buf) { sw->nvm->buf = vmalloc(NVM_MAX_SIZE); if (!sw->nvm->buf) { ret = -ENOMEM; goto unlock; } } sw->nvm->buf_data_size = offset + bytes; memcpy(sw->nvm->buf + offset, val, bytes); unlock: mutex_unlock(&switch_lock); return ret; } static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, size_t size, bool active) { struct nvmem_config config; memset(&config, 0, sizeof(config)); if (active) { config.name = "nvm_active"; config.reg_read = tb_switch_nvm_read; config.read_only = true; } else { config.name = "nvm_non_active"; config.reg_write = tb_switch_nvm_write; config.root_only = true; } config.id = id; config.stride = 4; config.word_size = 4; config.size = size; config.dev = &sw->dev; config.owner = THIS_MODULE; config.priv = sw; return nvmem_register(&config); } static int tb_switch_nvm_add(struct tb_switch *sw) { struct nvmem_device *nvm_dev; struct tb_switch_nvm *nvm; u32 val; int ret; if (!sw->dma_port) return 0; nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); if (!nvm) return -ENOMEM; nvm->id = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL); /* * 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) { u32 nvm_size, hdr_size; ret = dma_port_flash_read(sw->dma_port, NVM_FLASH_SIZE, &val, sizeof(val)); if (ret) goto err_ida; hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K; nvm_size = (SZ_1M << (val & 7)) / 8; nvm_size = (nvm_size - hdr_size) / 2; ret = dma_port_flash_read(sw->dma_port, NVM_VERSION, &val, sizeof(val)); if (ret) goto err_ida; nvm->major = val >> 16; nvm->minor = val >> 8; nvm_dev = register_nvmem(sw, nvm->id, nvm_size, true); if (IS_ERR(nvm_dev)) { ret = PTR_ERR(nvm_dev); goto err_ida; } nvm->active = nvm_dev; } nvm_dev = register_nvmem(sw, nvm->id, NVM_MAX_SIZE, false); if (IS_ERR(nvm_dev)) { ret = PTR_ERR(nvm_dev); goto err_nvm_active; } nvm->non_active = nvm_dev; mutex_lock(&switch_lock); sw->nvm = nvm; mutex_unlock(&switch_lock); return 0; err_nvm_active: if (nvm->active) nvmem_unregister(nvm->active); err_ida: ida_simple_remove(&nvm_ida, nvm->id); kfree(nvm); return ret; } static void tb_switch_nvm_remove(struct tb_switch *sw) { struct tb_switch_nvm *nvm; mutex_lock(&switch_lock); nvm = sw->nvm; sw->nvm = NULL; mutex_unlock(&switch_lock); if (!nvm) return; /* Remove authentication status in case the switch is unplugged */ if (!nvm->authenticating) nvm_clear_auth_status(sw); nvmem_unregister(nvm->non_active); if (nvm->active) nvmem_unregister(nvm->active); ida_simple_remove(&nvm_ida, nvm->id); vfree(nvm->buf); kfree(nvm); } /* port utility functions */ static const char *tb_port_type(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, struct tb_regs_port_header *port) { tb_info(tb, " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", port->port_number, port->vendor_id, port->device_id, port->revision, port->thunderbolt_version, tb_port_type(port), port->type); tb_info(tb, " Max hop id (in/out): %d/%d\n", port->max_in_hop_id, port->max_out_hop_id); tb_info(tb, " Max counters: %d\n", port->max_counters); tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); } /** * tb_port_state() - get connectedness state of a port * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: Returns an enum tb_port_state on success or an error code on failure. */ static 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 * * 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: Returns an error code on failure. Returns 0 if the port is not * connected or failed to reach state TB_PORT_UP within one second. Returns 1 * if the port is connected and in state TB_PORT_UP. */ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) { int retries = 10; int state; if (!port->cap_phy) { tb_port_WARN(port, "does not have PHY\n"); return -EINVAL; } if (tb_is_upstream_port(port)) { tb_port_WARN(port, "is the upstream port\n"); return -EINVAL; } while (retries--) { state = tb_port_state(port); if (state < 0) return state; if (state == TB_PORT_DISABLED) { tb_port_info(port, "is disabled (state: 0)\n"); return 0; } if (state == TB_PORT_UNPLUGGED) { if (wait_if_unplugged) { /* used during resume */ tb_port_info(port,