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

dice-transaction.c (8769B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * dice_transaction.c - a part of driver for Dice based devices
      4 *
      5 * Copyright (c) Clemens Ladisch
      6 * Copyright (c) 2014 Takashi Sakamoto
      7 */
      8
      9#include "dice.h"
     10
     11static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
     12		       u64 offset)
     13{
     14	switch (type) {
     15	case SND_DICE_ADDR_TYPE_TX:
     16		offset += dice->tx_offset;
     17		break;
     18	case SND_DICE_ADDR_TYPE_RX:
     19		offset += dice->rx_offset;
     20		break;
     21	case SND_DICE_ADDR_TYPE_SYNC:
     22		offset += dice->sync_offset;
     23		break;
     24	case SND_DICE_ADDR_TYPE_RSRV:
     25		offset += dice->rsrv_offset;
     26		break;
     27	case SND_DICE_ADDR_TYPE_GLOBAL:
     28	default:
     29		offset += dice->global_offset;
     30		break;
     31	}
     32	offset += DICE_PRIVATE_SPACE;
     33	return offset;
     34}
     35
     36int snd_dice_transaction_write(struct snd_dice *dice,
     37			       enum snd_dice_addr_type type,
     38			       unsigned int offset, void *buf, unsigned int len)
     39{
     40	return snd_fw_transaction(dice->unit,
     41				  (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
     42					       TCODE_WRITE_BLOCK_REQUEST,
     43				  get_subaddr(dice, type, offset), buf, len, 0);
     44}
     45
     46int snd_dice_transaction_read(struct snd_dice *dice,
     47			      enum snd_dice_addr_type type, unsigned int offset,
     48			      void *buf, unsigned int len)
     49{
     50	return snd_fw_transaction(dice->unit,
     51				  (len == 4) ? TCODE_READ_QUADLET_REQUEST :
     52					       TCODE_READ_BLOCK_REQUEST,
     53				  get_subaddr(dice, type, offset), buf, len, 0);
     54}
     55
     56static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
     57{
     58	return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
     59						info, 4);
     60}
     61
     62int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
     63					  unsigned int *source)
     64{
     65	__be32 info;
     66	int err;
     67
     68	err = get_clock_info(dice, &info);
     69	if (err >= 0)
     70		*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
     71
     72	return err;
     73}
     74
     75int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
     76{
     77	__be32 info;
     78	unsigned int index;
     79	int err;
     80
     81	err = get_clock_info(dice, &info);
     82	if (err < 0)
     83		goto end;
     84
     85	index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
     86	if (index >= SND_DICE_RATES_COUNT) {
     87		err = -ENOSYS;
     88		goto end;
     89	}
     90
     91	*rate = snd_dice_rates[index];
     92end:
     93	return err;
     94}
     95
     96int snd_dice_transaction_set_enable(struct snd_dice *dice)
     97{
     98	__be32 value;
     99	int err = 0;
    100
    101	if (dice->global_enabled)
    102		goto end;
    103
    104	value = cpu_to_be32(1);
    105	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
    106				 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
    107					     GLOBAL_ENABLE),
    108				 &value, 4,
    109				 FW_FIXED_GENERATION | dice->owner_generation);
    110	if (err < 0)
    111		goto end;
    112
    113	dice->global_enabled = true;
    114end:
    115	return err;
    116}
    117
    118void snd_dice_transaction_clear_enable(struct snd_dice *dice)
    119{
    120	__be32 value;
    121
    122	value = 0;
    123	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
    124			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
    125				       GLOBAL_ENABLE),
    126			   &value, 4, FW_QUIET |
    127			   FW_FIXED_GENERATION | dice->owner_generation);
    128
    129	dice->global_enabled = false;
    130}
    131
    132static void dice_notification(struct fw_card *card, struct fw_request *request,
    133			      int tcode, int destination, int source,
    134			      int generation, unsigned long long offset,
    135			      void *data, size_t length, void *callback_data)
    136{
    137	struct snd_dice *dice = callback_data;
    138	u32 bits;
    139	unsigned long flags;
    140
    141	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
    142		fw_send_response(card, request, RCODE_TYPE_ERROR);
    143		return;
    144	}
    145	if ((offset & 3) != 0) {
    146		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
    147		return;
    148	}
    149
    150	bits = be32_to_cpup(data);
    151
    152	spin_lock_irqsave(&dice->lock, flags);
    153	dice->notification_bits |= bits;
    154	spin_unlock_irqrestore(&dice->lock, flags);
    155
    156	fw_send_response(card, request, RCODE_COMPLETE);
    157
    158	if (bits & NOTIFY_CLOCK_ACCEPTED)
    159		complete(&dice->clock_accepted);
    160	wake_up(&dice->hwdep_wait);
    161}
    162
    163static int register_notification_address(struct snd_dice *dice, bool retry)
    164{
    165	struct fw_device *device = fw_parent_device(dice->unit);
    166	__be64 *buffer;
    167	unsigned int retries;
    168	int err;
    169
    170	retries = (retry) ? 3 : 0;
    171
    172	buffer = kmalloc(2 * 8, GFP_KERNEL);
    173	if (!buffer)
    174		return -ENOMEM;
    175
    176	for (;;) {
    177		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
    178		buffer[1] = cpu_to_be64(
    179			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
    180			dice->notification_handler.offset);
    181
    182		dice->owner_generation = device->generation;
    183		smp_rmb(); /* node_id vs. generation */
    184		err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
    185					 get_subaddr(dice,
    186						     SND_DICE_ADDR_TYPE_GLOBAL,
    187						     GLOBAL_OWNER),
    188					 buffer, 2 * 8,
    189					 FW_FIXED_GENERATION |
    190							dice->owner_generation);
    191		if (err == 0) {
    192			/* success */
    193			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
    194				break;
    195			/* The address seems to be already registered. */
    196			if (buffer[0] == buffer[1])
    197				break;
    198
    199			dev_err(&dice->unit->device,
    200				"device is already in use\n");
    201			err = -EBUSY;
    202		}
    203		if (err != -EAGAIN || retries-- > 0)
    204			break;
    205
    206		msleep(20);
    207	}
    208
    209	kfree(buffer);
    210
    211	if (err < 0)
    212		dice->owner_generation = -1;
    213
    214	return err;
    215}
    216
    217static void unregister_notification_address(struct snd_dice *dice)
    218{
    219	struct fw_device *device = fw_parent_device(dice->unit);
    220	__be64 *buffer;
    221
    222	buffer = kmalloc(2 * 8, GFP_KERNEL);
    223	if (buffer == NULL)
    224		return;
    225
    226	buffer[0] = cpu_to_be64(
    227		((u64)device->card->node_id << OWNER_NODE_SHIFT) |
    228		dice->notification_handler.offset);
    229	buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
    230	snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
    231			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
    232				       GLOBAL_OWNER),
    233			   buffer, 2 * 8, FW_QUIET |
    234			   FW_FIXED_GENERATION | dice->owner_generation);
    235
    236	kfree(buffer);
    237
    238	dice->owner_generation = -1;
    239}
    240
    241void snd_dice_transaction_destroy(struct snd_dice *dice)
    242{
    243	struct fw_address_handler *handler = &dice->notification_handler;
    244
    245	if (handler->callback_data == NULL)
    246		return;
    247
    248	unregister_notification_address(dice);
    249
    250	fw_core_remove_address_handler(handler);
    251	handler->callback_data = NULL;
    252}
    253
    254int snd_dice_transaction_reinit(struct snd_dice *dice)
    255{
    256	struct fw_address_handler *handler = &dice->notification_handler;
    257
    258	if (handler->callback_data == NULL)
    259		return -EINVAL;
    260
    261	return register_notification_address(dice, false);
    262}
    263
    264static int get_subaddrs(struct snd_dice *dice)
    265{
    266	static const int min_values[10] = {
    267		10, 0x60 / 4,
    268		10, 0x18 / 4,
    269		10, 0x18 / 4,
    270		0, 0,
    271		0, 0,
    272	};
    273	__be32 *pointers;
    274	__be32 version;
    275	u32 data;
    276	unsigned int i;
    277	int err;
    278
    279	pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
    280				 GFP_KERNEL);
    281	if (pointers == NULL)
    282		return -ENOMEM;
    283
    284	/*
    285	 * Check that the sub address spaces exist and are located inside the
    286	 * private address space.  The minimum values are chosen so that all
    287	 * minimally required registers are included.
    288	 */
    289	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
    290				 DICE_PRIVATE_SPACE, pointers,
    291				 sizeof(__be32) * ARRAY_SIZE(min_values), 0);
    292	if (err < 0)
    293		goto end;
    294
    295	for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
    296		data = be32_to_cpu(pointers[i]);
    297		if (data < min_values[i] || data >= 0x40000) {
    298			err = -ENODEV;
    299			goto end;
    300		}
    301	}
    302
    303	if (be32_to_cpu(pointers[1]) > 0x18) {
    304		/*
    305		 * Check that the implemented DICE driver specification major
    306		 * version number matches.
    307		 */
    308		err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
    309				DICE_PRIVATE_SPACE +
    310				be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
    311				&version, sizeof(version), 0);
    312		if (err < 0)
    313			goto end;
    314
    315		if ((version & cpu_to_be32(0xff000000)) !=
    316						cpu_to_be32(0x01000000)) {
    317			dev_err(&dice->unit->device,
    318				"unknown DICE version: 0x%08x\n",
    319				be32_to_cpu(version));
    320			err = -ENODEV;
    321			goto end;
    322		}
    323
    324		/* Set up later. */
    325		dice->clock_caps = 1;
    326	}
    327
    328	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
    329	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
    330	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
    331
    332	/* Old firmware doesn't support these fields. */
    333	if (pointers[7])
    334		dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
    335	if (pointers[9])
    336		dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
    337end:
    338	kfree(pointers);
    339	return err;
    340}
    341
    342int snd_dice_transaction_init(struct snd_dice *dice)
    343{
    344	struct fw_address_handler *handler = &dice->notification_handler;
    345	int err;
    346
    347	err = get_subaddrs(dice);
    348	if (err < 0)
    349		return err;
    350
    351	/* Allocation callback in address space over host controller */
    352	handler->length = 4;
    353	handler->address_callback = dice_notification;
    354	handler->callback_data = dice;
    355	err = fw_core_add_address_handler(handler, &fw_high_memory_region);
    356	if (err < 0) {
    357		handler->callback_data = NULL;
    358		return err;
    359	}
    360
    361	/* Register the address space */
    362	err = register_notification_address(dice, true);
    363	if (err < 0) {
    364		fw_core_remove_address_handler(handler);
    365		handler->callback_data = NULL;
    366	}
    367
    368	return err;
    369}