summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2021-11-15 14:11:25 +0000
committerDavid S. Miller <davem@davemloft.net>2021-11-15 14:11:25 +0000
commit71812af7234f30362b43ccff33f93890ae4c0655 (patch)
tree92a7bce858d15b20b9c7b7fff83fca3fe3a225ef /drivers/i2c
parentcc0be1ad686fb29a4d127948486f40b17fb34b50 (diff)
parent80be9b2c0d93dabe5dc0b4ee41da12422ed25d71 (diff)
downloadlinux-71812af7234f30362b43ccff33f93890ae4c0655.tar.gz
linux-71812af7234f30362b43ccff33f93890ae4c0655.tar.bz2
linux-71812af7234f30362b43ccff33f93890ae4c0655.zip
Merge branch 'mctp-i2c-driver'
Matt Johnston says: ==================== MCTP I2C driver This patch series adds a netdev driver providing MCTP transport over I2C. It applies against net-next using recent MCTP changes there, though also has I2C core changes for review. I'll leave it to maintainers where it should be applied - please let me know if it needs to be submitted differently. The I2C patches were previously sent as RFC though the only feedback there was an ack to 255 bytes for aspeed. The dt-bindings patch went through review on the list. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c5
-rw-r--r--drivers/i2c/busses/i2c-npcm7xx.c3
-rw-r--r--drivers/i2c/i2c-core-smbus.c20
-rw-r--r--drivers/i2c/i2c-dev.c93
4 files changed, 97 insertions, 24 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 67e8b97c0c95..7395f3702fae 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -533,7 +533,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
msg->buf[bus->buf_index++] = recv_byte;
if (msg->flags & I2C_M_RECV_LEN) {
- if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) {
+ if (unlikely(recv_byte > I2C_SMBUS_V3_BLOCK_MAX)) {
bus->cmd_err = -EPROTO;
aspeed_i2c_do_stop(bus);
goto out_no_complete;
@@ -718,7 +718,8 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_V3_BLOCK;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
index 2ad166355ec9..6d60f65add85 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -1399,7 +1399,7 @@ static void npcm_i2c_irq_master_handler_read(struct npcm_i2c *bus)
if (bus->read_block_use) {
/* first byte in block protocol is the size: */
data = npcm_i2c_rd_byte(bus);
- data = clamp_val(data, 1, I2C_SMBUS_BLOCK_MAX);
+ data = clamp_val(data, 1, I2C_SMBUS_V3_BLOCK_MAX);
bus->rd_size = data + block_extra_bytes_size;
bus->rd_buf[bus->rd_ind++] = data;
@@ -2187,6 +2187,7 @@ static u32 npcm_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_PEC |
+ I2C_FUNC_SMBUS_V3_BLOCK |
I2C_FUNC_SLAVE;
}
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index e5b2d1465e7e..743415584aba 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -303,7 +303,8 @@ static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val)
bool is_read = msg->flags & I2C_M_RD;
unsigned char *dma_buf;
- dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL);
+ dma_buf = kzalloc(I2C_SMBUS_V3_BLOCK_MAX + (is_read ? 2 : 3),
+ GFP_KERNEL);
if (!dma_buf)
return;
@@ -329,9 +330,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
* initialize most things with sane defaults, to keep the code below
* somewhat simpler.
*/
- unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
- unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
+ unsigned char msgbuf0[I2C_SMBUS_V3_BLOCK_MAX+3];
+ unsigned char msgbuf1[I2C_SMBUS_V3_BLOCK_MAX+2];
int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1;
+ u16 block_max;
u8 partial_pec = 0;
int status;
struct i2c_msg msg[2] = {
@@ -350,6 +352,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
+ /* Drivers must opt in to 255 byte max block size */
+ block_max = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_V3_BLOCK)
+ ? I2C_SMBUS_V3_BLOCK_MAX : I2C_SMBUS_BLOCK_MAX;
+
msgbuf0[0] = command;
switch (size) {
case I2C_SMBUS_QUICK:
@@ -399,7 +405,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
i2c_smbus_try_get_dmabuf(&msg[1], 0);
} else {
msg[0].len = data->block[0] + 2;
- if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
+ if (msg[0].len > block_max + 2) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
@@ -413,7 +419,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
case I2C_SMBUS_BLOCK_PROC_CALL:
nmsgs = 2; /* Another special case */
read_write = I2C_SMBUS_READ;
- if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+ if (data->block[0] > block_max) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[0]);
@@ -430,7 +436,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
i2c_smbus_try_get_dmabuf(&msg[1], 0);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
- if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+ if (data->block[0] > block_max) {
dev_err(&adapter->dev, "Invalid block %s size %d\n",
read_write == I2C_SMBUS_READ ? "read" : "write",
data->block[0]);
@@ -498,7 +504,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
- if (msg[1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ if (msg[1].buf[0] > block_max) {
dev_err(&adapter->dev,
"Invalid block size returned: %d\n",
msg[1].buf[0]);
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index bce0e8bb7852..5ee9118c0407 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -46,6 +46,24 @@ struct i2c_dev {
struct cdev cdev;
};
+/* The userspace union i2c_smbus_data for I2C_SMBUS ioctl is limited
+ * to 32 bytes (I2C_SMBUS_BLOCK_MAX) for compatibility.
+ */
+union compat_i2c_smbus_data {
+ __u8 byte;
+ __u16 word;
+ __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
+ /* and one more for user-space compatibility */
+};
+
+/* Must match i2c-dev.h definition with compat .data member */
+struct i2c_smbus_ioctl_data {
+ __u8 read_write;
+ __u8 command;
+ __u32 size;
+ union compat_i2c_smbus_data __user *data;
+};
+
#define I2C_MINORS (MINORMASK + 1)
static LIST_HEAD(i2c_dev_list);
static DEFINE_SPINLOCK(i2c_dev_list_lock);
@@ -235,14 +253,17 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
unsigned nmsgs, struct i2c_msg *msgs)
{
- u8 __user **data_ptrs;
+ u8 __user **data_ptrs = NULL;
+ u16 *orig_lens = NULL;
int i, res;
+ res = -ENOMEM;
data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL);
- if (data_ptrs == NULL) {
- kfree(msgs);
- return -ENOMEM;
- }
+ if (data_ptrs == NULL)
+ goto out;
+ orig_lens = kmalloc_array(nmsgs, sizeof(u16), GFP_KERNEL);
+ if (orig_lens == NULL)
+ goto out;
res = 0;
for (i = 0; i < nmsgs; i++) {
@@ -253,12 +274,30 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
}
data_ptrs[i] = (u8 __user *)msgs[i].buf;
- msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len);
+ msgs[i].buf = NULL;
+ if (msgs[i].len < 1) {
+ /* Sanity check */
+ res = -EINVAL;
+ break;
+
+ }
+ /* Allocate a larger buffer to accommodate possible 255 byte
+ * blocks. Read results will be dropped later
+ * if they are too large for the original length.
+ */
+ orig_lens[i] = msgs[i].len;
+ msgs[i].buf = kmalloc(msgs[i].len + I2C_SMBUS_V3_BLOCK_MAX,
+ GFP_USER | __GFP_NOWARN);
if (IS_ERR(msgs[i].buf)) {
res = PTR_ERR(msgs[i].buf);
break;
}
- /* memdup_user allocates with GFP_KERNEL, so DMA is ok */
+ if (copy_from_user(msgs[i].buf, data_ptrs[i], msgs[i].len)) {
+ kfree(msgs[i].buf);
+ res = -EFAULT;
+ break;
+ }
+ /* Buffer from kmalloc, so DMA is ok */
msgs[i].flags |= I2C_M_DMA_SAFE;
/*
@@ -274,7 +313,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
*/
if (msgs[i].flags & I2C_M_RECV_LEN) {
if (!(msgs[i].flags & I2C_M_RD) ||
- msgs[i].len < 1 || msgs[i].buf[0] < 1 ||
+ msgs[i].buf[0] < 1 ||
msgs[i].len < msgs[i].buf[0] +
I2C_SMBUS_BLOCK_MAX) {
i++;
@@ -297,12 +336,16 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
res = i2c_transfer(client->adapter, msgs, nmsgs);
while (i-- > 0) {
if (res >= 0 && (msgs[i].flags & I2C_M_RD)) {
- if (copy_to_user(data_ptrs[i], msgs[i].buf,
- msgs[i].len))
+ if (orig_lens[i] < msgs[i].len)
+ res = -EINVAL;
+ else if (copy_to_user(data_ptrs[i], msgs[i].buf,
+ msgs[i].len))
res = -EFAULT;
}
kfree(msgs[i].buf);
}
+out:
+ kfree(orig_lens);
kfree(data_ptrs);
kfree(msgs);
return res;
@@ -310,7 +353,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
u8 read_write, u8 command, u32 size,
- union i2c_smbus_data __user *data)
+ union compat_i2c_smbus_data __user *data)
{
union i2c_smbus_data temp = {};
int datasize, res;
@@ -371,6 +414,16 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
if (copy_from_user(&temp, data, datasize))
return -EFAULT;
}
+ if ((size == I2C_SMBUS_BLOCK_PROC_CALL ||
+ size == I2C_SMBUS_I2C_BLOCK_DATA ||
+ size == I2C_SMBUS_BLOCK_DATA) &&
+ read_write == I2C_SMBUS_WRITE &&
+ temp.block[0] > I2C_SMBUS_BLOCK_MAX) {
+ /* Don't accept writes larger than the buffer size */
+ dev_dbg(&client->adapter->dev, "block write is too large");
+ return -EINVAL;
+
+ }
if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
/* Convert old I2C block commands to the new
convention. This preserves binary compatibility. */
@@ -380,9 +433,21 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
}
res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
read_write, command, size, &temp);
- if (!res && ((size == I2C_SMBUS_PROC_CALL) ||
- (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
- (read_write == I2C_SMBUS_READ))) {
+ if (res)
+ return res;
+ if ((size == I2C_SMBUS_BLOCK_PROC_CALL ||
+ size == I2C_SMBUS_I2C_BLOCK_DATA ||
+ size == I2C_SMBUS_BLOCK_DATA) &&
+ read_write == I2C_SMBUS_READ &&
+ temp.block[0] > I2C_SMBUS_BLOCK_MAX) {
+ /* Don't accept reads larger than the buffer size */
+ dev_dbg(&client->adapter->dev, "block read is too large");
+ return -EINVAL;
+
+ }
+ if ((size == I2C_SMBUS_PROC_CALL) ||
+ (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
+ (read_write == I2C_SMBUS_READ)) {
if (copy_to_user(data, &temp, datasize))
return -EFAULT;
}