cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cap.c (5582B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Thunderbolt driver - capabilities lookup
      4 *
      5 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
      6 * Copyright (C) 2018, Intel Corporation
      7 */
      8
      9#include <linux/slab.h>
     10#include <linux/errno.h>
     11
     12#include "tb.h"
     13
     14#define CAP_OFFSET_MAX		0xff
     15#define VSE_CAP_OFFSET_MAX	0xffff
     16#define TMU_ACCESS_EN		BIT(20)
     17
     18static int tb_port_enable_tmu(struct tb_port *port, bool enable)
     19{
     20	struct tb_switch *sw = port->sw;
     21	u32 value, offset;
     22	int ret;
     23
     24	/*
     25	 * Legacy devices need to have TMU access enabled before port
     26	 * space can be fully accessed.
     27	 */
     28	if (tb_switch_is_light_ridge(sw))
     29		offset = 0x26;
     30	else if (tb_switch_is_eagle_ridge(sw))
     31		offset = 0x2a;
     32	else
     33		return 0;
     34
     35	ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
     36	if (ret)
     37		return ret;
     38
     39	if (enable)
     40		value |= TMU_ACCESS_EN;
     41	else
     42		value &= ~TMU_ACCESS_EN;
     43
     44	return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
     45}
     46
     47static void tb_port_dummy_read(struct tb_port *port)
     48{
     49	/*
     50	 * When reading from next capability pointer location in port
     51	 * config space the read data is not cleared on LR. To avoid
     52	 * reading stale data on next read perform one dummy read after
     53	 * port capabilities are walked.
     54	 */
     55	if (tb_switch_is_light_ridge(port->sw)) {
     56		u32 dummy;
     57
     58		tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
     59	}
     60}
     61
     62/**
     63 * tb_port_next_cap() - Return next capability in the linked list
     64 * @port: Port to find the capability for
     65 * @offset: Previous capability offset (%0 for start)
     66 *
     67 * Returns dword offset of the next capability in port config space
     68 * capability list and returns it. Passing %0 returns the first entry in
     69 * the capability list. If no next capability is found returns %0. In case
     70 * of failure returns negative errno.
     71 */
     72int tb_port_next_cap(struct tb_port *port, unsigned int offset)
     73{
     74	struct tb_cap_any header;
     75	int ret;
     76
     77	if (!offset)
     78		return port->config.first_cap_offset;
     79
     80	ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
     81	if (ret)
     82		return ret;
     83
     84	return header.basic.next;
     85}
     86
     87static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
     88{
     89	int offset = 0;
     90
     91	do {
     92		struct tb_cap_any header;
     93		int ret;
     94
     95		offset = tb_port_next_cap(port, offset);
     96		if (offset < 0)
     97			return offset;
     98
     99		ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
    100		if (ret)
    101			return ret;
    102
    103		if (header.basic.cap == cap)
    104			return offset;
    105	} while (offset > 0);
    106
    107	return -ENOENT;
    108}
    109
    110/**
    111 * tb_port_find_cap() - Find port capability
    112 * @port: Port to find the capability for
    113 * @cap: Capability to look
    114 *
    115 * Returns offset to start of capability or %-ENOENT if no such
    116 * capability was found. Negative errno is returned if there was an
    117 * error.
    118 */
    119int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
    120{
    121	int ret;
    122
    123	ret = tb_port_enable_tmu(port, true);
    124	if (ret)
    125		return ret;
    126
    127	ret = __tb_port_find_cap(port, cap);
    128
    129	tb_port_dummy_read(port);
    130	tb_port_enable_tmu(port, false);
    131
    132	return ret;
    133}
    134
    135/**
    136 * tb_switch_next_cap() - Return next capability in the linked list
    137 * @sw: Switch to find the capability for
    138 * @offset: Previous capability offset (%0 for start)
    139 *
    140 * Finds dword offset of the next capability in router config space
    141 * capability list and returns it. Passing %0 returns the first entry in
    142 * the capability list. If no next capability is found returns %0. In case
    143 * of failure returns negative errno.
    144 */
    145int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)
    146{
    147	struct tb_cap_any header;
    148	int ret;
    149
    150	if (!offset)
    151		return sw->config.first_cap_offset;
    152
    153	ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
    154	if (ret)
    155		return ret;
    156
    157	switch (header.basic.cap) {
    158	case TB_SWITCH_CAP_TMU:
    159		ret = header.basic.next;
    160		break;
    161
    162	case TB_SWITCH_CAP_VSE:
    163		if (!header.extended_short.length)
    164			ret = header.extended_long.next;
    165		else
    166			ret = header.extended_short.next;
    167		break;
    168
    169	default:
    170		tb_sw_dbg(sw, "unknown capability %#x at %#x\n",
    171			  header.basic.cap, offset);
    172		ret = -EINVAL;
    173		break;
    174	}
    175
    176	return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret;
    177}
    178
    179/**
    180 * tb_switch_find_cap() - Find switch capability
    181 * @sw: Switch to find the capability for
    182 * @cap: Capability to look
    183 *
    184 * Returns offset to start of capability or %-ENOENT if no such
    185 * capability was found. Negative errno is returned if there was an
    186 * error.
    187 */
    188int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
    189{
    190	int offset = 0;
    191
    192	do {
    193		struct tb_cap_any header;
    194		int ret;
    195
    196		offset = tb_switch_next_cap(sw, offset);
    197		if (offset < 0)
    198			return offset;
    199
    200		ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
    201		if (ret)
    202			return ret;
    203
    204		if (header.basic.cap == cap)
    205			return offset;
    206	} while (offset);
    207
    208	return -ENOENT;
    209}
    210
    211/**
    212 * tb_switch_find_vse_cap() - Find switch vendor specific capability
    213 * @sw: Switch to find the capability for
    214 * @vsec: Vendor specific capability to look
    215 *
    216 * Functions enumerates vendor specific capabilities (VSEC) of a switch
    217 * and returns offset when capability matching @vsec is found. If no
    218 * such capability is found returns %-ENOENT. In case of error returns
    219 * negative errno.
    220 */
    221int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
    222{
    223	int offset = 0;
    224
    225	do {
    226		struct tb_cap_any header;
    227		int ret;
    228
    229		offset = tb_switch_next_cap(sw, offset);
    230		if (offset < 0)
    231			return offset;
    232
    233		ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
    234		if (ret)
    235			return ret;
    236
    237		if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&
    238		    header.extended_short.vsec_id == vsec)
    239			return offset;
    240	} while (offset);
    241
    242	return -ENOENT;
    243}