[PATCH] vesafb: Add blanking support
[linux-2.6] / arch / sparc64 / kernel / sys_sunos32.c
1 /* $Id: sys_sunos32.c,v 1.64 2002/02/09 19:49:31 davem Exp $
2  * sys_sunos32.c: SunOS binary compatibility layer on sparc64.
3  *
4  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
5  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
6  *
7  * Based upon preliminary work which is:
8  *
9  * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/types.h>
15 #include <linux/compat.h>
16 #include <linux/mman.h>
17 #include <linux/mm.h>
18 #include <linux/swap.h>
19 #include <linux/fs.h>
20 #include <linux/file.h>
21 #include <linux/resource.h>
22 #include <linux/ipc.h>
23 #include <linux/shm.h>
24 #include <linux/msg.h>
25 #include <linux/sem.h>
26 #include <linux/signal.h>
27 #include <linux/uio.h>
28 #include <linux/utsname.h>
29 #include <linux/major.h>
30 #include <linux/stat.h>
31 #include <linux/slab.h>
32 #include <linux/pagemap.h>
33 #include <linux/errno.h>
34 #include <linux/smp.h>
35 #include <linux/smp_lock.h>
36 #include <linux/syscalls.h>
37
38 #include <asm/uaccess.h>
39 #include <asm/page.h>
40 #include <asm/pgtable.h>
41 #include <asm/pconf.h>
42 #include <asm/idprom.h> /* for gethostid() */
43 #include <asm/unistd.h>
44 #include <asm/system.h>
45
46 /* For the nfs mount emulation */
47 #include <linux/socket.h>
48 #include <linux/in.h>
49 #include <linux/nfs.h>
50 #include <linux/nfs2.h>
51 #include <linux/nfs_mount.h>
52
53 /* for sunos_select */
54 #include <linux/time.h>
55 #include <linux/personality.h>
56
57 /* For SOCKET_I */
58 #include <linux/socket.h>
59 #include <net/sock.h>
60 #include <net/compat.h>
61
62 #define SUNOS_NR_OPEN   256
63
64 asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off)
65 {
66         struct file *file = NULL;
67         unsigned long retval, ret_type;
68
69         if (flags & MAP_NORESERVE) {
70                 static int cnt;
71                 if (cnt++ < 10)
72                         printk("%s:  unimplemented SunOS MAP_NORESERVE mmap() flag\n",
73                                current->comm);
74                 flags &= ~MAP_NORESERVE;
75         }
76         retval = -EBADF;
77         if (!(flags & MAP_ANONYMOUS)) {
78                 struct inode * inode;
79                 if (fd >= SUNOS_NR_OPEN)
80                         goto out;
81                 file = fget(fd);
82                 if (!file)
83                         goto out;
84                 inode = file->f_dentry->d_inode;
85                 if (imajor(inode) == MEM_MAJOR && iminor(inode) == 5) {
86                         flags |= MAP_ANONYMOUS;
87                         fput(file);
88                         file = NULL;
89                 }
90         }
91
92         retval = -EINVAL;
93         if (!(flags & MAP_FIXED))
94                 addr = 0;
95         else if (len > 0xf0000000 || addr > 0xf0000000 - len)
96                 goto out_putf;
97         ret_type = flags & _MAP_NEW;
98         flags &= ~_MAP_NEW;
99
100         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
101         down_write(&current->mm->mmap_sem);
102         retval = do_mmap(file,
103                          (unsigned long) addr, (unsigned long) len,
104                          (unsigned long) prot, (unsigned long) flags,
105                          (unsigned long) off);
106         up_write(&current->mm->mmap_sem);
107         if (!ret_type)
108                 retval = ((retval < 0xf0000000) ? 0 : retval);
109 out_putf:
110         if (file)
111                 fput(file);
112 out:
113         return (u32) retval;
114 }
115
116 asmlinkage int sunos_mctl(u32 addr, u32 len, int function, u32 arg)
117 {
118         return 0;
119 }
120
121 asmlinkage int sunos_brk(u32 baddr)
122 {
123         int freepages, retval = -ENOMEM;
124         unsigned long rlim;
125         unsigned long newbrk, oldbrk, brk = (unsigned long) baddr;
126
127         down_write(&current->mm->mmap_sem);
128         if (brk < current->mm->end_code)
129                 goto out;
130         newbrk = PAGE_ALIGN(brk);
131         oldbrk = PAGE_ALIGN(current->mm->brk);
132         retval = 0;
133         if (oldbrk == newbrk) {
134                 current->mm->brk = brk;
135                 goto out;
136         }
137         /* Always allow shrinking brk. */
138         if (brk <= current->mm->brk) {
139                 current->mm->brk = brk;
140                 do_munmap(current->mm, newbrk, oldbrk-newbrk);
141                 goto out;
142         }
143         /* Check against rlimit and stack.. */
144         retval = -ENOMEM;
145         rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
146         if (rlim >= RLIM_INFINITY)
147                 rlim = ~0;
148         if (brk - current->mm->end_code > rlim)
149                 goto out;
150         /* Check against existing mmap mappings. */
151         if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE))
152                 goto out;
153         /* stupid algorithm to decide if we have enough memory: while
154          * simple, it hopefully works in most obvious cases.. Easy to
155          * fool it, but this should catch most mistakes.
156          */
157         freepages = get_page_cache_size();
158         freepages >>= 1;
159         freepages += nr_free_pages();
160         freepages += nr_swap_pages;
161         freepages -= num_physpages >> 4;
162         freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
163         if (freepages < 0)
164                 goto out;
165         /* Ok, we have probably got enough memory - let it rip. */
166         current->mm->brk = brk;
167         do_brk(oldbrk, newbrk-oldbrk);
168         retval = 0;
169 out:
170         up_write(&current->mm->mmap_sem);
171         return retval;
172 }
173
174 asmlinkage u32 sunos_sbrk(int increment)
175 {
176         int error, oldbrk;
177
178         /* This should do it hopefully... */
179         oldbrk = (int)current->mm->brk;
180         error = sunos_brk(((int) current->mm->brk) + increment);
181         if (!error)
182                 error = oldbrk;
183         return error;
184 }
185
186 asmlinkage u32 sunos_sstk(int increment)
187 {
188         printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n",
189                current->comm, increment);
190
191         return (u32)-1;
192 }
193
194 /* Give hints to the kernel as to what paging strategy to use...
195  * Completely bogus, don't remind me.
196  */
197 #define VA_NORMAL     0 /* Normal vm usage expected */
198 #define VA_ABNORMAL   1 /* Abnormal/random vm usage probable */
199 #define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */
200 #define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */
201 static char *vstrings[] = {
202         "VA_NORMAL",
203         "VA_ABNORMAL",
204         "VA_SEQUENTIAL",
205         "VA_INVALIDATE",
206 };
207
208 asmlinkage void sunos_vadvise(u32 strategy)
209 {
210         static int count;
211
212         /* I wanna see who uses this... */
213         if (count++ < 5)
214                 printk("%s: Advises us to use %s paging strategy\n",
215                        current->comm,
216                        strategy <= 3 ? vstrings[strategy] : "BOGUS");
217 }
218
219 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
220  * resource limit and is for backwards compatibility with older sunos
221  * revs.
222  */
223 asmlinkage int sunos_getdtablesize(void)
224 {
225         return SUNOS_NR_OPEN;
226 }
227
228
229 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
230
231 asmlinkage u32 sunos_sigblock(u32 blk_mask)
232 {
233         u32 old;
234
235         spin_lock_irq(&current->sighand->siglock);
236         old = (u32) current->blocked.sig[0];
237         current->blocked.sig[0] |= (blk_mask & _BLOCKABLE);
238         recalc_sigpending();
239         spin_unlock_irq(&current->sighand->siglock);
240         return old;
241 }
242
243 asmlinkage u32 sunos_sigsetmask(u32 newmask)
244 {
245         u32 retval;
246
247         spin_lock_irq(&current->sighand->siglock);
248         retval = (u32) current->blocked.sig[0];
249         current->blocked.sig[0] = (newmask & _BLOCKABLE);
250         recalc_sigpending();
251         spin_unlock_irq(&current->sighand->siglock);
252         return retval;
253 }
254
255 /* SunOS getdents is very similar to the newer Linux (iBCS2 compliant)    */
256 /* getdents system call, the format of the structure just has a different */
257 /* layout (d_off+d_ino instead of d_ino+d_off) */
258 struct sunos_dirent {
259     s32         d_off;
260     u32         d_ino;
261     u16         d_reclen;
262     u16         d_namlen;
263     char        d_name[1];
264 };
265
266 struct sunos_dirent_callback {
267     struct sunos_dirent __user *curr;
268     struct sunos_dirent __user *previous;
269     int count;
270     int error;
271 };
272
273 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
274 #define ROUND_UP(x) (((x)+sizeof(s32)-1) & ~(sizeof(s32)-1))
275
276 static int sunos_filldir(void * __buf, const char * name, int namlen,
277                          loff_t offset, ino_t ino, unsigned int d_type)
278 {
279         struct sunos_dirent __user *dirent;
280         struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf;
281         int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
282
283         buf->error = -EINVAL;   /* only used if we fail.. */
284         if (reclen > buf->count)
285                 return -EINVAL;
286         dirent = buf->previous;
287         if (dirent)
288                 put_user(offset, &dirent->d_off);
289         dirent = buf->curr;
290         buf->previous = dirent;
291         put_user(ino, &dirent->d_ino);
292         put_user(namlen, &dirent->d_namlen);
293         put_user(reclen, &dirent->d_reclen);
294         if (copy_to_user(dirent->d_name, name, namlen))
295                 return -EFAULT;
296         put_user(0, dirent->d_name + namlen);
297         dirent = (void __user *) dirent + reclen;
298         buf->curr = dirent;
299         buf->count -= reclen;
300         return 0;
301 }
302
303 asmlinkage int sunos_getdents(unsigned int fd, void __user *dirent, int cnt)
304 {
305         struct file * file;
306         struct sunos_dirent __user *lastdirent;
307         struct sunos_dirent_callback buf;
308         int error = -EBADF;
309
310         if (fd >= SUNOS_NR_OPEN)
311                 goto out;
312
313         file = fget(fd);
314         if (!file)
315                 goto out;
316
317         error = -EINVAL;
318         if (cnt < (sizeof(struct sunos_dirent) + 255))
319                 goto out_putf;
320
321         buf.curr = (struct sunos_dirent __user *) dirent;
322         buf.previous = NULL;
323         buf.count = cnt;
324         buf.error = 0;
325
326         error = vfs_readdir(file, sunos_filldir, &buf);
327         if (error < 0)
328                 goto out_putf;
329
330         lastdirent = buf.previous;
331         error = buf.error;
332         if (lastdirent) {
333                 put_user(file->f_pos, &lastdirent->d_off);
334                 error = cnt - buf.count;
335         }
336
337 out_putf:
338         fput(file);
339 out:
340         return error;
341 }
342
343 /* Old sunos getdirentries, severely broken compatibility stuff here. */
344 struct sunos_direntry {
345     u32         d_ino;
346     u16         d_reclen;
347     u16         d_namlen;
348     char        d_name[1];
349 };
350
351 struct sunos_direntry_callback {
352     struct sunos_direntry __user *curr;
353     struct sunos_direntry __user *previous;
354     int count;
355     int error;
356 };
357
358 static int sunos_filldirentry(void * __buf, const char * name, int namlen,
359                               loff_t offset, ino_t ino, unsigned int d_type)
360 {
361         struct sunos_direntry __user *dirent;
362         struct sunos_direntry_callback * buf =
363                 (struct sunos_direntry_callback *) __buf;
364         int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
365
366         buf->error = -EINVAL;   /* only used if we fail.. */
367         if (reclen > buf->count)
368                 return -EINVAL;
369         dirent = buf->previous;
370         dirent = buf->curr;
371         buf->previous = dirent;
372         put_user(ino, &dirent->d_ino);
373         put_user(namlen, &dirent->d_namlen);
374         put_user(reclen, &dirent->d_reclen);
375         if (copy_to_user(dirent->d_name, name, namlen))
376                 return -EFAULT;
377         put_user(0, dirent->d_name + namlen);
378         dirent = (void __user *) dirent + reclen;
379         buf->curr = dirent;
380         buf->count -= reclen;
381         return 0;
382 }
383
384 asmlinkage int sunos_getdirentries(unsigned int fd,
385                                    void __user *dirent,
386                                    int cnt,
387                                    unsigned int __user *basep)
388 {
389         struct file * file;
390         struct sunos_direntry __user *lastdirent;
391         int error = -EBADF;
392         struct sunos_direntry_callback buf;
393
394         if (fd >= SUNOS_NR_OPEN)
395                 goto out;
396
397         file = fget(fd);
398         if (!file)
399                 goto out;
400
401         error = -EINVAL;
402         if (cnt < (sizeof(struct sunos_direntry) + 255))
403                 goto out_putf;
404
405         buf.curr = (struct sunos_direntry __user *) dirent;
406         buf.previous = NULL;
407         buf.count = cnt;
408         buf.error = 0;
409
410         error = vfs_readdir(file, sunos_filldirentry, &buf);
411         if (error < 0)
412                 goto out_putf;
413
414         lastdirent = buf.previous;
415         error = buf.error;
416         if (lastdirent) {
417                 put_user(file->f_pos, basep);
418                 error = cnt - buf.count;
419         }
420
421 out_putf:
422         fput(file);
423 out:
424         return error;
425 }
426
427 struct sunos_utsname {
428         char sname[9];
429         char nname[9];
430         char nnext[56];
431         char rel[9];
432         char ver[9];
433         char mach[9];
434 };
435
436 asmlinkage int sunos_uname(struct sunos_utsname __user *name)
437 {
438         int ret;
439
440         down_read(&uts_sem);
441         ret = copy_to_user(&name->sname[0], &system_utsname.sysname[0],
442                            sizeof(name->sname) - 1);
443         ret |= copy_to_user(&name->nname[0], &system_utsname.nodename[0],
444                             sizeof(name->nname) - 1);
445         ret |= put_user('\0', &name->nname[8]);
446         ret |= copy_to_user(&name->rel[0], &system_utsname.release[0],
447                             sizeof(name->rel) - 1);
448         ret |= copy_to_user(&name->ver[0], &system_utsname.version[0],
449                             sizeof(name->ver) - 1);
450         ret |= copy_to_user(&name->mach[0], &system_utsname.machine[0],
451                             sizeof(name->mach) - 1);
452         up_read(&uts_sem);
453         return (ret ? -EFAULT : 0);
454 }
455
456 asmlinkage int sunos_nosys(void)
457 {
458         struct pt_regs *regs;
459         siginfo_t info;
460         static int cnt;
461
462         regs = current_thread_info()->kregs;
463         if (test_thread_flag(TIF_32BIT)) {
464                 regs->tpc &= 0xffffffff;
465                 regs->tnpc &= 0xffffffff;
466         }
467         info.si_signo = SIGSYS;
468         info.si_errno = 0;
469         info.si_code = __SI_FAULT|0x100;
470         info.si_addr = (void __user *)regs->tpc;
471         info.si_trapno = regs->u_regs[UREG_G1];
472         send_sig_info(SIGSYS, &info, current);
473         if (cnt++ < 4) {
474                 printk("Process makes ni_syscall number %d, register dump:\n",
475                        (int) regs->u_regs[UREG_G1]);
476                 show_regs(regs);
477         }
478         return -ENOSYS;
479 }
480
481 /* This is not a real and complete implementation yet, just to keep
482  * the easy SunOS binaries happy.
483  */
484 asmlinkage int sunos_fpathconf(int fd, int name)
485 {
486         int ret;
487
488         switch(name) {
489         case _PCONF_LINK:
490                 ret = LINK_MAX;
491                 break;
492         case _PCONF_CANON:
493                 ret = MAX_CANON;
494                 break;
495         case _PCONF_INPUT:
496                 ret = MAX_INPUT;
497                 break;
498         case _PCONF_NAME:
499                 ret = NAME_MAX;
500                 break;
501         case _PCONF_PATH:
502                 ret = PATH_MAX;
503                 break;
504         case _PCONF_PIPE:
505                 ret = PIPE_BUF;
506                 break;
507         case _PCONF_CHRESTRICT:         /* XXX Investigate XXX */
508                 ret = 1;
509                 break;
510         case _PCONF_NOTRUNC:            /* XXX Investigate XXX */
511         case _PCONF_VDISABLE:
512                 ret = 0;
513                 break;
514         default:
515                 ret = -EINVAL;
516                 break;
517         }
518         return ret;
519 }
520
521 asmlinkage int sunos_pathconf(u32 u_path, int name)
522 {
523         int ret;
524
525         ret = sunos_fpathconf(0, name); /* XXX cheese XXX */
526         return ret;
527 }
528
529 asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x)
530 {
531         int ret;
532
533         /* SunOS binaries expect that select won't change the tvp contents */
534         ret = compat_sys_select(width, compat_ptr(inp), compat_ptr(outp),
535                                 compat_ptr(exp), compat_ptr(tvp_x));
536         if (ret == -EINTR && tvp_x) {
537                 struct compat_timeval __user *tvp = compat_ptr(tvp_x);
538                 time_t sec, usec;
539
540                 __get_user(sec, &tvp->tv_sec);
541                 __get_user(usec, &tvp->tv_usec);
542                 if (sec == 0 && usec == 0)
543                         ret = 0;
544         }
545         return ret;
546 }
547
548 asmlinkage void sunos_nop(void)
549 {
550         return;
551 }
552
553 #if 0 /* This code doesn't translate user pointers correctly,
554        * disable for now. -DaveM
555        */
556
557 /* XXXXXXXXXX SunOS mount/umount. XXXXXXXXXXX */
558 #define SMNT_RDONLY       1
559 #define SMNT_NOSUID       2
560 #define SMNT_NEWTYPE      4
561 #define SMNT_GRPID        8
562 #define SMNT_REMOUNT      16
563 #define SMNT_NOSUB        32
564 #define SMNT_MULTI        64
565 #define SMNT_SYS5         128
566
567 struct sunos_fh_t {
568         char fh_data [NFS_FHSIZE];
569 };
570
571 struct sunos_nfs_mount_args {
572         struct sockaddr_in  *addr; /* file server address */
573         struct nfs_fh *fh;     /* File handle to be mounted */
574         int        flags;      /* flags */
575         int        wsize;      /* write size in bytes */
576         int        rsize;      /* read size in bytes */
577         int        timeo;      /* initial timeout in .1 secs */
578         int        retrans;    /* times to retry send */
579         char       *hostname;  /* server's hostname */
580         int        acregmin;   /* attr cache file min secs */
581         int        acregmax;   /* attr cache file max secs */
582         int        acdirmin;   /* attr cache dir min secs */
583         int        acdirmax;   /* attr cache dir max secs */
584         char       *netname;   /* server's netname */
585 };
586
587
588 /* Bind the socket on a local reserved port and connect it to the
589  * remote server.  This on Linux/i386 is done by the mount program,
590  * not by the kernel. 
591  */
592 /* XXXXXXXXXXXXXXXXXXXX */
593 static int
594 sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
595 {
596         struct sockaddr_in local;
597         struct sockaddr_in server;
598         int    try_port;
599         int    ret;
600         struct socket *socket;
601         struct inode  *inode;
602         struct file   *file;
603
604         file = fget(fd);
605         if (!file)
606                 return 0;
607
608         inode = file->f_dentry->d_inode;
609
610         socket = SOCKET_I(inode);
611         local.sin_family = AF_INET;
612         local.sin_addr.s_addr = INADDR_ANY;
613
614         /* IPPORT_RESERVED = 1024, can't find the definition in the kernel */
615         try_port = 1024;
616         do {
617                 local.sin_port = htons (--try_port);
618                 ret = socket->ops->bind(socket, (struct sockaddr*)&local,
619                                         sizeof(local));
620         } while (ret && try_port > (1024 / 2));
621
622         if (ret) {
623                 fput(file);
624                 return 0;
625         }
626
627         server.sin_family = AF_INET;
628         server.sin_addr = addr->sin_addr;
629         server.sin_port = NFS_PORT;
630
631         /* Call sys_connect */
632         ret = socket->ops->connect (socket, (struct sockaddr *) &server,
633                                     sizeof (server), file->f_flags);
634         fput(file);
635         if (ret < 0)
636                 return 0;
637         return 1;
638 }
639
640 /* XXXXXXXXXXXXXXXXXXXX */
641 static int get_default (int value, int def_value)
642 {
643     if (value)
644         return value;
645     else
646         return def_value;
647 }
648
649 /* XXXXXXXXXXXXXXXXXXXX */
650 static int sunos_nfs_mount(char *dir_name, int linux_flags, void __user *data)
651 {
652         int  server_fd, err;
653         char *the_name, *mount_page;
654         struct nfs_mount_data linux_nfs_mount;
655         struct sunos_nfs_mount_args sunos_mount;
656
657         /* Ok, here comes the fun part: Linux's nfs mount needs a
658          * socket connection to the server, but SunOS mount does not
659          * require this, so we use the information on the destination
660          * address to create a socket and bind it to a reserved
661          * port on this system
662          */
663         if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount)))
664                 return -EFAULT;
665
666         server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
667         if (server_fd < 0)
668                 return -ENXIO;
669
670         if (copy_from_user(&linux_nfs_mount.addr, sunos_mount.addr,
671                            sizeof(*sunos_mount.addr)) ||
672             copy_from_user(&linux_nfs_mount.root, sunos_mount.fh,
673                            sizeof(*sunos_mount.fh))) {
674                 sys_close (server_fd);
675                 return -EFAULT;
676         }
677
678         if (!sunos_nfs_get_server_fd (server_fd, &linux_nfs_mount.addr)){
679                 sys_close (server_fd);
680                 return -ENXIO;
681         }
682
683         /* Now, bind it to a locally reserved port */
684         linux_nfs_mount.version  = NFS_MOUNT_VERSION;
685         linux_nfs_mount.flags    = sunos_mount.flags;
686         linux_nfs_mount.fd       = server_fd;
687         
688         linux_nfs_mount.rsize    = get_default (sunos_mount.rsize, 8192);
689         linux_nfs_mount.wsize    = get_default (sunos_mount.wsize, 8192);
690         linux_nfs_mount.timeo    = get_default (sunos_mount.timeo, 10);
691         linux_nfs_mount.retrans  = sunos_mount.retrans;
692         
693         linux_nfs_mount.acregmin = sunos_mount.acregmin;
694         linux_nfs_mount.acregmax = sunos_mount.acregmax;
695         linux_nfs_mount.acdirmin = sunos_mount.acdirmin;
696         linux_nfs_mount.acdirmax = sunos_mount.acdirmax;
697
698         the_name = getname(sunos_mount.hostname);
699         if (IS_ERR(the_name))
700                 return PTR_ERR(the_name);
701
702         strlcpy(linux_nfs_mount.hostname, the_name,
703                 sizeof(linux_nfs_mount.hostname));
704         putname (the_name);
705         
706         mount_page = (char *) get_zeroed_page(GFP_KERNEL);
707         if (!mount_page)
708                 return -ENOMEM;
709
710         memcpy(mount_page, &linux_nfs_mount, sizeof(linux_nfs_mount));
711
712         err = do_mount("", dir_name, "nfs", linux_flags, mount_page);
713
714         free_page((unsigned long) mount_page);
715         return err;
716 }
717
718 /* XXXXXXXXXXXXXXXXXXXX */
719 asmlinkage int
720 sunos_mount(char *type, char *dir, int flags, void *data)
721 {
722         int linux_flags = 0;
723         int ret = -EINVAL;
724         char *dev_fname = 0;
725         char *dir_page, *type_page;
726
727         if (!capable (CAP_SYS_ADMIN))
728                 return -EPERM;
729
730         /* We don't handle the integer fs type */
731         if ((flags & SMNT_NEWTYPE) == 0)
732                 goto out;
733
734         /* Do not allow for those flags we don't support */
735         if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5))
736                 goto out;
737
738         if (flags & SMNT_REMOUNT)
739                 linux_flags |= MS_REMOUNT;
740         if (flags & SMNT_RDONLY)
741                 linux_flags |= MS_RDONLY;
742         if (flags & SMNT_NOSUID)
743                 linux_flags |= MS_NOSUID;
744
745         dir_page = getname(dir);
746         ret = PTR_ERR(dir_page);
747         if (IS_ERR(dir_page))
748                 goto out;
749
750         type_page = getname(type);
751         ret = PTR_ERR(type_page);
752         if (IS_ERR(type_page))
753                 goto out1;
754
755         if (strcmp(type_page, "ext2") == 0) {
756                 dev_fname = getname(data);
757         } else if (strcmp(type_page, "iso9660") == 0) {
758                 dev_fname = getname(data);
759         } else if (strcmp(type_page, "minix") == 0) {
760                 dev_fname = getname(data);
761         } else if (strcmp(type_page, "nfs") == 0) {
762                 ret = sunos_nfs_mount (dir_page, flags, data);
763                 goto out2;
764         } else if (strcmp(type_page, "ufs") == 0) {
765                 printk("Warning: UFS filesystem mounts unsupported.\n");
766                 ret = -ENODEV;
767                 goto out2;
768         } else if (strcmp(type_page, "proc")) {
769                 ret = -ENODEV;
770                 goto out2;
771         }
772         ret = PTR_ERR(dev_fname);
773         if (IS_ERR(dev_fname))
774                 goto out2;
775         lock_kernel();
776         ret = do_mount(dev_fname, dir_page, type_page, linux_flags, NULL);
777         unlock_kernel();
778         if (dev_fname)
779                 putname(dev_fname);
780 out2:
781         putname(type_page);
782 out1:
783         putname(dir_page);
784 out:
785         return ret;
786 }
787 #endif
788
789 asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid)
790 {
791         int ret;
792
793         /* So stupid... */
794         if ((!pid || pid == current->pid) &&
795             !pgid) {
796                 sys_setsid();
797                 ret = 0;
798         } else {
799                 ret = sys_setpgid(pid, pgid);
800         }
801         return ret;
802 }
803
804 /* So stupid... */
805 extern long compat_sys_wait4(compat_pid_t, compat_uint_t __user *, int,
806                              struct compat_rusage __user *);
807
808 asmlinkage int sunos_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, struct compat_rusage __user *ru)
809 {
810         int ret;
811
812         ret = compat_sys_wait4((pid ? pid : ((compat_pid_t)-1)),
813                                stat_addr, options, ru);
814         return ret;
815 }
816
817 extern int kill_pg(int, int, int);
818 asmlinkage int sunos_killpg(int pgrp, int sig)
819 {
820         return kill_pg(pgrp, sig, 0);
821 }
822
823 asmlinkage int sunos_audit(void)
824 {
825         printk ("sys_audit\n");
826         return -1;
827 }
828
829 asmlinkage u32 sunos_gethostid(void)
830 {
831         u32 ret;
832
833         ret = (((u32)idprom->id_machtype << 24) | ((u32)idprom->id_sernum));
834
835         return ret;
836 }
837
838 /* sysconf options, for SunOS compatibility */
839 #define   _SC_ARG_MAX             1
840 #define   _SC_CHILD_MAX           2
841 #define   _SC_CLK_TCK             3
842 #define   _SC_NGROUPS_MAX         4
843 #define   _SC_OPEN_MAX            5
844 #define   _SC_JOB_CONTROL         6
845 #define   _SC_SAVED_IDS           7
846 #define   _SC_VERSION             8
847
848 asmlinkage s32 sunos_sysconf (int name)
849 {
850         s32 ret;
851
852         switch (name){
853         case _SC_ARG_MAX:
854                 ret = ARG_MAX;
855                 break;
856         case _SC_CHILD_MAX:
857                 ret = CHILD_MAX;
858                 break;
859         case _SC_CLK_TCK:
860                 ret = HZ;
861                 break;
862         case _SC_NGROUPS_MAX:
863                 ret = NGROUPS_MAX;
864                 break;
865         case _SC_OPEN_MAX:
866                 ret = OPEN_MAX;
867                 break;
868         case _SC_JOB_CONTROL:
869                 ret = 1;        /* yes, we do support job control */
870                 break;
871         case _SC_SAVED_IDS:
872                 ret = 1;        /* yes, we do support saved uids  */
873                 break;
874         case _SC_VERSION:
875                 /* mhm, POSIX_VERSION is in /usr/include/unistd.h
876                  * should it go on /usr/include/linux?
877                  */
878                 ret = 199009;
879                 break;
880         default:
881                 ret = -1;
882                 break;
883         };
884         return ret;
885 }
886
887 asmlinkage int sunos_semsys(int op, u32 arg1, u32 arg2, u32 arg3, void __user *ptr)
888 {
889         union semun arg4;
890         int ret;
891
892         switch (op) {
893         case 0:
894                 /* Most arguments match on a 1:1 basis but cmd doesn't */
895                 switch(arg3) {
896                 case 4:
897                         arg3=GETPID; break;
898                 case 5:
899                         arg3=GETVAL; break;
900                 case 6:
901                         arg3=GETALL; break;
902                 case 3:
903                         arg3=GETNCNT; break;
904                 case 7:
905                         arg3=GETZCNT; break;
906                 case 8:
907                         arg3=SETVAL; break;
908                 case 9:
909                         arg3=SETALL; break;
910                 }
911                 /* sys_semctl(): */
912                 /* value to modify semaphore to */
913                 arg4.__pad = ptr;
914                 ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4);
915                 break;
916         case 1:
917                 /* sys_semget(): */
918                 ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3);
919                 break;
920         case 2:
921                 /* sys_semop(): */
922                 ret = sys_semop((int)arg1, (struct sembuf __user *)(unsigned long)arg2,
923                                 (unsigned int) arg3);
924                 break;
925         default:
926                 ret = -EINVAL;
927                 break;
928         };
929         return ret;
930 }
931
932 struct msgbuf32 {
933         s32 mtype;
934         char mtext[1];
935 };
936
937 struct ipc_perm32
938 {
939         key_t             key;
940         compat_uid_t  uid;
941         compat_gid_t  gid;
942         compat_uid_t  cuid;
943         compat_gid_t  cgid;
944         compat_mode_t mode;
945         unsigned short  seq;
946 };
947
948 struct msqid_ds32
949 {
950         struct ipc_perm32 msg_perm;
951         u32 msg_first;
952         u32 msg_last;
953         compat_time_t msg_stime;
954         compat_time_t msg_rtime;
955         compat_time_t msg_ctime;
956         u32 wwait;
957         u32 rwait;
958         unsigned short msg_cbytes;
959         unsigned short msg_qnum;  
960         unsigned short msg_qbytes;
961         compat_ipc_pid_t msg_lspid;
962         compat_ipc_pid_t msg_lrpid;
963 };
964
965 static inline int sunos_msqid_get(struct msqid_ds32 __user *user,
966                                   struct msqid_ds *kern)
967 {
968         if (get_user(kern->msg_perm.key, &user->msg_perm.key)           ||
969             __get_user(kern->msg_perm.uid, &user->msg_perm.uid)         ||
970             __get_user(kern->msg_perm.gid, &user->msg_perm.gid)         ||
971             __get_user(kern->msg_perm.cuid, &user->msg_perm.cuid)       ||
972             __get_user(kern->msg_perm.cgid, &user->msg_perm.cgid)       ||
973             __get_user(kern->msg_stime, &user->msg_stime)               ||
974             __get_user(kern->msg_rtime, &user->msg_rtime)               ||
975             __get_user(kern->msg_ctime, &user->msg_ctime)               ||
976             __get_user(kern->msg_ctime, &user->msg_cbytes)              ||
977             __get_user(kern->msg_ctime, &user->msg_qnum)                ||
978             __get_user(kern->msg_ctime, &user->msg_qbytes)              ||
979             __get_user(kern->msg_ctime, &user->msg_lspid)               ||
980             __get_user(kern->msg_ctime, &user->msg_lrpid))
981                 return -EFAULT;
982         return 0;
983 }
984
985 static inline int sunos_msqid_put(struct msqid_ds32 __user *user,
986                                   struct msqid_ds *kern)
987 {
988         if (put_user(kern->msg_perm.key, &user->msg_perm.key)           ||
989             __put_user(kern->msg_perm.uid, &user->msg_perm.uid)         ||
990             __put_user(kern->msg_perm.gid, &user->msg_perm.gid)         ||
991             __put_user(kern->msg_perm.cuid, &user->msg_perm.cuid)       ||
992             __put_user(kern->msg_perm.cgid, &user->msg_perm.cgid)       ||
993             __put_user(kern->msg_stime, &user->msg_stime)               ||
994             __put_user(kern->msg_rtime, &user->msg_rtime)               ||
995             __put_user(kern->msg_ctime, &user->msg_ctime)               ||
996             __put_user(kern->msg_ctime, &user->msg_cbytes)              ||
997             __put_user(kern->msg_ctime, &user->msg_qnum)                ||
998             __put_user(kern->msg_ctime, &user->msg_qbytes)              ||
999             __put_user(kern->msg_ctime, &user->msg_lspid)               ||
1000             __put_user(kern->msg_ctime, &user->msg_lrpid))
1001                 return -EFAULT;
1002         return 0;
1003 }
1004
1005 static inline int sunos_msgbuf_get(struct msgbuf32 __user *user, struct msgbuf *kern, int len)
1006 {
1007         if (get_user(kern->mtype, &user->mtype) ||
1008             __copy_from_user(kern->mtext, &user->mtext, len))
1009                 return -EFAULT;
1010         return 0;
1011 }
1012
1013 static inline int sunos_msgbuf_put(struct msgbuf32 __user *user, struct msgbuf *kern, int len)
1014 {
1015         if (put_user(kern->mtype, &user->mtype) ||
1016             __copy_to_user(user->mtext, kern->mtext, len))
1017                 return -EFAULT;
1018         return 0;
1019 }
1020
1021 asmlinkage int sunos_msgsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
1022 {
1023         struct sparc_stackf32 __user *sp;
1024         struct msqid_ds kds;
1025         struct msgbuf *kmbuf;
1026         mm_segment_t old_fs = get_fs();
1027         u32 arg5;
1028         int rval;
1029
1030         switch(op) {
1031         case 0:
1032                 rval = sys_msgget((key_t)arg1, (int)arg2);
1033                 break;
1034         case 1:
1035                 if (!sunos_msqid_get((struct msqid_ds32 __user *)(unsigned long)arg3, &kds)) {
1036                         set_fs(KERNEL_DS);
1037                         rval = sys_msgctl((int)arg1, (int)arg2,
1038                                           (struct msqid_ds __user *)(unsigned long)arg3);
1039                         set_fs(old_fs);
1040                         if (!rval)
1041                                 rval = sunos_msqid_put((struct msqid_ds32 __user *)(unsigned long)arg3,
1042                                                        &kds);
1043                 } else
1044                         rval = -EFAULT;
1045                 break;
1046         case 2:
1047                 rval = -EFAULT;
1048                 kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3,
1049                                                  GFP_KERNEL);
1050                 if (!kmbuf)
1051                         break;
1052                 sp = (struct sparc_stackf32 __user *)
1053                         (current_thread_info()->kregs->u_regs[UREG_FP] & 0xffffffffUL);
1054                 if (get_user(arg5, &sp->xxargs[0])) {
1055                         rval = -EFAULT;
1056                         kfree(kmbuf);
1057                         break;
1058                 }
1059                 set_fs(KERNEL_DS);
1060                 rval = sys_msgrcv((int)arg1, (struct msgbuf __user *) kmbuf,
1061                                   (size_t)arg3,
1062                                   (long)arg4, (int)arg5);
1063                 set_fs(old_fs);
1064                 if (!rval)
1065                         rval = sunos_msgbuf_put((struct msgbuf32 __user *)(unsigned long)arg2,
1066                                                 kmbuf, arg3);
1067                 kfree(kmbuf);
1068                 break;
1069         case 3:
1070                 rval = -EFAULT;
1071                 kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3,
1072                                                  GFP_KERNEL);
1073                 if (!kmbuf || sunos_msgbuf_get((struct msgbuf32 __user *)(unsigned long)arg2,
1074                                                kmbuf, arg3))
1075                         break;
1076                 set_fs(KERNEL_DS);
1077                 rval = sys_msgsnd((int)arg1, (struct msgbuf __user *) kmbuf,
1078                                   (size_t)arg3, (int)arg4);
1079                 set_fs(old_fs);
1080                 kfree(kmbuf);
1081                 break;
1082         default:
1083                 rval = -EINVAL;
1084                 break;
1085         }
1086         return rval;
1087 }
1088
1089 struct shmid_ds32 {
1090         struct ipc_perm32       shm_perm;
1091         int                     shm_segsz;
1092         compat_time_t         shm_atime;
1093         compat_time_t         shm_dtime;
1094         compat_time_t         shm_ctime;
1095         compat_ipc_pid_t    shm_cpid; 
1096         compat_ipc_pid_t    shm_lpid; 
1097         unsigned short          shm_nattch;
1098 };
1099                                                         
1100 static inline int sunos_shmid_get(struct shmid_ds32 __user *user,
1101                                   struct shmid_ds *kern)
1102 {
1103         if (get_user(kern->shm_perm.key, &user->shm_perm.key)           ||
1104             __get_user(kern->shm_perm.uid, &user->shm_perm.uid)         ||
1105             __get_user(kern->shm_perm.gid, &user->shm_perm.gid)         ||
1106             __get_user(kern->shm_perm.cuid, &user->shm_perm.cuid)       ||
1107             __get_user(kern->shm_perm.cgid, &user->shm_perm.cgid)       ||
1108             __get_user(kern->shm_segsz, &user->shm_segsz)               ||
1109             __get_user(kern->shm_atime, &user->shm_atime)               ||
1110             __get_user(kern->shm_dtime, &user->shm_dtime)               ||
1111             __get_user(kern->shm_ctime, &user->shm_ctime)               ||
1112             __get_user(kern->shm_cpid, &user->shm_cpid)                 ||
1113             __get_user(kern->shm_lpid, &user->shm_lpid)                 ||
1114             __get_user(kern->shm_nattch, &user->shm_nattch))
1115                 return -EFAULT;
1116         return 0;
1117 }
1118
1119 static inline int sunos_shmid_put(struct shmid_ds32 __user *user,
1120                                   struct shmid_ds *kern)
1121 {
1122         if (put_user(kern->shm_perm.key, &user->shm_perm.key)           ||
1123             __put_user(kern->shm_perm.uid, &user->shm_perm.uid)         ||
1124             __put_user(kern->shm_perm.gid, &user->shm_perm.gid)         ||
1125             __put_user(kern->shm_perm.cuid, &user->shm_perm.cuid)       ||
1126             __put_user(kern->shm_perm.cgid, &user->shm_perm.cgid)       ||
1127             __put_user(kern->shm_segsz, &user->shm_segsz)               ||
1128             __put_user(kern->shm_atime, &user->shm_atime)               ||
1129             __put_user(kern->shm_dtime, &user->shm_dtime)               ||
1130             __put_user(kern->shm_ctime, &user->shm_ctime)               ||
1131             __put_user(kern->shm_cpid, &user->shm_cpid)                 ||
1132             __put_user(kern->shm_lpid, &user->shm_lpid)                 ||
1133             __put_user(kern->shm_nattch, &user->shm_nattch))
1134                 return -EFAULT;
1135         return 0;
1136 }
1137
1138 asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3)
1139 {
1140         struct shmid_ds ksds;
1141         unsigned long raddr;
1142         mm_segment_t old_fs = get_fs();
1143         int rval;
1144
1145         switch(op) {
1146         case 0:
1147                 /* do_shmat(): attach a shared memory area */
1148                 rval = do_shmat((int)arg1,(char __user *)(unsigned long)arg2,(int)arg3,&raddr);
1149                 if (!rval)
1150                         rval = (int) raddr;
1151                 break;
1152         case 1:
1153                 /* sys_shmctl(): modify shared memory area attr. */
1154                 if (!sunos_shmid_get((struct shmid_ds32 __user *)(unsigned long)arg3, &ksds)) {
1155                         set_fs(KERNEL_DS);
1156                         rval = sys_shmctl((int) arg1,(int) arg2,
1157                                           (struct shmid_ds __user *) &ksds);
1158                         set_fs(old_fs);
1159                         if (!rval)
1160                                 rval = sunos_shmid_put((struct shmid_ds32 __user *)(unsigned long)arg3,
1161                                                        &ksds);
1162                 } else
1163                         rval = -EFAULT;
1164                 break;
1165         case 2:
1166                 /* sys_shmdt(): detach a shared memory area */
1167                 rval = sys_shmdt((char __user *)(unsigned long)arg1);
1168                 break;
1169         case 3:
1170                 /* sys_shmget(): get a shared memory area */
1171                 rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3);
1172                 break;
1173         default:
1174                 rval = -EINVAL;
1175                 break;
1176         };
1177         return rval;
1178 }
1179
1180 extern asmlinkage long sparc32_open(const char __user * filename, int flags, int mode);
1181
1182 asmlinkage int sunos_open(u32 fname, int flags, int mode)
1183 {
1184         const char __user *filename = compat_ptr(fname);
1185
1186         return sparc32_open(filename, flags, mode);
1187 }
1188
1189 #define SUNOS_EWOULDBLOCK 35
1190
1191 /* see the sunos man page read(2v) for an explanation
1192    of this garbage. We use O_NDELAY to mark
1193    file descriptors that have been set non-blocking 
1194    using 4.2BSD style calls. (tridge) */
1195
1196 static inline int check_nonblock(int ret, int fd)
1197 {
1198         if (ret == -EAGAIN) {
1199                 struct file * file = fget(fd);
1200                 if (file) {
1201                         if (file->f_flags & O_NDELAY)
1202                                 ret = -SUNOS_EWOULDBLOCK;
1203                         fput(file);
1204                 }
1205         }
1206         return ret;
1207 }
1208
1209 asmlinkage int sunos_read(unsigned int fd, char __user *buf, u32 count)
1210 {
1211         int ret;
1212
1213         ret = check_nonblock(sys_read(fd, buf, count), fd);
1214         return ret;
1215 }
1216
1217 asmlinkage int sunos_readv(u32 fd, void __user *vector, s32 count)
1218 {
1219         int ret;
1220
1221         ret = check_nonblock(compat_sys_readv(fd, vector, count), fd);
1222         return ret;
1223 }
1224
1225 asmlinkage int sunos_write(unsigned int fd, char __user *buf, u32 count)
1226 {
1227         int ret;
1228
1229         ret = check_nonblock(sys_write(fd, buf, count), fd);
1230         return ret;
1231 }
1232
1233 asmlinkage int sunos_writev(u32 fd, void __user *vector, s32 count)
1234 {
1235         int ret;
1236
1237         ret = check_nonblock(compat_sys_writev(fd, vector, count), fd);
1238         return ret;
1239 }
1240
1241 asmlinkage int sunos_recv(u32 __fd, void __user *ubuf, int size, unsigned flags)
1242 {
1243         int ret, fd = (int) __fd;
1244
1245         ret = check_nonblock(sys_recv(fd, ubuf, size, flags), fd);
1246         return ret;
1247 }
1248
1249 asmlinkage int sunos_send(u32 __fd, void __user *buff, int len, unsigned flags)
1250 {
1251         int ret, fd = (int) __fd;
1252
1253         ret = check_nonblock(sys_send(fd, buff, len, flags), fd);
1254         return ret;
1255 }
1256
1257 asmlinkage int sunos_accept(u32 __fd, struct sockaddr __user *sa, int __user *addrlen)
1258 {
1259         int ret, fd = (int) __fd;
1260
1261         while (1) {
1262                 ret = check_nonblock(sys_accept(fd, sa, addrlen), fd);
1263                 if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
1264                         break;
1265         }
1266         return ret;
1267 }
1268
1269 #define SUNOS_SV_INTERRUPT 2
1270
1271 asmlinkage int sunos_sigaction (int sig,
1272                                 struct old_sigaction32 __user *act,
1273                                 struct old_sigaction32 __user *oact)
1274 {
1275         struct k_sigaction new_ka, old_ka;
1276         int ret;
1277
1278         if (act) {
1279                 compat_old_sigset_t mask;
1280                 u32 u_handler;
1281
1282                 if (get_user(u_handler, &act->sa_handler) ||
1283                     __get_user(new_ka.sa.sa_flags, &act->sa_flags))
1284                         return -EFAULT;
1285                 new_ka.sa.sa_handler = compat_ptr(u_handler);
1286                 __get_user(mask, &act->sa_mask);
1287                 new_ka.sa.sa_restorer = NULL;
1288                 new_ka.ka_restorer = NULL;
1289                 siginitset(&new_ka.sa.sa_mask, mask);
1290                 new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
1291         }
1292
1293         ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
1294
1295         if (!ret && oact) {
1296                 old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
1297                 if (put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler) ||
1298                     __put_user(old_ka.sa.sa_flags, &oact->sa_flags))
1299                         return -EFAULT;
1300                 __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
1301         }
1302
1303         return ret;
1304 }
1305
1306 asmlinkage int sunos_setsockopt(u32 __fd, u32 __level, u32 __optname,
1307                                 char __user *optval, u32 __optlen)
1308 {
1309         int fd = (int) __fd;
1310         int level = (int) __level;
1311         int optname = (int) __optname;
1312         int optlen = (int) __optlen;
1313         int tr_opt = optname;
1314         int ret;
1315
1316         if (level == SOL_IP) {
1317                 /* Multicast socketopts (ttl, membership) */
1318                 if (tr_opt >=2 && tr_opt <= 6)
1319                         tr_opt += 30;
1320         }
1321         ret = sys_setsockopt(fd, level, tr_opt,
1322                              optval, optlen);
1323         return ret;
1324 }
1325
1326 asmlinkage int sunos_getsockopt(u32 __fd, u32 __level, u32 __optname,
1327                                 char __user *optval, int __user *optlen)
1328 {
1329         int fd = (int) __fd;
1330         int level = (int) __level;
1331         int optname = (int) __optname;
1332         int tr_opt = optname;
1333         int ret;
1334
1335         if (level == SOL_IP) {
1336                 /* Multicast socketopts (ttl, membership) */
1337                 if (tr_opt >=2 && tr_opt <= 6)
1338                         tr_opt += 30;
1339         }
1340         ret = compat_sys_getsockopt(fd, level, tr_opt,
1341                                     optval, optlen);
1342         return ret;
1343 }