[Patch 8/9]: vt: Enhancements to the VT ioctl interface

Alan Mackenzie <[email protected]>
Newsgroups gmane.linux.serial,gmane.linux.kernel,gmane.comp.video.dri.devel
Message-ID <[email protected]>
vt: 32b glyph: 10. Enhancements to the VT ioctl interface

Handle the current 16-bit P/GIO_UNIMAP ioctls by converting
to/from 32-bit code-points/glyph numbers.  Add handling for new
32-bit ioctls P/GIO_UNIMAP21.  Convert to and from __user in
vt_ioctl.c rather than in vt.c or consolemap.c.

Signed-off-by: Alan Mackenzie <[email protected]>

diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 28993a3d0acb..05986430e1bb 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -24,7 +24,6 @@
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/console.h>
-#include <linux/consolemap.h>
 #include <linux/signal.h>
 #include <linux/suspend.h>
 #include <linux/timex.h>
@@ -36,6 +35,7 @@
 
 #include <linux/kbd_kern.h>
 #include <linux/vt_kern.h>
+#include <linux/consolemap.h>
 #include <linux/kbd_diacr.h>
 #include <linux/selection.h>
 
@@ -484,10 +484,155 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
 	return 0;
 }
 
+#ifdef CONFIG_FB_GLYPH_21BIT
+static int vt__user_unipair_8_to_21(unsigned int ct,
+				    struct unipair __user *entries,
+				    struct unipair21 **tmp21_entries)
+{
+	struct unipair *tmp_entries;
+	int i, ret = 0;
+
+	tmp_entries = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
+	if (!tmp_entries)
+		return -ENOMEM;
+	if (copy_from_user(tmp_entries, entries, ct * sizeof(struct unipair))) {
+		ret = -EFAULT;
+		goto free_tmp;
+	}
+	*tmp21_entries = kmalloc_array(ct, sizeof(struct unipair21), GFP_KERNEL);
+	if (!*tmp21_entries) {
+		ret = -ENOMEM;
+		goto free_tmp;
+	}
+	for (i = 0; i < ct; i++) {
+		(*tmp21_entries)[i].unicode = tmp_entries[i].unicode;
+		(*tmp21_entries)[i].fontpos = tmp_entries[i].fontpos;
+	}
+free_tmp: kfree(tmp_entries);
+	return ret;
+}
+
+static int vt_unipair_21_to__user_8(unsigned int ct,
+				    struct unipair21 *entries21,
+				    struct unipair __user *entries)
+{
+	struct unipair *tmp_entries;
+	int i, ret = 0;
+
+	tmp_entries = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
+	if (!tmp_entries)
+		return -ENOMEM;
+	for (i = 0; i < ct; i++) {
+		if ((entries21[i].unicode > 0xffff) ||
+		    (entries21[i].fontpos > 0xffff)) {
+			ret = -EINVAL;
+			goto free_tmp;
+		}
+		tmp_entries[i].unicode = entries21[i].unicode;
+		tmp_entries[i].fontpos = entries21[i].fontpos;
+	}
+	if (copy_to_user(entries, tmp_entries, ct * sizeof(struct unipair)))
+		ret = -EFAULT;
+free_tmp: kfree(tmp_entries);
+	return ret;
+}
+
 static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud,
 		bool perm, struct vc_data *vc)
 {
 	struct unimapdesc tmp;
+	struct unipair21 *tmp21_entries = NULL;
+	int ret = 0, ret1 = 0;
+	unsigned int ct;
+
+	if (copy_from_user(&tmp, user_ud, sizeof(tmp)))
+		return -EFAULT;
+	switch (cmd) {
+	case PIO_UNIMAP:
+		if (!perm)
+			return -EPERM;
+		ret = vt__user_unipair_8_to_21(tmp.entry_ct, tmp.entries,
+					       &tmp21_entries);
+		if (ret)
+			return ret;
+		ret = con_set_unimap(vc, tmp.entry_ct, tmp21_entries);
+		kfree(tmp21_entries);
+		return ret;
+
+	case GIO_UNIMAP:
+		if (!perm && fg_console != vc->vc_num)
+			return -EPERM;
+		tmp21_entries = kmalloc_array(tmp.entry_ct,
+					      sizeof(struct unipair21),
+					      GFP_KERNEL);
+		if (!tmp21_entries)
+			return -ENOMEM;
+		ret = con_get_unimap(vc, tmp.entry_ct, &ct, tmp21_entries);
+		if (ret)
+			goto free_tmp21;
+		ret = vt_unipair_21_to__user_8(ct, tmp21_entries, tmp.entries);
+free_tmp21:	ret1 = put_user(ct, &user_ud->entry_ct);
+		kfree(tmp21_entries);
+		return ret ? ret : ret1;
+	}
+	return 0;
+}
+
+static inline int do_unimap_ioctl21(int cmd, struct unimapdesc21 __user *user_ud,
+		bool perm, struct vc_data *vc)
+{
+	struct unimapdesc21 tmp;
+	struct unipair21 *k_entries;
+	int ret = 0, ret1 = 0;
+
+	if (copy_from_user(&tmp, user_ud, sizeof(tmp)))
+		return -EFAULT;
+	switch (cmd) {
+	case PIO_UNIMAP21:
+		if (!perm)
+			return -EPERM;
+		k_entries = kmalloc_array(tmp.entry_ct,
+					  sizeof(struct unipair21),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		if (copy_from_user(k_entries, tmp.entries,
+				   tmp.entry_ct * sizeof(struct unipair21))) {
+			ret = -EFAULT;
+			goto free_k_entries;
+		}
+		ret = con_set_unimap(vc, tmp.entry_ct, k_entries);
+free_k_entries: kfree(k_entries);
+		return ret;
+	case GIO_UNIMAP21:
+		if (!perm && fg_console != vc->vc_num)
+			return -EPERM;
+		k_entries = kmalloc_array(tmp.entry_ct,
+					  sizeof(struct unipair21),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		ret = con_get_unimap(vc, tmp.entry_ct, &tmp.entry_ct,
+				     k_entries);
+		if (ret)
+			goto free_k_entries1;
+		if (copy_to_user(tmp.entries, k_entries,
+				 tmp.entry_ct * sizeof(struct unipair21)))
+			ret = -EFAULT;
+free_k_entries1: ret1 = put_user(tmp.entry_ct, &user_ud->entry_ct);
+		kfree(k_entries);
+		return ret ? ret : ret1;
+	}
+	return 0;
+}
+
+#else
+static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud,
+		bool perm, struct vc_data *vc)
+{
+	struct unimapdesc tmp;
+	struct unipair *k_entries;
+	int ret = 0, ret1 = 0;
 
 	if (copy_from_user(&tmp, user_ud, sizeof tmp))
 		return -EFAULT;
@@ -495,15 +640,41 @@ static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud,
 	case PIO_UNIMAP:
 		if (!perm)
 			return -EPERM;
-		return con_set_unimap(vc, tmp.entry_ct, tmp.entries);
+		k_entries = kmalloc_array(tmp.entry_ct,
+					  sizeof(struct unipair),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		if (copy_from_user(k_entries, tmp.entries,
+				   tmp.entry_ct * sizeof(struct unipair))) {
+			ret = -EFAULT;
+			goto free_k_entries;
+		}
+		ret = con_set_unimap(vc, tmp.entry_ct, k_entries);
+free_k_entries: kfree(k_entries);
+		return ret;
 	case GIO_UNIMAP:
 		if (!perm && fg_console != vc->vc_num)
 			return -EPERM;
-		return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct),
-				tmp.entries);
+		k_entries = kmalloc_array(tmp.entry_ct,
+					  sizeof(struct unipair),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		ret = con_get_unimap(vc, tmp.entry_ct, &tmp.entry_ct,
+				     k_entries);
+		if (ret)
+			goto free_k_entries1;
+		if (copy_to_user(tmp.entries, k_entries,
+				 tmp.entry_ct * sizeof(struct unipair)))
+			ret = -EFAULT;
+free_k_entries1: ret1 = put_user(tmp.entry_ct, &user_ud->entry_ct);
+		kfree(k_entries);
+		return ret ? ret : ret1;
 	}
 	return 0;
 }
+#endif /* CONFIG_FB_GLYPH_21BIT */
 
 static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up,
 		bool perm)
@@ -543,6 +714,12 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up,
 	case GIO_UNIMAP:
 		return do_unimap_ioctl(cmd, up, perm, vc);
 
+#ifdef CONFIG_FB_GLYPH_21BIT
+	case PIO_UNIMAP21:
+	case GIO_UNIMAP21:
+		return do_unimap_ioctl21(cmd, up, perm, vc);
+#endif
+
 	default:
 		return -ENOIOCTLCMD;
 	}
@@ -1026,16 +1203,20 @@ compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop,
 }
 
 struct compat_unimapdesc {
-	unsigned short entry_ct;
+	u16 entry_ct;
 	compat_caddr_t entries;
 };
 
+#ifdef CONFIG_FB_GLYPH_21BIT
 static inline int
 compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
 			 int perm, struct vc_data *vc)
 {
 	struct compat_unimapdesc tmp;
 	struct unipair __user *tmp_entries;
+	struct unipair21 *tmp21_entries = NULL;
+	int ret = 0, ret1 = 0;
+	unsigned int ct;
 
 	if (copy_from_user(&tmp, user_ud, sizeof tmp))
 		return -EFAULT;
@@ -1044,14 +1225,83 @@ compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
 	case PIO_UNIMAP:
 		if (!perm)
 			return -EPERM;
-		return con_set_unimap(vc, tmp.entry_ct, tmp_entries);
+		ret = vt__user_unipair_8_to_21(tmp.entry_ct, tmp_entries,
+					       &tmp21_entries);
+		if (ret)
+			return ret;
+		ret = con_set_unimap(vc, tmp.entry_ct, tmp21_entries);
+		kfree(tmp21_entries);
+		return ret;
+
 	case GIO_UNIMAP:
 		if (!perm && fg_console != vc->vc_num)
 			return -EPERM;
-		return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries);
+		tmp21_entries = kmalloc_array(tmp.entry_ct,
+					      sizeof(struct unipair21),
+					      GFP_KERNEL);
+		if (!tmp21_entries)
+			return -ENOMEM;
+		ret = con_get_unimap(vc, tmp.entry_ct, &ct, tmp21_entries);
+		if (ret)
+			goto free_tmp21;
+		ret = vt_unipair_21_to__user_8(ct, tmp21_entries, tmp_entries);
+free_tmp21:	ret1 = put_user(ct, &user_ud->entry_ct);
+		kfree(tmp21_entries);
+		return ret ? ret : ret1;
+	}
+	return 0;
+}
+#else
+static inline int
+compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
+			 int perm, struct vc_data *vc)
+{
+	struct compat_unimapdesc tmp;
+	struct unipair __user *tmp_entries;
+	struct unipair *k_entries;
+	int ret = 0, ret1 = 0;
+
+	if (copy_from_user(&tmp, user_ud, sizeof(tmp)))
+		return -EFAULT;
+	tmp_entries = compat_ptr(tmp.entries);
+	switch (cmd) {
+	case PIO_UNIMAP:
+		if (!perm)
+			return -EPERM;
+		k_entries = kmalloc_array(tmp.entry_ct,
+					  sizeof(struct unipair),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		if (copy_from_user(k_entries, tmp_entries,
+				   tmp.entry_ct * sizeof(struct unipair))) {
+			ret = -EFAULT;
+			goto free_k_entries;
+		}
+		ret = con_set_unimap(vc, tmp.entry_ct, k_entries);
+free_k_entries: kfree(k_entries);
+		return ret;
+	case GIO_UNIMAP:
+		if (!perm && fg_console != vc->vc_num)
+			return -EPERM;
+		k_entries = kmalloc_array(tmp.entry_ct, sizeof(struct unipair),
+					  GFP_KERNEL);
+		if (!k_entries)
+			return -ENOMEM;
+		ret = con_get_unimap(vc, tmp.entry_ct, &tmp.entry_ct,
+				     k_entries);
+		if (ret)
+			goto free_k_entries1;
+		if (copy_to_user(tmp_entries, k_entries,
+				 tmp.entry_ct * sizeof(struct unipair)))
+			ret = -EFAULT;
+free_k_entries1: ret1 = put_user(tmp.entry_ct, &user_ud->entry_ct);
+		kfree(k_entries);
+		return ret ? ret : ret1;
 	}
 	return 0;
 }
+#endif /* CONFIG_FB_GLYPH_21BIT */
 
 long vt_compat_ioctl(struct tty_struct *tty,
 	     unsigned int cmd, unsigned long arg)
diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h
index 6b384065c013..342654dfbc16 100644
--- a/include/uapi/linux/kd.h
+++ b/include/uapi/linux/kd.h
@@ -56,7 +56,7 @@ typedef char scrnmap_t;
 #define GIO_SCRNMAP	0x4B40	/* get screen mapping from kernel */
 #define PIO_SCRNMAP	0x4B41	/* put screen mapping table in kernel */
 #define GIO_UNISCRNMAP  0x4B69	/* get full Unicode screen mapping */
-#define PIO_UNISCRNMAP  0x4B6A  /* set full Unicode screen mapping */
+#define PIO_UNISCRNMAP  0x4B6A	/* set full Unicode screen mapping */
 
 #define GIO_UNIMAP	0x4B66	/* get unicode-to-font mapping from kernel */
 struct unipair {
@@ -67,8 +67,19 @@ struct unimapdesc {
 	unsigned short entry_ct;
 	struct unipair __user *entries;
 };
+struct unipair21 {
+	unsigned int unicode;
+	unsigned int fontpos;
+};
+struct unimapdesc21 {
+	unsigned int entry_ct;
+	struct unipair21 __user *entries;
+};
 #define PIO_UNIMAP	0x4B67	/* put unicode-to-font mapping in kernel */
 #define PIO_UNIMAPCLR	0x4B68	/* clear table, possibly advise hash algorithm */
+#define GIO_UNIMAP21	0x4B6E	/* get 21-bit unicode-to-font mapping from kernel */
+#define PIO_UNIMAP21	0x4B6F	/* put 21-bit unicode-to-font mapping to kernel */
+
 struct unimapinit {
 	unsigned short advised_hashsize;  /* 0 if no opinion */
 	unsigned short advised_hashstep;  /* 0 if no opinion */
@@ -185,6 +196,6 @@ struct console_font {
 
 /* note: 0x4B00-0x4B4E all have had a value at some time;
    don't reuse for the time being */
-/* note: 0x4B60-0x4B6D, 0x4B70-0x4B72 used above */
+/* note: 0x4B60-0x4B6F, 0x4B70-0x4B72 used above */
 
 #endif /* _UAPI_LINUX_KD_H */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index d008c3d0a9bb..af99aeefe177 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -90,13 +96,13 @@ static inline int con_clear_unimap(struct vc_data *vc)
 	return 0;
 }
 static inline
-int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
+int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair *list)
 {
 	return 0;
 }
 static inline
-int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct,
-		   struct unipair __user *list)
+int con_get_unimap(struct vc_data *vc, ushort ct, ushort *uct,
+		   struct unipair *list)
 {
 	return -EINVAL;
 }


-- 
Alan Mackenzie (Nuremberg, Germany).
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.