git: 682b069c5c56 - main - iicbus: add compat32 support for I2C ioctls

From: Stephen J. Kiernan <stevek_at_FreeBSD.org>
Date: Fri, 01 Dec 2023 01:33:55 UTC
The branch main has been updated by stevek:

URL: https://cgit.FreeBSD.org/src/commit/?id=682b069c5c5643d26199cc1b65717f99c98bce9d

commit 682b069c5c5643d26199cc1b65717f99c98bce9d
Author:     Stephen J. Kiernan <stevek@FreeBSD.org>
AuthorDate: 2023-11-29 19:20:45 +0000
Commit:     Stephen J. Kiernan <stevek@FreeBSD.org>
CommitDate: 2023-12-01 01:33:46 +0000

    iicbus: add compat32 support for I2C ioctls
    
    Some of the I2C ioctl request structures contain pointers and need to
    handle requests from 32-bit applications on 64-bit kernels.
    
    Obtained from:  Juniper Networks, Inc.
    Differential Revision:  https://reviews.freebsd.org/D42836
---
 sys/dev/iicbus/iic.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 108 insertions(+), 3 deletions(-)

diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c
index baaa7a096a14..8c9dbb6bc145 100644
--- a/sys/dev/iicbus/iic.c
+++ b/sys/dev/iicbus/iic.c
@@ -2,6 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 1998, 2001 Nicolas Souchu
+ * Copyright (c) 2023 Juniper Networks, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,6 +28,7 @@
  *
  */
 #include <sys/param.h>
+#include <sys/abi_compat.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
 #include <sys/fcntl.h>
@@ -57,6 +59,30 @@ struct iic_cdevpriv {
 	uint8_t addr;
 };
 
+#ifdef COMPAT_FREEBSD32
+struct iic_msg32 {
+	uint16_t slave;
+	uint16_t flags;
+	uint16_t len;
+	uint32_t buf;
+};
+
+struct iiccmd32 {
+	u_char slave;
+	uint32_t count;
+	uint32_t last;
+	uint32_t buf;
+};
+
+struct iic_rdwr_data32 {
+	uint32_t msgs;
+	uint32_t nmsgs;
+};
+
+#define	I2CWRITE32	_IOW('i', 4, struct iiccmd32)
+#define	I2CREAD32	_IOW('i', 5, struct iiccmd32)
+#define	I2CRDWR32	_IOW('i', 6, struct iic_rdwr_data32)
+#endif
 
 #define	IIC_LOCK(cdp)			sx_xlock(&(cdp)->lock)
 #define	IIC_UNLOCK(cdp)			sx_xunlock(&(cdp)->lock)
@@ -70,7 +96,7 @@ static void iic_identify(driver_t *driver, device_t parent);
 static void iicdtor(void *data);
 static int iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last);
 static int iicuio(struct cdev *dev, struct uio *uio, int ioflag);
-static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags);
+static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags, bool compat32);
 
 static device_method_t iic_methods[] = {
 	/* device interface */
@@ -285,9 +311,36 @@ iicuio(struct cdev *dev, struct uio *uio, int ioflag)
 	return (error);
 }
 
+#ifdef COMPAT_FREEBSD32
+static int
+iic_copyinmsgs32(struct iic_rdwr_data *d, struct iic_msg *buf)
+{
+	struct iic_msg32 msg32;
+	struct iic_msg32 *m32;
+	int error, i;
+
+	m32 = (struct iic_msg32 *)d->msgs;
+	for (i = 0; i < d->nmsgs; i++) {
+		error = copyin(&m32[i], &msg32, sizeof(msg32));
+		if (error != 0)
+			return (error);
+		CP(msg32, buf[i], slave);
+		CP(msg32, buf[i], flags);
+		CP(msg32, buf[i], len);
+		PTRIN_CP(msg32, buf[i], buf);
+	}
+	return (0);
+}
+#endif
+
 static int
-iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags)
+iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags,
+    bool compat32 __unused)
 {
+#ifdef COMPAT_FREEBSD32
+	struct iic_rdwr_data dswab;
+	struct iic_rdwr_data32 *d32;
+#endif
 	struct iic_msg *buf, *m;
 	void **usrbufs;
 	device_t iicdev, parent;
@@ -297,12 +350,25 @@ iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags)
 	iicdev = priv->sc->sc_dev;
 	parent = device_get_parent(iicdev);
 	error = 0;
+#ifdef COMPAT_FREEBSD32
+	if (compat32) {
+		d32 = (struct iic_rdwr_data32 *)d;
+		PTRIN_CP(*d32, dswab, msgs);
+		CP(*d32, dswab, nmsgs);
+		d = &dswab;
+	}
+#endif
 
 	if (d->nmsgs > IIC_RDRW_MAX_MSGS)
 		return (EINVAL);
 
 	buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_IIC, M_WAITOK);
 
+#ifdef COMPAT_FREEBSD32
+	if (compat32)
+		error = iic_copyinmsgs32(d, buf);
+	else
+#endif
 	error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs);
 	if (error != 0) {
 		free(buf, M_IIC);
@@ -355,14 +421,24 @@ iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags)
 static int
 iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
 {
+#ifdef COMPAT_FREEBSD32
+	struct iiccmd iicswab;
+#endif
 	device_t parent, iicdev;
 	struct iiccmd *s;
+#ifdef COMPAT_FREEBSD32
+	struct iiccmd32 *s32;
+#endif
 	struct uio ubuf;
 	struct iovec uvec;
 	struct iic_cdevpriv *priv;
 	int error;
+	bool compat32;
 
 	s = (struct iiccmd *)data;
+#ifdef COMPAT_FREEBSD32
+	s32 = (struct iiccmd32 *)data;
+#endif
 	error = devfs_get_cdevpriv((void**)&priv);
 	if (error != 0)
 		return (error);
@@ -373,6 +449,20 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
 	parent = device_get_parent(iicdev);
 	IIC_LOCK(priv);
 
+#ifdef COMPAT_FREEBSD32
+	switch (cmd) {
+	case I2CWRITE32:
+	case I2CREAD32:
+		CP(*s32, iicswab, slave);
+		CP(*s32, iicswab, count);
+		CP(*s32, iicswab, last);
+		PTRIN_CP(*s32, iicswab, buf);
+		s = &iicswab;
+		break;
+	default:
+		break;
+	}
+#endif
 
 	switch (cmd) {
 	case I2CSTART:
@@ -428,6 +518,9 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
 		break;
 
 	case I2CWRITE:
+#ifdef COMPAT_FREEBSD32
+	case I2CWRITE32:
+#endif
 		if (!priv->started) {
 			error = EINVAL;
 			break;
@@ -445,6 +538,9 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
 		break;
 
 	case I2CREAD:
+#ifdef COMPAT_FREEBSD32
+	case I2CREAD32:
+#endif
 		if (!priv->started) {
 			error = EINVAL;
 			break;
@@ -461,6 +557,9 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
 		error = iicuio_move(priv, &ubuf, s->last);
 		break;
 
+#ifdef COMPAT_FREEBSD32
+	case I2CRDWR32:
+#endif
 	case I2CRDWR:
 		/*
 		 * The rdwr list should be a self-contained set of
@@ -471,7 +570,13 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
 			break;
 		}
 
-		error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags);
+#ifdef COMPAT_FREEBSD32
+		compat32 = (cmd == I2CRDWR32);
+#else
+		compat32 = false;
+#endif
+		error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags,
+		    compat32);
 
 		break;