4044 lines · 94.8 KB · C
RawDownload
// 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.