summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/ioctl32.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-06-17 13:20:30 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-06-17 13:20:30 +0000
commit7acb77a6e7bddd4c4c5aa975bbf976927c013798 (patch)
tree4139829ec6edb85f73774bb95cdec376758bfc73 /arch/sparc64/kernel/ioctl32.c
parent64d58d4c8cd6a89ee218301ec0dc0ebfec91a4db (diff)
Merge with 2.1.43.
Diffstat (limited to 'arch/sparc64/kernel/ioctl32.c')
-rw-r--r--arch/sparc64/kernel/ioctl32.c502
1 files changed, 491 insertions, 11 deletions
diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
index 3db6fa945..d3792dec6 100644
--- a/arch/sparc64/kernel/ioctl32.c
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.3 1997/05/27 19:30:13 jj Exp $
+/* $Id: ioctl32.c,v 1.8 1997/06/04 13:05:15 jj Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -7,22 +7,369 @@
* ioctls.
*/
+#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/hdreg.h>
+#include <linux/md.h>
+#include <linux/kd.h>
+#include <linux/route.h>
+#include <linux/netlink.h>
#include <asm/types.h>
#include <asm/uaccess.h>
-/* As gcc will warn about casting u32 to some ptr, we have to cast it to unsigned long first, and that's what is A() for.
- * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x) or instead of just (void *)x, which will
- * produce warnings */
+/* As gcc will warn about casting u32 to some ptr, we have to cast it to
+ * unsigned long first, and that's what is A() for.
+ * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x)
+ * or instead of just (void *)x, which will produce warnings.
+ */
#define A(x) ((unsigned long)x)
extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
+
+static int w_long(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ unsigned long old_fs = get_fs();
+ int err;
+ unsigned long val;
+
+ set_fs (KERNEL_DS);
+ err = sys_ioctl(fd, cmd, (unsigned long)&val);
+ set_fs (old_fs);
+ if (!err && put_user(val, (u32 *)A(arg)))
+ return -EFAULT;
+ return err;
+}
+struct ifmap32 {
+ u32 mem_start;
+ u32 mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+};
+
+struct ifreq32 {
+#define IFHWADDRLEN 6
+#define IFNAMSIZ 16
+ union {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap32 ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ __kernel_caddr_t32 ifru_data;
+ } ifr_ifru;
+};
+
+struct ifconf32 {
+ int ifc_len; /* size of buffer */
+ __kernel_caddr_t32 ifcbuf;
+};
+
+static inline int dev_ifconf(unsigned int fd, u32 arg)
+{
+ struct ifconf32 ifc32;
+ struct ifconf ifc;
+ struct ifreq32 *ifr32;
+ struct ifreq *ifr;
+ unsigned long old_fs;
+ unsigned int i, j;
+ int err;
+
+ if (copy_from_user(&ifc32, (struct ifconf32 *)A(arg), sizeof(struct ifconf32)))
+ return -EFAULT;
+ ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * sizeof (struct ifreq);
+ ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL);
+ if (!ifc.ifc_buf) return -ENOMEM;
+ ifr = ifc.ifc_req;
+ ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf);
+ for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) {
+ if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) {
+ kfree (ifc.ifc_buf);
+ return -EFAULT;
+ }
+ }
+ old_fs = get_fs(); set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc);
+ set_fs (old_fs);
+ if (!err) {
+ ifr = ifc.ifc_req;
+ ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf);
+ for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len;
+ i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) {
+ if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) {
+ err = -EFAULT;
+ break;
+ }
+ }
+ if (!err) {
+ if (i <= ifc32.ifc_len)
+ ifc32.ifc_len = i;
+ else
+ ifc32.ifc_len = i - sizeof (struct ifreq32);
+ if (copy_to_user((struct ifconf32 *)A(arg), &ifc32, sizeof(struct ifconf32)))
+ err = -EFAULT;
+ }
+ }
+ kfree (ifc.ifc_buf);
+ return err;
+}
+
+static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct ifreq ifr;
+ unsigned long old_fs;
+ int err;
+
+ if (cmd == SIOCSIFMAP) {
+ if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(ifr.ifr_name)) ||
+ __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) ||
+ __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) ||
+ __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.base_addr)) ||
+ __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.irq)) ||
+ __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) ||
+ __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port)))
+ return -EFAULT;
+ } else {
+ if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32)))
+ return -EFAULT;
+ }
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long)&ifr);
+ set_fs (old_fs);
+ if (!err) {
+ switch (cmd) {
+ case SIOCGIFFLAGS:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFMEM:
+ case SIOCGIFHWADDR:
+ case SIOGIFINDEX:
+ case SIOCGIFADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFNETMASK:
+ if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(struct ifreq32)))
+ return -EFAULT;
+ break;
+ case SIOCGIFMAP:
+ if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(ifr.ifr_name)) ||
+ __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) ||
+ __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) ||
+ __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.base_addr)) ||
+ __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.irq)) ||
+ __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) ||
+ __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port)))
+ return -EFAULT;
+ break;
+ }
+ }
+ return err;
+}
+
+struct rtentry32 {
+ u32 rt_pad1;
+ struct sockaddr rt_dst; /* target address */
+ struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */
+ struct sockaddr rt_genmask; /* target network mask (IP) */
+ unsigned short rt_flags;
+ short rt_pad2;
+ u32 rt_pad3;
+ unsigned char rt_tos;
+ unsigned char rt_class;
+ short rt_pad4;
+ short rt_metric; /* +1 for binary compatibility! */
+ /* char * */ u32 rt_dev; /* forcing the device at add */
+ u32 rt_mtu; /* per route MTU/Window */
+ u32 rt_window; /* Window clamping */
+ unsigned short rt_irtt; /* Initial RTT */
+
+};
+
+static inline int routing_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct rtentry r;
+ char devname[16];
+ u32 rtdev;
+ int ret;
+ unsigned long old_fs = get_fs();
+
+ if (get_user (r.rt_pad1, &(((struct rtentry32 *)A(arg))->rt_pad1)) ||
+ copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) ||
+ __get_user (r.rt_flags, &(((struct rtentry32 *)A(arg))->rt_flags)) ||
+ __get_user (r.rt_pad2, &(((struct rtentry32 *)A(arg))->rt_pad2)) ||
+ __get_user (r.rt_pad3, &(((struct rtentry32 *)A(arg))->rt_pad3)) ||
+ __get_user (r.rt_tos, &(((struct rtentry32 *)A(arg))->rt_tos)) ||
+ __get_user (r.rt_class, &(((struct rtentry32 *)A(arg))->rt_class)) ||
+ __get_user (r.rt_pad4, &(((struct rtentry32 *)A(arg))->rt_pad4)) ||
+ __get_user (r.rt_metric, &(((struct rtentry32 *)A(arg))->rt_metric)) ||
+ __get_user (r.rt_mtu, &(((struct rtentry32 *)A(arg))->rt_mtu)) ||
+ __get_user (r.rt_window, &(((struct rtentry32 *)A(arg))->rt_window)) ||
+ __get_user (r.rt_irtt, &(((struct rtentry32 *)A(arg))->rt_irtt)) ||
+ __get_user (rtdev, &(((struct rtentry32 *)A(arg))->rt_dev)) ||
+ (rtdev && copy_from_user (devname, (char *)A(rtdev), 15)))
+ return -EFAULT;
+ if (rtdev) {
+ r.rt_dev = devname; devname[15] = 0;
+ } else
+ r.rt_dev = 0;
+ set_fs (KERNEL_DS);
+ ret = sys_ioctl (fd, cmd, (long)&r);
+ set_fs (old_fs);
+ return ret;
+}
+
+struct nlmsghdr32 {
+ u32 nlmsg_len; /* Length of message including header */
+ u32 nlmsg_type; /* Message type */
+ u32 nlmsg_seq; /* Sequence number */
+ u32 nlmsg_pid; /* Sending process PID */
+ unsigned char nlmsg_data[0];
+};
+
+struct in_rtmsg32 {
+ struct in_addr rtmsg_prefix;
+ struct in_addr rtmsg_gateway;
+ unsigned rtmsg_flags;
+ u32 rtmsg_mtu;
+ u32 rtmsg_window;
+ unsigned short rtmsg_rtt;
+ short rtmsg_metric;
+ unsigned char rtmsg_tos;
+ unsigned char rtmsg_class;
+ unsigned char rtmsg_prefixlen;
+ unsigned char rtmsg_reserved;
+ int rtmsg_ifindex;
+};
+
+struct in_ifmsg32 {
+ struct sockaddr ifmsg_lladdr;
+ struct in_addr ifmsg_prefix;
+ struct in_addr ifmsg_brd;
+ unsigned ifmsg_flags;
+ u32 ifmsg_mtu;
+ short ifmsg_metric;
+ unsigned char ifmsg_prefixlen;
+ unsigned char ifmsg_reserved;
+ int ifmsg_index;
+ char ifmsg_name[16];
+};
+
+static inline int rtmsg_ioctl(unsigned int fd, u32 arg)
+{
+ struct {
+ struct nlmsghdr n;
+ union {
+ struct in_rtmsg rt;
+ struct in_ifmsg iff;
+ struct in_rtctlmsg ctl;
+ struct in_rtrulemsg rule;
+ } u;
+ } nn;
+ char *p;
+ int ret;
+ unsigned long old_fs = get_fs();
+
+ if (get_user (nn.n.nlmsg_len, &(((struct nlmsghdr32 *)A(arg))->nlmsg_len)) ||
+ __get_user (nn.n.nlmsg_type, &(((struct nlmsghdr32 *)A(arg))->nlmsg_type)) ||
+ __get_user (nn.n.nlmsg_seq, &(((struct nlmsghdr32 *)A(arg))->nlmsg_seq)) ||
+ __get_user (nn.n.nlmsg_pid, &(((struct nlmsghdr32 *)A(arg))->nlmsg_pid)) ||
+ __get_user (nn.n.nlmsg_data[0], &(((struct nlmsghdr32 *)A(arg))->nlmsg_data[0])))
+ return -EFAULT;
+ p = ((char *)(&nn.n)) + sizeof(struct nlmsghdr);
+ arg += sizeof(struct nlmsghdr32);
+ switch (nn.n.nlmsg_type) {
+ case RTMSG_NEWRULE:
+ case RTMSG_DELRULE:
+ if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtrulemsg)
+ - sizeof(struct in_rtmsg) + sizeof(struct in_rtmsg32))
+ return -EINVAL;
+ if (copy_from_user (p, (struct in_rtrulemsg *)A(arg), sizeof(struct in_rtrulemsg) - sizeof(struct in_rtmsg)))
+ return -EFAULT;
+ nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtrulemsg);
+ p += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg);
+ arg += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg);
+ goto newroute;
+ case RTMSG_NEWROUTE:
+ case RTMSG_DELROUTE:
+ if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtmsg))
+ return -EINVAL;
+ nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtmsg);
+newroute:
+ if (copy_from_user (p, (struct in_rtmsg32 *)A(arg), 2*sizeof(struct in_addr) + sizeof(unsigned)) ||
+ __get_user (((struct in_rtmsg *)p)->rtmsg_mtu, &((struct in_rtmsg32 *)A(arg))->rtmsg_mtu) ||
+ __get_user (((struct in_rtmsg *)p)->rtmsg_window, &((struct in_rtmsg32 *)A(arg))->rtmsg_window) ||
+ copy_from_user (&(((struct in_rtmsg *)p)->rtmsg_rtt), &((struct in_rtmsg32 *)A(arg))->rtmsg_rtt,
+ 2 * sizeof(short) + 4 + sizeof(int)))
+ return -EFAULT;
+ break;
+ case RTMSG_NEWDEVICE:
+ case RTMSG_DELDEVICE:
+ if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_ifmsg))
+ return -EINVAL;
+ nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_ifmsg);
+ if (copy_from_user (p, (struct in_ifmsg32 *)A(arg),
+ sizeof(struct sockaddr) + 2*sizeof(struct in_addr) + sizeof(unsigned)) ||
+ __get_user (((struct in_ifmsg *)p)->ifmsg_mtu, &((struct in_ifmsg32 *)A(arg))->ifmsg_mtu) ||
+ copy_from_user (&(((struct in_ifmsg *)p)->ifmsg_metric), &((struct in_ifmsg32 *)A(arg))->ifmsg_metric,
+ sizeof(short) + 2 + sizeof(int) + 16))
+ return -EFAULT;
+ break;
+ case RTMSG_CONTROL:
+ if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtctlmsg))
+ return -EINVAL;
+ nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtctlmsg);
+ if (copy_from_user (p, (struct in_rtctlmsg *)A(arg), sizeof(struct in_rtctlmsg)))
+ return -EFAULT;
+ break;
+ }
+ set_fs (KERNEL_DS);
+ ret = sys_ioctl (fd, SIOCRTMSG, (long)&(nn.n));
+ set_fs (old_fs);
+ return ret;
+}
+
+struct hd_geometry32 {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ u32 start;
+};
+
+static inline int hdio_getgeo(unsigned int fd, u32 arg)
+{
+ unsigned long old_fs = get_fs();
+ struct hd_geometry geo;
+ int err;
+
+ set_fs (KERNEL_DS);
+ err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo);
+ set_fs (old_fs);
+ if (!err) {
+ if (copy_to_user ((struct hd_geometry32 *)A(arg), &geo, 4) ||
+ __put_user (geo.start, &(((struct hd_geometry32 *)A(arg))->start)))
+ return -EFAULT;
+ }
+ return err;
+}
+
asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
{
struct file * filp;
@@ -35,16 +382,149 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
error = sys_ioctl (fd, cmd, (unsigned long)arg);
goto out;
}
- error = 0;
+ error = -EFAULT;
switch (cmd) {
- default:
- error = sys_ioctl (fd, cmd, (unsigned long)arg);
- goto out;
+ case SIOCGIFCONF:
+ error = dev_ifconf(fd, arg);
+ goto out;
+
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ case SIOGIFINDEX:
+ case SIOCGIFMAP:
+ case SIOCSIFMAP:
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ error = dev_ifsioc(fd, cmd, arg);
+ goto out;
+
+ case SIOCADDRT:
+ case SIOCDELRT:
+ error = routing_ioctl(fd, cmd, arg);
+ goto out;
+
+ case SIOCRTMSG:
+ error = rtmsg_ioctl(fd, arg);
+ goto out;
+
+ case HDIO_GETGEO:
+ error = hdio_getgeo(fd, arg);
+ goto out;
+
+ case BLKRAGET:
+ case BLKGETSIZE:
+ error = w_long(fd, cmd, arg);
+ goto out;
+
+ /* List here exlicitly which ioctl's are known to have
+ * compatable types passed or none at all...
+ */
+
+ /* Bit T */
+ case TCGETA:
+ case TCSETA:
+ case TCSETAW:
+ case TCSETAF:
+ case TCSBRK:
+ case TCXONC:
+ case TCFLSH:
+ case TCGETS:
+ case TCSETS:
+ case TCSETSW:
+ case TCSETSF:
+ case TIOCLINUX:
+
+ /* Little t */
+ case TIOCGETD:
+ case TIOCSETD:
+ case TIOCEXCL:
+ case TIOCNXCL:
+ case TIOCCONS:
+ case TIOCGSOFTCAR:
+ case TIOCSSOFTCAR:
+ case TIOCSWINSZ:
+ case TIOCGWINSZ:
+ case TIOCMGET:
+ case TIOCMBIC:
+ case TIOCMBIS:
+ case TIOCMSET:
+ case TIOCPKT:
+ case TIOCNOTTY:
+ case TIOCSTI:
+ case TIOCOUTQ:
+ case TIOCSPGRP:
+ case TIOCGPGRP:
+ case TIOCSCTTY:
+
+ /* Little f */
+ case FIOCLEX:
+ case FIONCLEX:
+ case FIOASYNC:
+ case FIONBIO:
+ case FIONREAD: /* This is also TIOCINQ */
+
+ /* 0x12 */
+ case BLKRRPART:
+ case BLKFLSBUF:
+ case BLKRASET:
+
+ /* 0x09 */
+ case REGISTER_DEV:
+ case START_MD:
+ case STOP_MD:
+
+ /* Big K */
+ case PIO_FONT:
+ case GIO_FONT:
+ case KDSIGACCEPT:
+ case KDGETKEYCODE:
+ case KDSETKEYCODE:
+
+ /* Socket level stuff */
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ case SIOCATMARK:
+ case SIOCGSTAMP:
+ case SIOCSIFLINK:
+ case SIOCSIFENCAP:
+ case SIOCGIFENCAP:
+ case SIOCSIFBR:
+ case SIOCGIFBR:
+ case SIOCSARP:
+ case SIOCGARP:
+ case SIOCDARP:
+ case SIOCADDDLCI:
+ case SIOCDELDLCI:
+ error = sys_ioctl (fd, cmd, (unsigned long)arg);
+ goto out;
+ break;
+
+ default:
+ printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
+ (int)fd, (unsigned int)cmd, (unsigned int)arg);
+ error = -EINVAL;
+ goto out;
+ break;
}
out:
- if (error == -EINVAL) {
- printk ("sys32_ioctl on %016lx's %08x returns EINVAL\n", filp->f_op ? (long)filp->f_op->ioctl : 0UL, cmd);
- }
unlock_kernel();
return error;
}