OpenCloudOS-Kernel/drivers/net/ethernet/asix/ax88796c_spi.c

116 lines
2.9 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010 ASIX Electronics Corporation
* Copyright (c) 2020 Samsung Electronics Co., Ltd.
*
* ASIX AX88796C SPI Fast Ethernet Linux driver
*/
#define pr_fmt(fmt) "ax88796c: " fmt
#include <linux/string.h>
#include <linux/spi/spi.h>
#include "ax88796c_spi.h"
const u8 ax88796c_rx_cmd_buf[5] = {AX_SPICMD_READ_RXQ, 0xFF, 0xFF, 0xFF, 0xFF};
const u8 ax88796c_tx_cmd_buf[4] = {AX_SPICMD_WRITE_TXQ, 0xFF, 0xFF, 0xFF};
/* driver bus management functions */
int axspi_wakeup(struct axspi_data *ax_spi)
{
int ret;
ax_spi->cmd_buf[0] = AX_SPICMD_EXIT_PWD; /* OP */
ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 1);
if (ret)
dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
return ret;
}
int axspi_read_status(struct axspi_data *ax_spi, struct spi_status *status)
{
int ret;
/* OP */
ax_spi->cmd_buf[0] = AX_SPICMD_READ_STATUS;
net: ax88796c: do not receive data in pointer Function axspi_read_status calls: ret = spi_write_then_read(ax_spi->spi, ax_spi->cmd_buf, 1, (u8 *)&status, 3); status is a pointer to a struct spi_status, which is 3-byte wide: struct spi_status { u16 isr; u8 status; }; But &status is the pointer to this pointer, and spi_write_then_read does not dereference this parameter: int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx) Therefore axspi_read_status currently receive a SPI response in the pointer status, which overwrites 24 bits of the pointer. Thankfully, on Little-Endian systems, the pointer is only used in le16_to_cpus(&status->isr); ... which is a no-operation. So there, the overwritten pointer is not dereferenced. Nevertheless on Big-Endian systems, this can lead to dereferencing pointers after their 24 most significant bits were overwritten. And in all systems this leads to possible use of uninitialized value in functions calling spi_write_then_read which expect status to be initialized when the function returns. Moreover function axspi_read_status (and macro AX_READ_STATUS) do not seem to be used anywhere. So currently this seems to be dead code. Fix the issue anyway so that future code works properly when using function axspi_read_status. Fixes: a97c69ba4f30 ("net: ax88796c: ASIX AX88796C SPI Ethernet Adapter Driver") Signed-off-by: Nicolas Iooss <nicolas.iooss_linux@m4x.org> Acked-by: Łukasz Stelmach <l.stelmach@samsung.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-11-22 04:06:42 +08:00
ret = spi_write_then_read(ax_spi->spi, ax_spi->cmd_buf, 1, (u8 *)status, 3);
if (ret)
dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
else
le16_to_cpus(&status->isr);
return ret;
}
int axspi_read_rxq(struct axspi_data *ax_spi, void *data, int len)
{
struct spi_transfer *xfer = ax_spi->spi_rx_xfer;
int ret;
memcpy(ax_spi->cmd_buf, ax88796c_rx_cmd_buf, 5);
xfer->tx_buf = ax_spi->cmd_buf;
xfer->rx_buf = NULL;
xfer->len = ax_spi->comp ? 2 : 5;
xfer->bits_per_word = 8;
spi_message_add_tail(xfer, &ax_spi->rx_msg);
xfer++;
xfer->rx_buf = data;
xfer->tx_buf = NULL;
xfer->len = len;
xfer->bits_per_word = 8;
spi_message_add_tail(xfer, &ax_spi->rx_msg);
ret = spi_sync(ax_spi->spi, &ax_spi->rx_msg);
if (ret)
dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
return ret;
}
int axspi_write_txq(const struct axspi_data *ax_spi, void *data, int len)
{
return spi_write(ax_spi->spi, data, len);
}
u16 axspi_read_reg(struct axspi_data *ax_spi, u8 reg)
{
int ret;
int len = ax_spi->comp ? 3 : 4;
ax_spi->cmd_buf[0] = 0x03; /* OP code read register */
ax_spi->cmd_buf[1] = reg; /* register address */
ax_spi->cmd_buf[2] = 0xFF; /* dumy cycle */
ax_spi->cmd_buf[3] = 0xFF; /* dumy cycle */
ret = spi_write_then_read(ax_spi->spi,
ax_spi->cmd_buf, len,
ax_spi->rx_buf, 2);
if (ret) {
dev_err(&ax_spi->spi->dev,
"%s() failed: ret = %d\n", __func__, ret);
return 0xFFFF;
}
le16_to_cpus((u16 *)ax_spi->rx_buf);
return *(u16 *)ax_spi->rx_buf;
}
int axspi_write_reg(struct axspi_data *ax_spi, u8 reg, u16 value)
{
int ret;
memset(ax_spi->cmd_buf, 0, sizeof(ax_spi->cmd_buf));
ax_spi->cmd_buf[0] = AX_SPICMD_WRITE_REG; /* OP code read register */
ax_spi->cmd_buf[1] = reg; /* register address */
ax_spi->cmd_buf[2] = value;
ax_spi->cmd_buf[3] = value >> 8;
ret = spi_write(ax_spi->spi, ax_spi->cmd_buf, 4);
if (ret)
dev_err(&ax_spi->spi->dev, "%s() failed: ret = %d\n", __func__, ret);
return ret;
}