1540 lines · 35.2 KB · C
RawDownload
/* * 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.