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

cmp.c (9797B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Connection Management Procedures (IEC 61883-1) helper functions
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/firewire.h>
     10#include <linux/firewire-constants.h>
     11#include <linux/module.h>
     12#include <linux/sched.h>
     13#include "lib.h"
     14#include "iso-resources.h"
     15#include "cmp.h"
     16
     17/* MPR common fields */
     18#define MPR_SPEED_MASK		0xc0000000
     19#define MPR_SPEED_SHIFT		30
     20#define MPR_XSPEED_MASK		0x00000060
     21#define MPR_XSPEED_SHIFT	5
     22#define MPR_PLUGS_MASK		0x0000001f
     23
     24/* PCR common fields */
     25#define PCR_ONLINE		0x80000000
     26#define PCR_BCAST_CONN		0x40000000
     27#define PCR_P2P_CONN_MASK	0x3f000000
     28#define PCR_P2P_CONN_SHIFT	24
     29#define PCR_CHANNEL_MASK	0x003f0000
     30#define PCR_CHANNEL_SHIFT	16
     31
     32/* oPCR specific fields */
     33#define OPCR_XSPEED_MASK	0x00C00000
     34#define OPCR_XSPEED_SHIFT	22
     35#define OPCR_SPEED_MASK		0x0000C000
     36#define OPCR_SPEED_SHIFT	14
     37#define OPCR_OVERHEAD_ID_MASK	0x00003C00
     38#define OPCR_OVERHEAD_ID_SHIFT	10
     39
     40enum bus_reset_handling {
     41	ABORT_ON_BUS_RESET,
     42	SUCCEED_ON_BUS_RESET,
     43};
     44
     45static __printf(2, 3)
     46void cmp_error(struct cmp_connection *c, const char *fmt, ...)
     47{
     48	va_list va;
     49
     50	va_start(va, fmt);
     51	dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
     52		(c->direction == CMP_INPUT) ? 'i' : 'o',
     53		c->pcr_index, &(struct va_format){ fmt, &va });
     54	va_end(va);
     55}
     56
     57static u64 mpr_address(struct cmp_connection *c)
     58{
     59	if (c->direction == CMP_INPUT)
     60		return CSR_REGISTER_BASE + CSR_IMPR;
     61	else
     62		return CSR_REGISTER_BASE + CSR_OMPR;
     63}
     64
     65static u64 pcr_address(struct cmp_connection *c)
     66{
     67	if (c->direction == CMP_INPUT)
     68		return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
     69	else
     70		return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
     71}
     72
     73static int pcr_modify(struct cmp_connection *c,
     74		      __be32 (*modify)(struct cmp_connection *c, __be32 old),
     75		      int (*check)(struct cmp_connection *c, __be32 pcr),
     76		      enum bus_reset_handling bus_reset_handling)
     77{
     78	__be32 old_arg, buffer[2];
     79	int err;
     80
     81	buffer[0] = c->last_pcr_value;
     82	for (;;) {
     83		old_arg = buffer[0];
     84		buffer[1] = modify(c, buffer[0]);
     85
     86		err = snd_fw_transaction(
     87				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
     88				pcr_address(c), buffer, 8,
     89				FW_FIXED_GENERATION | c->resources.generation);
     90
     91		if (err < 0) {
     92			if (err == -EAGAIN &&
     93			    bus_reset_handling == SUCCEED_ON_BUS_RESET)
     94				err = 0;
     95			return err;
     96		}
     97
     98		if (buffer[0] == old_arg) /* success? */
     99			break;
    100
    101		if (check) {
    102			err = check(c, buffer[0]);
    103			if (err < 0)
    104				return err;
    105		}
    106	}
    107	c->last_pcr_value = buffer[1];
    108
    109	return 0;
    110}
    111
    112
    113/**
    114 * cmp_connection_init - initializes a connection manager
    115 * @c: the connection manager to initialize
    116 * @unit: a unit of the target device
    117 * @direction: input or output
    118 * @pcr_index: the index of the iPCR/oPCR on the target device
    119 */
    120int cmp_connection_init(struct cmp_connection *c,
    121			struct fw_unit *unit,
    122			enum cmp_direction direction,
    123			unsigned int pcr_index)
    124{
    125	__be32 mpr_be;
    126	u32 mpr;
    127	int err;
    128
    129	c->direction = direction;
    130	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
    131				 mpr_address(c), &mpr_be, 4, 0);
    132	if (err < 0)
    133		return err;
    134	mpr = be32_to_cpu(mpr_be);
    135
    136	if (pcr_index >= (mpr & MPR_PLUGS_MASK))
    137		return -EINVAL;
    138
    139	err = fw_iso_resources_init(&c->resources, unit);
    140	if (err < 0)
    141		return err;
    142
    143	c->connected = false;
    144	mutex_init(&c->mutex);
    145	c->last_pcr_value = cpu_to_be32(0x80000000);
    146	c->pcr_index = pcr_index;
    147	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
    148	if (c->max_speed == SCODE_BETA)
    149		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
    150
    151	return 0;
    152}
    153EXPORT_SYMBOL(cmp_connection_init);
    154
    155/**
    156 * cmp_connection_check_used - check connection is already esablished or not
    157 * @c: the connection manager to be checked
    158 * @used: the pointer to store the result of checking the connection
    159 */
    160int cmp_connection_check_used(struct cmp_connection *c, bool *used)
    161{
    162	__be32 pcr;
    163	int err;
    164
    165	err = snd_fw_transaction(
    166			c->resources.unit, TCODE_READ_QUADLET_REQUEST,
    167			pcr_address(c), &pcr, 4, 0);
    168	if (err >= 0)
    169		*used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
    170					     PCR_P2P_CONN_MASK));
    171
    172	return err;
    173}
    174EXPORT_SYMBOL(cmp_connection_check_used);
    175
    176/**
    177 * cmp_connection_destroy - free connection manager resources
    178 * @c: the connection manager
    179 */
    180void cmp_connection_destroy(struct cmp_connection *c)
    181{
    182	WARN_ON(c->connected);
    183	mutex_destroy(&c->mutex);
    184	fw_iso_resources_destroy(&c->resources);
    185}
    186EXPORT_SYMBOL(cmp_connection_destroy);
    187
    188int cmp_connection_reserve(struct cmp_connection *c,
    189			   unsigned int max_payload_bytes)
    190{
    191	int err;
    192
    193	mutex_lock(&c->mutex);
    194
    195	if (WARN_ON(c->resources.allocated)) {
    196		err = -EBUSY;
    197		goto end;
    198	}
    199
    200	c->speed = min(c->max_speed,
    201		       fw_parent_device(c->resources.unit)->max_speed);
    202
    203	err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
    204					c->speed);
    205end:
    206	mutex_unlock(&c->mutex);
    207
    208	return err;
    209}
    210EXPORT_SYMBOL(cmp_connection_reserve);
    211
    212void cmp_connection_release(struct cmp_connection *c)
    213{
    214	mutex_lock(&c->mutex);
    215	fw_iso_resources_free(&c->resources);
    216	mutex_unlock(&c->mutex);
    217}
    218EXPORT_SYMBOL(cmp_connection_release);
    219
    220static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
    221{
    222	ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
    223			     PCR_P2P_CONN_MASK |
    224			     PCR_CHANNEL_MASK);
    225	ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
    226	ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
    227
    228	return ipcr;
    229}
    230
    231static int get_overhead_id(struct cmp_connection *c)
    232{
    233	int id;
    234
    235	/*
    236	 * apply "oPCR overhead ID encoding"
    237	 * the encoding table can convert up to 512.
    238	 * here the value over 512 is converted as the same way as 512.
    239	 */
    240	for (id = 1; id < 16; id++) {
    241		if (c->resources.bandwidth_overhead < (id << 5))
    242			break;
    243	}
    244	if (id == 16)
    245		id = 0;
    246
    247	return id;
    248}
    249
    250static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
    251{
    252	unsigned int spd, xspd;
    253
    254	/* generate speed and extended speed field value */
    255	if (c->speed > SCODE_400) {
    256		spd  = SCODE_800;
    257		xspd = c->speed - SCODE_800;
    258	} else {
    259		spd = c->speed;
    260		xspd = 0;
    261	}
    262
    263	opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
    264			     PCR_P2P_CONN_MASK |
    265			     OPCR_XSPEED_MASK |
    266			     PCR_CHANNEL_MASK |
    267			     OPCR_SPEED_MASK |
    268			     OPCR_OVERHEAD_ID_MASK);
    269	opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
    270	opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
    271	opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
    272	opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
    273	opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
    274
    275	return opcr;
    276}
    277
    278static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
    279{
    280	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
    281			      PCR_P2P_CONN_MASK)) {
    282		cmp_error(c, "plug is already in use\n");
    283		return -EBUSY;
    284	}
    285	if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
    286		cmp_error(c, "plug is not on-line\n");
    287		return -ECONNREFUSED;
    288	}
    289
    290	return 0;
    291}
    292
    293/**
    294 * cmp_connection_establish - establish a connection to the target
    295 * @c: the connection manager
    296 *
    297 * This function establishes a point-to-point connection from the local
    298 * computer to the target by allocating isochronous resources (channel and
    299 * bandwidth) and setting the target's input/output plug control register.
    300 * When this function succeeds, the caller is responsible for starting
    301 * transmitting packets.
    302 */
    303int cmp_connection_establish(struct cmp_connection *c)
    304{
    305	int err;
    306
    307	mutex_lock(&c->mutex);
    308
    309	if (WARN_ON(c->connected)) {
    310		mutex_unlock(&c->mutex);
    311		return -EISCONN;
    312	}
    313
    314retry_after_bus_reset:
    315	if (c->direction == CMP_OUTPUT)
    316		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
    317				 ABORT_ON_BUS_RESET);
    318	else
    319		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
    320				 ABORT_ON_BUS_RESET);
    321
    322	if (err == -EAGAIN) {
    323		err = fw_iso_resources_update(&c->resources);
    324		if (err >= 0)
    325			goto retry_after_bus_reset;
    326	}
    327	if (err >= 0)
    328		c->connected = true;
    329
    330	mutex_unlock(&c->mutex);
    331
    332	return err;
    333}
    334EXPORT_SYMBOL(cmp_connection_establish);
    335
    336/**
    337 * cmp_connection_update - update the connection after a bus reset
    338 * @c: the connection manager
    339 *
    340 * This function must be called from the driver's .update handler to
    341 * reestablish any connection that might have been active.
    342 *
    343 * Returns zero on success, or a negative error code.  On an error, the
    344 * connection is broken and the caller must stop transmitting iso packets.
    345 */
    346int cmp_connection_update(struct cmp_connection *c)
    347{
    348	int err;
    349
    350	mutex_lock(&c->mutex);
    351
    352	if (!c->connected) {
    353		mutex_unlock(&c->mutex);
    354		return 0;
    355	}
    356
    357	err = fw_iso_resources_update(&c->resources);
    358	if (err < 0)
    359		goto err_unconnect;
    360
    361	if (c->direction == CMP_OUTPUT)
    362		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
    363				 SUCCEED_ON_BUS_RESET);
    364	else
    365		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
    366				 SUCCEED_ON_BUS_RESET);
    367
    368	if (err < 0)
    369		goto err_unconnect;
    370
    371	mutex_unlock(&c->mutex);
    372
    373	return 0;
    374
    375err_unconnect:
    376	c->connected = false;
    377	mutex_unlock(&c->mutex);
    378
    379	return err;
    380}
    381EXPORT_SYMBOL(cmp_connection_update);
    382
    383static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
    384{
    385	return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
    386}
    387
    388/**
    389 * cmp_connection_break - break the connection to the target
    390 * @c: the connection manager
    391 *
    392 * This function deactives the connection in the target's input/output plug
    393 * control register, and frees the isochronous resources of the connection.
    394 * Before calling this function, the caller should cease transmitting packets.
    395 */
    396void cmp_connection_break(struct cmp_connection *c)
    397{
    398	int err;
    399
    400	mutex_lock(&c->mutex);
    401
    402	if (!c->connected) {
    403		mutex_unlock(&c->mutex);
    404		return;
    405	}
    406
    407	err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
    408	if (err < 0)
    409		cmp_error(c, "plug is still connected\n");
    410
    411	c->connected = false;
    412
    413	mutex_unlock(&c->mutex);
    414}
    415EXPORT_SYMBOL(cmp_connection_break);