Commit fbd2e227 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull staging driver fixes from Greg KH:
 "Here are some staging driver fixes that missed 6.17-final due to my
  travel schedule. They fix a number of reported issues in the axis-fifo
  driver, one of which was just independently discovered by someone else
  today so someone is looking at this code.

  All of these fixes have been in linux-next for many weeks with no
  reported issues"

* tag 'staging-6.18-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging:
  staging: axis-fifo: flush RX FIFO on read errors
  staging: axis-fifo: fix TX handling on copy_from_user() failure
  staging: axis-fifo: fix maximum TX packet length check
parents 3d15d6c1 82a051e2
Loading
Loading
Loading
Loading
+31 −37
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@
#define DRIVER_NAME "axis_fifo"

#define READ_BUF_SIZE 128U /* read buffer length in words */
#define WRITE_BUF_SIZE 128U /* write buffer length in words */

#define AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN	4

@@ -231,6 +230,7 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
	}

	bytes_available = ioread32(fifo->base_addr + XLLF_RLR_OFFSET);
	words_available = bytes_available / sizeof(u32);
	if (!bytes_available) {
		dev_err(fifo->dt_device, "received a packet of length 0\n");
		ret = -EIO;
@@ -241,7 +241,7 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
		dev_err(fifo->dt_device, "user read buffer too small (available bytes=%zu user buffer bytes=%zu)\n",
			bytes_available, len);
		ret = -EINVAL;
		goto end_unlock;
		goto err_flush_rx;
	}

	if (bytes_available % sizeof(u32)) {
@@ -250,11 +250,9 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
		 */
		dev_err(fifo->dt_device, "received a packet that isn't word-aligned\n");
		ret = -EIO;
		goto end_unlock;
		goto err_flush_rx;
	}

	words_available = bytes_available / sizeof(u32);

	/* read data into an intermediate buffer, copying the contents
	 * to userspace when the buffer is full
	 */
@@ -266,18 +264,23 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
			tmp_buf[i] = ioread32(fifo->base_addr +
					      XLLF_RDFD_OFFSET);
		}
		words_available -= copy;

		if (copy_to_user(buf + copied * sizeof(u32), tmp_buf,
				 copy * sizeof(u32))) {
			ret = -EFAULT;
			goto end_unlock;
			goto err_flush_rx;
		}

		copied += copy;
		words_available -= copy;
	}
	mutex_unlock(&fifo->read_lock);

	return bytes_available;

	ret = bytes_available;
err_flush_rx:
	while (words_available--)
		ioread32(fifo->base_addr + XLLF_RDFD_OFFSET);

end_unlock:
	mutex_unlock(&fifo->read_lock);
@@ -305,11 +308,8 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
{
	struct axis_fifo *fifo = (struct axis_fifo *)f->private_data;
	unsigned int words_to_write;
	unsigned int copied;
	unsigned int copy;
	unsigned int i;
	u32 *txbuf;
	int ret;
	u32 tmp_buf[WRITE_BUF_SIZE];

	if (len % sizeof(u32)) {
		dev_err(fifo->dt_device,
@@ -325,11 +325,17 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
		return -EINVAL;
	}

	if (words_to_write > fifo->tx_fifo_depth) {
		dev_err(fifo->dt_device, "tried to write more words [%u] than slots in the fifo buffer [%u]\n",
			words_to_write, fifo->tx_fifo_depth);
	/*
	 * In 'Store-and-Forward' mode, the maximum packet that can be
	 * transmitted is limited by the size of the FIFO, which is
	 * (C_TX_FIFO_DEPTH–4)*(data interface width/8) bytes.
	 *
	 * Do not attempt to send a packet larger than 'tx_fifo_depth - 4',
	 * otherwise a 'Transmit Packet Overrun Error' interrupt will be
	 * raised, which requires a reset of the TX circuit to recover.
	 */
	if (words_to_write > (fifo->tx_fifo_depth - 4))
		return -EINVAL;
	}

	if (fifo->write_flags & O_NONBLOCK) {
		/*
@@ -368,32 +374,20 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
		}
	}

	/* write data from an intermediate buffer into the fifo IP, refilling
	 * the buffer with userspace data as needed
	 */
	copied = 0;
	while (words_to_write > 0) {
		copy = min(words_to_write, WRITE_BUF_SIZE);

		if (copy_from_user(tmp_buf, buf + copied * sizeof(u32),
				   copy * sizeof(u32))) {
			ret = -EFAULT;
	txbuf = vmemdup_user(buf, len);
	if (IS_ERR(txbuf)) {
		ret = PTR_ERR(txbuf);
		goto end_unlock;
	}

		for (i = 0; i < copy; i++)
			iowrite32(tmp_buf[i], fifo->base_addr +
				  XLLF_TDFD_OFFSET);

		copied += copy;
		words_to_write -= copy;
	}

	ret = copied * sizeof(u32);
	for (int i = 0; i < words_to_write; ++i)
		iowrite32(txbuf[i], fifo->base_addr + XLLF_TDFD_OFFSET);

	/* write packet size to fifo */
	iowrite32(ret, fifo->base_addr + XLLF_TLR_OFFSET);
	iowrite32(len, fifo->base_addr + XLLF_TLR_OFFSET);

	ret = len;
	kvfree(txbuf);
end_unlock:
	mutex_unlock(&fifo->write_lock);