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

saa7164-bus.c (13745B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Driver for the NXP SAA7164 PCIe bridge
      4 *
      5 *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
      6 */
      7
      8#include "saa7164.h"
      9
     10/* The message bus to/from the firmware is a ring buffer in PCI address
     11 * space. Establish the defaults.
     12 */
     13int saa7164_bus_setup(struct saa7164_dev *dev)
     14{
     15	struct tmComResBusInfo *b	= &dev->bus;
     16
     17	mutex_init(&b->lock);
     18
     19	b->Type			= TYPE_BUS_PCIe;
     20	b->m_wMaxReqSize	= SAA_DEVICE_MAXREQUESTSIZE;
     21
     22	b->m_pdwSetRing		= (u8 __iomem *)(dev->bmmio +
     23		((u32)dev->busdesc.CommandRing));
     24
     25	b->m_dwSizeSetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
     26
     27	b->m_pdwGetRing		= (u8 __iomem *)(dev->bmmio +
     28		((u32)dev->busdesc.ResponseRing));
     29
     30	b->m_dwSizeGetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
     31
     32	b->m_dwSetWritePos	= ((u32)dev->intfdesc.BARLocation) +
     33		(2 * sizeof(u64));
     34	b->m_dwSetReadPos	= b->m_dwSetWritePos + (1 * sizeof(u32));
     35
     36	b->m_dwGetWritePos	= b->m_dwSetWritePos + (2 * sizeof(u32));
     37	b->m_dwGetReadPos	= b->m_dwSetWritePos + (3 * sizeof(u32));
     38
     39	return 0;
     40}
     41
     42void saa7164_bus_dump(struct saa7164_dev *dev)
     43{
     44	struct tmComResBusInfo *b = &dev->bus;
     45
     46	dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
     47	dprintk(DBGLVL_BUS, " .type             = %d\n", b->Type);
     48	dprintk(DBGLVL_BUS, " .dev->bmmio       = 0x%p\n", dev->bmmio);
     49	dprintk(DBGLVL_BUS, " .m_wMaxReqSize    = 0x%x\n", b->m_wMaxReqSize);
     50	dprintk(DBGLVL_BUS, " .m_pdwSetRing     = 0x%p\n", b->m_pdwSetRing);
     51	dprintk(DBGLVL_BUS, " .m_dwSizeSetRing  = 0x%x\n", b->m_dwSizeSetRing);
     52	dprintk(DBGLVL_BUS, " .m_pdwGetRing     = 0x%p\n", b->m_pdwGetRing);
     53	dprintk(DBGLVL_BUS, " .m_dwSizeGetRing  = 0x%x\n", b->m_dwSizeGetRing);
     54
     55	dprintk(DBGLVL_BUS, " .m_dwSetReadPos   = 0x%x (0x%08x)\n",
     56		b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
     57
     58	dprintk(DBGLVL_BUS, " .m_dwSetWritePos  = 0x%x (0x%08x)\n",
     59		b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
     60
     61	dprintk(DBGLVL_BUS, " .m_dwGetReadPos   = 0x%x (0x%08x)\n",
     62		b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
     63
     64	dprintk(DBGLVL_BUS, " .m_dwGetWritePos  = 0x%x (0x%08x)\n",
     65		b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
     66
     67}
     68
     69/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
     70static void saa7164_bus_verify(struct saa7164_dev *dev)
     71{
     72	struct tmComResBusInfo *b = &dev->bus;
     73	int bug = 0;
     74
     75	if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
     76		bug++;
     77
     78	if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
     79		bug++;
     80
     81	if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
     82		bug++;
     83
     84	if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
     85		bug++;
     86
     87	if (bug) {
     88		saa_debug = 0xffff; /* Ensure we get the bus dump */
     89		saa7164_bus_dump(dev);
     90		saa_debug = 1024; /* Ensure we get the bus dump */
     91		BUG();
     92	}
     93}
     94
     95static void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m,
     96				void *buf)
     97{
     98	dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
     99	dprintk(DBGLVL_BUS, " .id               = %d\n",   m->id);
    100	dprintk(DBGLVL_BUS, " .flags            = 0x%x\n", m->flags);
    101	dprintk(DBGLVL_BUS, " .size             = 0x%x\n", m->size);
    102	dprintk(DBGLVL_BUS, " .command          = 0x%x\n", m->command);
    103	dprintk(DBGLVL_BUS, " .controlselector  = 0x%x\n", m->controlselector);
    104	dprintk(DBGLVL_BUS, " .seqno            = %d\n",   m->seqno);
    105	if (buf)
    106		dprintk(DBGLVL_BUS, " .buffer (ignored)\n");
    107}
    108
    109/*
    110 * Places a command or a response on the bus. The implementation does not
    111 * know if it is a command or a response it just places the data on the
    112 * bus depending on the bus information given in the struct tmComResBusInfo
    113 * structure. If the command or response does not fit into the bus ring
    114 * buffer it will be refused.
    115 *
    116 * Return Value:
    117 *  SAA_OK     The function executed successfully.
    118 *  < 0        One or more members are not initialized.
    119 */
    120int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
    121	void *buf)
    122{
    123	struct tmComResBusInfo *bus = &dev->bus;
    124	u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
    125	u32 new_swp, space_rem;
    126	int ret = SAA_ERR_BAD_PARAMETER;
    127	u16 size;
    128
    129	if (!msg) {
    130		printk(KERN_ERR "%s() !msg\n", __func__);
    131		return SAA_ERR_BAD_PARAMETER;
    132	}
    133
    134	dprintk(DBGLVL_BUS, "%s()\n", __func__);
    135
    136	saa7164_bus_verify(dev);
    137
    138	if (msg->size > dev->bus.m_wMaxReqSize) {
    139		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
    140			__func__);
    141		return SAA_ERR_BAD_PARAMETER;
    142	}
    143
    144	if ((msg->size > 0) && (buf == NULL)) {
    145		printk(KERN_ERR "%s() Missing message buffer\n", __func__);
    146		return SAA_ERR_BAD_PARAMETER;
    147	}
    148
    149	/* Lock the bus from any other access */
    150	mutex_lock(&bus->lock);
    151
    152	bytes_to_write = sizeof(*msg) + msg->size;
    153	free_write_space = 0;
    154	timeout = SAA_BUS_TIMEOUT;
    155	curr_srp = saa7164_readl(bus->m_dwSetReadPos);
    156	curr_swp = saa7164_readl(bus->m_dwSetWritePos);
    157
    158	/* Deal with ring wrapping issues */
    159	if (curr_srp > curr_swp)
    160		/* Deal with the wrapped ring */
    161		free_write_space = curr_srp - curr_swp;
    162	else
    163		/* The ring has not wrapped yet */
    164		free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
    165
    166	dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
    167		bytes_to_write);
    168
    169	dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
    170		free_write_space);
    171
    172	dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
    173	dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
    174
    175	/* Process the msg and write the content onto the bus */
    176	while (bytes_to_write >= free_write_space) {
    177
    178		if (timeout-- == 0) {
    179			printk(KERN_ERR "%s() bus timeout\n", __func__);
    180			ret = SAA_ERR_NO_RESOURCES;
    181			goto out;
    182		}
    183
    184		/* TODO: Review this delay, efficient? */
    185		/* Wait, allowing the hardware fetch time */
    186		mdelay(1);
    187
    188		/* Check the space usage again */
    189		curr_srp = saa7164_readl(bus->m_dwSetReadPos);
    190
    191		/* Deal with ring wrapping issues */
    192		if (curr_srp > curr_swp)
    193			/* Deal with the wrapped ring */
    194			free_write_space = curr_srp - curr_swp;
    195		else
    196			/* Read didn't wrap around the buffer */
    197			free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
    198				curr_swp;
    199
    200	}
    201
    202	/* Calculate the new write position */
    203	new_swp = curr_swp + bytes_to_write;
    204
    205	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
    206	dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
    207		bus->m_dwSizeSetRing);
    208
    209	/*
    210	 * Make a copy of msg->size before it is converted to le16 since it is
    211	 * used in the code below.
    212	 */
    213	size = msg->size;
    214	/* Convert to le16/le32 */
    215	msg->size = (__force u16)cpu_to_le16(msg->size);
    216	msg->command = (__force u32)cpu_to_le32(msg->command);
    217	msg->controlselector = (__force u16)cpu_to_le16(msg->controlselector);
    218
    219	/* Mental Note: line 462 tmmhComResBusPCIe.cpp */
    220
    221	/* Check if we're going to wrap again */
    222	if (new_swp > bus->m_dwSizeSetRing) {
    223
    224		/* Ring wraps */
    225		new_swp -= bus->m_dwSizeSetRing;
    226
    227		space_rem = bus->m_dwSizeSetRing - curr_swp;
    228
    229		dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__,
    230			space_rem);
    231
    232		dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__,
    233			(u32)sizeof(*msg));
    234
    235		if (space_rem < sizeof(*msg)) {
    236			dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
    237
    238			/* Split the msg into pieces as the ring wraps */
    239			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem);
    240			memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem,
    241				sizeof(*msg) - space_rem);
    242
    243			memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
    244				buf, size);
    245
    246		} else if (space_rem == sizeof(*msg)) {
    247			dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
    248
    249			/* Additional data at the beginning of the ring */
    250			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
    251			memcpy_toio(bus->m_pdwSetRing, buf, size);
    252
    253		} else {
    254			/* Additional data wraps around the ring */
    255			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
    256			if (size > 0) {
    257				memcpy_toio(bus->m_pdwSetRing + curr_swp +
    258					sizeof(*msg), buf, space_rem -
    259					sizeof(*msg));
    260				memcpy_toio(bus->m_pdwSetRing, (u8 *)buf +
    261					space_rem - sizeof(*msg),
    262					bytes_to_write - space_rem);
    263			}
    264
    265		}
    266
    267	} /* (new_swp > bus->m_dwSizeSetRing) */
    268	else {
    269		dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
    270
    271		/* The ring buffer doesn't wrap, two simple copies */
    272		memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
    273		memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
    274			size);
    275	}
    276
    277	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
    278
    279	/* Update the bus write position */
    280	saa7164_writel(bus->m_dwSetWritePos, new_swp);
    281
    282	/* Convert back to cpu after writing the msg to the ringbuffer. */
    283	msg->size = le16_to_cpu((__force __le16)msg->size);
    284	msg->command = le32_to_cpu((__force __le32)msg->command);
    285	msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
    286	ret = SAA_OK;
    287
    288out:
    289	saa7164_bus_dump(dev);
    290	mutex_unlock(&bus->lock);
    291	saa7164_bus_verify(dev);
    292	return ret;
    293}
    294
    295/*
    296 * Receive a command or a response from the bus. The implementation does not
    297 * know if it is a command or a response it simply dequeues the data,
    298 * depending on the bus information given in the struct tmComResBusInfo
    299 * structure.
    300 *
    301 * Return Value:
    302 *  0          The function executed successfully.
    303 *  < 0        One or more members are not initialized.
    304 */
    305int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
    306	void *buf, int peekonly)
    307{
    308	struct tmComResBusInfo *bus = &dev->bus;
    309	u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
    310		new_grp, buf_size, space_rem;
    311	struct tmComResInfo msg_tmp;
    312	int ret = SAA_ERR_BAD_PARAMETER;
    313
    314	saa7164_bus_verify(dev);
    315
    316	if (msg == NULL)
    317		return ret;
    318
    319	if (msg->size > dev->bus.m_wMaxReqSize) {
    320		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
    321			__func__);
    322		return ret;
    323	}
    324
    325	if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
    326		printk(KERN_ERR
    327			"%s() Missing msg buf, size should be %d bytes\n",
    328			__func__, msg->size);
    329		return ret;
    330	}
    331
    332	mutex_lock(&bus->lock);
    333
    334	/* Peek the bus to see if a msg exists, if it's not what we're expecting
    335	 * then return cleanly else read the message from the bus.
    336	 */
    337	curr_gwp = saa7164_readl(bus->m_dwGetWritePos);
    338	curr_grp = saa7164_readl(bus->m_dwGetReadPos);
    339
    340	if (curr_gwp == curr_grp) {
    341		ret = SAA_ERR_EMPTY;
    342		goto out;
    343	}
    344
    345	bytes_to_read = sizeof(*msg);
    346
    347	/* Calculate write distance to current read position */
    348	write_distance = 0;
    349	if (curr_gwp >= curr_grp)
    350		/* Write doesn't wrap around the ring */
    351		write_distance = curr_gwp - curr_grp;
    352	else
    353		/* Write wraps around the ring */
    354		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
    355
    356	if (bytes_to_read > write_distance) {
    357		printk(KERN_ERR "%s() No message/response found\n", __func__);
    358		ret = SAA_ERR_INVALID_COMMAND;
    359		goto out;
    360	}
    361
    362	/* Calculate the new read position */
    363	new_grp = curr_grp + bytes_to_read;
    364	if (new_grp > bus->m_dwSizeGetRing) {
    365
    366		/* Ring wraps */
    367		new_grp -= bus->m_dwSizeGetRing;
    368		space_rem = bus->m_dwSizeGetRing - curr_grp;
    369
    370		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
    371		memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
    372			bytes_to_read - space_rem);
    373
    374	} else {
    375		/* No wrapping */
    376		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
    377	}
    378	/* Convert from little endian to CPU */
    379	msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
    380	msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
    381	msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
    382	memcpy(msg, &msg_tmp, sizeof(*msg));
    383
    384	/* No need to update the read positions, because this was a peek */
    385	/* If the caller specifically want to peek, return */
    386	if (peekonly) {
    387		goto peekout;
    388	}
    389
    390	/* Check if the command/response matches what is expected */
    391	if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) ||
    392		(msg_tmp.controlselector != msg->controlselector) ||
    393		(msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) {
    394
    395		printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
    396		saa7164_bus_dumpmsg(dev, msg, buf);
    397		saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
    398		ret = SAA_ERR_INVALID_COMMAND;
    399		goto out;
    400	}
    401
    402	/* Get the actual command and response from the bus */
    403	buf_size = msg->size;
    404
    405	bytes_to_read = sizeof(*msg) + msg->size;
    406	/* Calculate write distance to current read position */
    407	write_distance = 0;
    408	if (curr_gwp >= curr_grp)
    409		/* Write doesn't wrap around the ring */
    410		write_distance = curr_gwp - curr_grp;
    411	else
    412		/* Write wraps around the ring */
    413		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
    414
    415	if (bytes_to_read > write_distance) {
    416		printk(KERN_ERR "%s() Invalid bus state, missing msg or mangled ring, faulty H/W / bad code?\n",
    417		       __func__);
    418		ret = SAA_ERR_INVALID_COMMAND;
    419		goto out;
    420	}
    421
    422	/* Calculate the new read position */
    423	new_grp = curr_grp + bytes_to_read;
    424	if (new_grp > bus->m_dwSizeGetRing) {
    425
    426		/* Ring wraps */
    427		new_grp -= bus->m_dwSizeGetRing;
    428		space_rem = bus->m_dwSizeGetRing - curr_grp;
    429
    430		if (space_rem < sizeof(*msg)) {
    431			if (buf)
    432				memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
    433					space_rem, buf_size);
    434
    435		} else if (space_rem == sizeof(*msg)) {
    436			if (buf)
    437				memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
    438		} else {
    439			/* Additional data wraps around the ring */
    440			if (buf) {
    441				memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
    442					sizeof(*msg), space_rem - sizeof(*msg));
    443				memcpy_fromio(buf + space_rem - sizeof(*msg),
    444					bus->m_pdwGetRing, bytes_to_read -
    445					space_rem);
    446			}
    447
    448		}
    449
    450	} else {
    451		/* No wrapping */
    452		if (buf)
    453			memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
    454				buf_size);
    455	}
    456
    457	/* Update the read positions, adjusting the ring */
    458	saa7164_writel(bus->m_dwGetReadPos, new_grp);
    459
    460peekout:
    461	ret = SAA_OK;
    462out:
    463	mutex_unlock(&bus->lock);
    464	saa7164_bus_verify(dev);
    465	return ret;
    466}
    467