linux/arch/m68k/kernel/sys_m68k.c
<<
>>
Prefs
   1/*
   2 * linux/arch/m68k/kernel/sys_m68k.c
   3 *
   4 * This file contains various random system calls that
   5 * have a non-standard calling sequence on the Linux/m68k
   6 * platform.
   7 */
   8
   9#include <linux/capability.h>
  10#include <linux/errno.h>
  11#include <linux/sched.h>
  12#include <linux/mm.h>
  13#include <linux/fs.h>
  14#include <linux/smp.h>
  15#include <linux/smp_lock.h>
  16#include <linux/sem.h>
  17#include <linux/msg.h>
  18#include <linux/shm.h>
  19#include <linux/stat.h>
  20#include <linux/syscalls.h>
  21#include <linux/mman.h>
  22#include <linux/file.h>
  23#include <linux/ipc.h>
  24
  25#include <asm/setup.h>
  26#include <asm/uaccess.h>
  27#include <asm/cachectl.h>
  28#include <asm/traps.h>
  29#include <asm/page.h>
  30#include <asm/unistd.h>
  31
  32/* common code for old and new mmaps */
  33static inline long do_mmap2(
  34        unsigned long addr, unsigned long len,
  35        unsigned long prot, unsigned long flags,
  36        unsigned long fd, unsigned long pgoff)
  37{
  38        int error = -EBADF;
  39        struct file * file = NULL;
  40
  41        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
  42        if (!(flags & MAP_ANONYMOUS)) {
  43                file = fget(fd);
  44                if (!file)
  45                        goto out;
  46        }
  47
  48        down_write(&current->mm->mmap_sem);
  49        error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
  50        up_write(&current->mm->mmap_sem);
  51
  52        if (file)
  53                fput(file);
  54out:
  55        return error;
  56}
  57
  58asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
  59        unsigned long prot, unsigned long flags,
  60        unsigned long fd, unsigned long pgoff)
  61{
  62        return do_mmap2(addr, len, prot, flags, fd, pgoff);
  63}
  64
  65/*
  66 * Perform the select(nd, in, out, ex, tv) and mmap() system
  67 * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
  68 * handle more than 4 system call parameters, so these system calls
  69 * used a memory block for parameter passing..
  70 */
  71
  72struct mmap_arg_struct {
  73        unsigned long addr;
  74        unsigned long len;
  75        unsigned long prot;
  76        unsigned long flags;
  77        unsigned long fd;
  78        unsigned long offset;
  79};
  80
  81asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
  82{
  83        struct mmap_arg_struct a;
  84        int error = -EFAULT;
  85
  86        if (copy_from_user(&a, arg, sizeof(a)))
  87                goto out;
  88
  89        error = -EINVAL;
  90        if (a.offset & ~PAGE_MASK)
  91                goto out;
  92
  93        a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
  94
  95        error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
  96out:
  97        return error;
  98}
  99
 100#if 0
 101struct mmap_arg_struct64 {
 102        __u32 addr;
 103        __u32 len;
 104        __u32 prot;
 105        __u32 flags;
 106        __u64 offset; /* 64 bits */
 107        __u32 fd;
 108};
 109
 110asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
 111{
 112        int error = -EFAULT;
 113        struct file * file = NULL;
 114        struct mmap_arg_struct64 a;
 115        unsigned long pgoff;
 116
 117        if (copy_from_user(&a, arg, sizeof(a)))
 118                return -EFAULT;
 119
 120        if ((long)a.offset & ~PAGE_MASK)
 121                return -EINVAL;
 122
 123        pgoff = a.offset >> PAGE_SHIFT;
 124        if ((a.offset >> PAGE_SHIFT) != pgoff)
 125                return -EINVAL;
 126
 127        if (!(a.flags & MAP_ANONYMOUS)) {
 128                error = -EBADF;
 129                file = fget(a.fd);
 130                if (!file)
 131                        goto out;
 132        }
 133        a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 134
 135        down_write(&current->mm->mmap_sem);
 136        error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
 137        up_write(&current->mm->mmap_sem);
 138        if (file)
 139                fput(file);
 140out:
 141        return error;
 142}
 143#endif
 144
 145struct sel_arg_struct {
 146        unsigned long n;
 147        fd_set __user *inp, *outp, *exp;
 148        struct timeval __user *tvp;
 149};
 150
 151asmlinkage int old_select(struct sel_arg_struct __user *arg)
 152{
 153        struct sel_arg_struct a;
 154
 155        if (copy_from_user(&a, arg, sizeof(a)))
 156                return -EFAULT;
 157        /* sys_select() does the appropriate kernel locking */
 158        return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
 159}
 160
 161/*
 162 * sys_ipc() is the de-multiplexer for the SysV IPC calls..
 163 *
 164 * This is really horribly ugly.
 165 */
 166asmlinkage int sys_ipc (uint call, int first, int second,
 167                        int third, void __user *ptr, long fifth)
 168{
 169        int version, ret;
 170
 171        version = call >> 16; /* hack for backward compatibility */
 172        call &= 0xffff;
 173
 174        if (call <= SEMCTL)
 175                switch (call) {
 176                case SEMOP:
 177                        return sys_semop (first, ptr, second);
 178                case SEMGET:
 179                        return sys_semget (first, second, third);
 180                case SEMCTL: {
 181                        union semun fourth;
 182                        if (!ptr)
 183                                return -EINVAL;
 184                        if (get_user(fourth.__pad, (void __user *__user *) ptr))
 185                                return -EFAULT;
 186                        return sys_semctl (first, second, third, fourth);
 187                        }
 188                default:
 189                        return -ENOSYS;
 190                }
 191        if (call <= MSGCTL)
 192                switch (call) {
 193                case MSGSND:
 194                        return sys_msgsnd (first, ptr, second, third);
 195                case MSGRCV:
 196                        switch (version) {
 197                        case 0: {
 198                                struct ipc_kludge tmp;
 199                                if (!ptr)
 200                                        return -EINVAL;
 201                                if (copy_from_user (&tmp, ptr, sizeof (tmp)))
 202                                        return -EFAULT;
 203                                return sys_msgrcv (first, tmp.msgp, second,
 204                                                   tmp.msgtyp, third);
 205                                }
 206                        default:
 207                                return sys_msgrcv (first, ptr,
 208                                                   second, fifth, third);
 209                        }
 210                case MSGGET:
 211                        return sys_msgget ((key_t) first, second);
 212                case MSGCTL:
 213                        return sys_msgctl (first, second, ptr);
 214                default:
 215                        return -ENOSYS;
 216                }
 217        if (call <= SHMCTL)
 218                switch (call) {
 219                case SHMAT:
 220                        switch (version) {
 221                        default: {
 222                                ulong raddr;
 223                                ret = do_shmat (first, ptr, second, &raddr);
 224                                if (ret)
 225                                        return ret;
 226                                return put_user (raddr, (ulong __user *) third);
 227                        }
 228                        }
 229                case SHMDT:
 230                        return sys_shmdt (ptr);
 231                case SHMGET:
 232                        return sys_shmget (first, second, third);
 233                case SHMCTL:
 234                        return sys_shmctl (first, second, ptr);
 235                default:
 236                        return -ENOSYS;
 237                }
 238
 239        return -EINVAL;
 240}
 241
 242/* Convert virtual (user) address VADDR to physical address PADDR */
 243#define virt_to_phys_040(vaddr)                                         \
 244({                                                                      \
 245  unsigned long _mmusr, _paddr;                                         \
 246                                                                        \
 247  __asm__ __volatile__ (".chip 68040\n\t"                               \
 248                        "ptestr (%1)\n\t"                               \
 249                        "movec %%mmusr,%0\n\t"                          \
 250                        ".chip 68k"                                     \
 251                        : "=r" (_mmusr)                                 \
 252                        : "a" (vaddr));                                 \
 253  _paddr = (_mmusr & MMU_R_040) ? (_mmusr & PAGE_MASK) : 0;             \
 254  _paddr;                                                               \
 255})
 256
 257static inline int
 258cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len)
 259{
 260  unsigned long paddr, i;
 261
 262  switch (scope)
 263    {
 264    case FLUSH_SCOPE_ALL:
 265      switch (cache)
 266        {
 267        case FLUSH_CACHE_DATA:
 268          /* This nop is needed for some broken versions of the 68040.  */
 269          __asm__ __volatile__ ("nop\n\t"
 270                                ".chip 68040\n\t"
 271                                "cpusha %dc\n\t"
 272                                ".chip 68k");
 273          break;
 274        case FLUSH_CACHE_INSN:
 275          __asm__ __volatile__ ("nop\n\t"
 276                                ".chip 68040\n\t"
 277                                "cpusha %ic\n\t"
 278                                ".chip 68k");
 279          break;
 280        default:
 281        case FLUSH_CACHE_BOTH:
 282          __asm__ __volatile__ ("nop\n\t"
 283                                ".chip 68040\n\t"
 284                                "cpusha %bc\n\t"
 285                                ".chip 68k");
 286          break;
 287        }
 288      break;
 289
 290    case FLUSH_SCOPE_LINE:
 291      /* Find the physical address of the first mapped page in the
 292         address range.  */
 293      if ((paddr = virt_to_phys_040(addr))) {
 294        paddr += addr & ~(PAGE_MASK | 15);
 295        len = (len + (addr & 15) + 15) >> 4;
 296      } else {
 297        unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK);
 298
 299        if (len <= tmp)
 300          return 0;
 301        addr += tmp;
 302        len -= tmp;
 303        tmp = PAGE_SIZE;
 304        for (;;)
 305          {
 306            if ((paddr = virt_to_phys_040(addr)))
 307              break;
 308            if (len <= tmp)
 309              return 0;
 310            addr += tmp;
 311            len -= tmp;
 312          }
 313        len = (len + 15) >> 4;
 314      }
 315      i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4;
 316      while (len--)
 317        {
 318          switch (cache)
 319            {
 320            case FLUSH_CACHE_DATA:
 321              __asm__ __volatile__ ("nop\n\t"
 322                                    ".chip 68040\n\t"
 323                                    "cpushl %%dc,(%0)\n\t"
 324                                    ".chip 68k"
 325                                    : : "a" (paddr));
 326              break;
 327            case FLUSH_CACHE_INSN:
 328              __asm__ __volatile__ ("nop\n\t"
 329                                    ".chip 68040\n\t"
 330                                    "cpushl %%ic,(%0)\n\t"
 331                                    ".chip 68k"
 332                                    : : "a" (paddr));
 333              break;
 334            default:
 335            case FLUSH_CACHE_BOTH:
 336              __asm__ __volatile__ ("nop\n\t"
 337                                    ".chip 68040\n\t"
 338                                    "cpushl %%bc,(%0)\n\t"
 339                                    ".chip 68k"
 340                                    : : "a" (paddr));
 341              break;
 342            }
 343          if (!--i && len)
 344            {
 345              /*
 346               * No need to page align here since it is done by
 347               * virt_to_phys_040().
 348               */
 349              addr += PAGE_SIZE;
 350              i = PAGE_SIZE / 16;
 351              /* Recompute physical address when crossing a page
 352                 boundary. */
 353              for (;;)
 354                {
 355                  if ((paddr = virt_to_phys_040(addr)))
 356                    break;
 357                  if (len <= i)
 358                    return 0;
 359                  len -= i;
 360                  addr += PAGE_SIZE;
 361                }
 362            }
 363          else
 364            paddr += 16;
 365        }
 366      break;
 367
 368    default:
 369    case FLUSH_SCOPE_PAGE:
 370      len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1);
 371      for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
 372        {
 373          if (!(paddr = virt_to_phys_040(addr)))
 374            continue;
 375          switch (cache)
 376            {
 377            case FLUSH_CACHE_DATA:
 378              __asm__ __volatile__ ("nop\n\t"
 379                                    ".chip 68040\n\t"
 380                                    "cpushp %%dc,(%0)\n\t"
 381                                    ".chip 68k"
 382                                    : : "a" (paddr));
 383              break;
 384            case FLUSH_CACHE_INSN:
 385              __asm__ __volatile__ ("nop\n\t"
 386                                    ".chip 68040\n\t"
 387                                    "cpushp %%ic,(%0)\n\t"
 388                                    ".chip 68k"
 389                                    : : "a" (paddr));
 390              break;
 391            default:
 392            case FLUSH_CACHE_BOTH:
 393              __asm__ __volatile__ ("nop\n\t"
 394                                    ".chip 68040\n\t"
 395                                    "cpushp %%bc,(%0)\n\t"
 396                                    ".chip 68k"
 397                                    : : "a" (paddr));
 398              break;
 399            }
 400        }
 401      break;
 402    }
 403  return 0;
 404}
 405
 406#define virt_to_phys_060(vaddr)                         \
 407({                                                      \
 408  unsigned long paddr;                                  \
 409  __asm__ __volatile__ (".chip 68060\n\t"               \
 410                        "plpar (%0)\n\t"                \
 411                        ".chip 68k"                     \
 412                        : "=a" (paddr)                  \
 413                        : "0" (vaddr));                 \
 414  (paddr); /* XXX */                                    \
 415})
 416
 417static inline int
 418cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len)
 419{
 420  unsigned long paddr, i;
 421
 422  /*
 423   * 68060 manual says:
 424   *  cpush %dc : flush DC, remains valid (with our %cacr setup)
 425   *  cpush %ic : invalidate IC
 426   *  cpush %bc : flush DC + invalidate IC
 427   */
 428  switch (scope)
 429    {
 430    case FLUSH_SCOPE_ALL:
 431      switch (cache)
 432        {
 433        case FLUSH_CACHE_DATA:
 434          __asm__ __volatile__ (".chip 68060\n\t"
 435                                "cpusha %dc\n\t"
 436                                ".chip 68k");
 437          break;
 438        case FLUSH_CACHE_INSN:
 439          __asm__ __volatile__ (".chip 68060\n\t"
 440                                "cpusha %ic\n\t"
 441                                ".chip 68k");
 442          break;
 443        default:
 444        case FLUSH_CACHE_BOTH:
 445          __asm__ __volatile__ (".chip 68060\n\t"
 446                                "cpusha %bc\n\t"
 447                                ".chip 68k");
 448          break;
 449        }
 450      break;
 451
 452    case FLUSH_SCOPE_LINE:
 453      /* Find the physical address of the first mapped page in the
 454         address range.  */
 455      len += addr & 15;
 456      addr &= -16;
 457      if (!(paddr = virt_to_phys_060(addr))) {
 458        unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK);
 459
 460        if (len <= tmp)
 461          return 0;
 462        addr += tmp;
 463        len -= tmp;
 464        tmp = PAGE_SIZE;
 465        for (;;)
 466          {
 467            if ((paddr = virt_to_phys_060(addr)))
 468              break;
 469            if (len <= tmp)
 470              return 0;
 471            addr += tmp;
 472            len -= tmp;
 473          }
 474      }
 475      len = (len + 15) >> 4;
 476      i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4;
 477      while (len--)
 478        {
 479          switch (cache)
 480            {
 481            case FLUSH_CACHE_DATA:
 482              __asm__ __volatile__ (".chip 68060\n\t"
 483                                    "cpushl %%dc,(%0)\n\t"
 484                                    ".chip 68k"
 485                                    : : "a" (paddr));
 486              break;
 487            case FLUSH_CACHE_INSN:
 488              __asm__ __volatile__ (".chip 68060\n\t"
 489                                    "cpushl %%ic,(%0)\n\t"
 490                                    ".chip 68k"
 491                                    : : "a" (paddr));
 492              break;
 493            default:
 494            case FLUSH_CACHE_BOTH:
 495              __asm__ __volatile__ (".chip 68060\n\t"
 496                                    "cpushl %%bc,(%0)\n\t"
 497                                    ".chip 68k"
 498                                    : : "a" (paddr));
 499              break;
 500            }
 501          if (!--i && len)
 502            {
 503
 504              /*
 505               * We just want to jump to the first cache line
 506               * in the next page.
 507               */
 508              addr += PAGE_SIZE;
 509              addr &= PAGE_MASK;
 510
 511              i = PAGE_SIZE / 16;
 512              /* Recompute physical address when crossing a page
 513                 boundary. */
 514              for (;;)
 515                {
 516                  if ((paddr = virt_to_phys_060(addr)))
 517                    break;
 518                  if (len <= i)
 519                    return 0;
 520                  len -= i;
 521                  addr += PAGE_SIZE;
 522                }
 523            }
 524          else
 525            paddr += 16;
 526        }
 527      break;
 528
 529    default:
 530    case FLUSH_SCOPE_PAGE:
 531      len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1);
 532      addr &= PAGE_MASK;        /* Workaround for bug in some
 533                                   revisions of the 68060 */
 534      for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
 535        {
 536          if (!(paddr = virt_to_phys_060(addr)))
 537            continue;
 538          switch (cache)
 539            {
 540            case FLUSH_CACHE_DATA:
 541              __asm__ __volatile__ (".chip 68060\n\t"
 542                                    "cpushp %%dc,(%0)\n\t"
 543                                    ".chip 68k"
 544                                    : : "a" (paddr));
 545              break;
 546            case FLUSH_CACHE_INSN:
 547              __asm__ __volatile__ (".chip 68060\n\t"
 548                                    "cpushp %%ic,(%0)\n\t"
 549                                    ".chip 68k"
 550                                    : : "a" (paddr));
 551              break;
 552            default:
 553            case FLUSH_CACHE_BOTH:
 554              __asm__ __volatile__ (".chip 68060\n\t"
 555                                    "cpushp %%bc,(%0)\n\t"
 556                                    ".chip 68k"
 557                                    : : "a" (paddr));
 558              break;
 559            }
 560        }
 561      break;
 562    }
 563  return 0;
 564}
 565
 566/* sys_cacheflush -- flush (part of) the processor cache.  */
 567asmlinkage int
 568sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
 569{
 570        struct vm_area_struct *vma;
 571        int ret = -EINVAL;
 572
 573        lock_kernel();
 574        if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL ||
 575            cache & ~FLUSH_CACHE_BOTH)
 576                goto out;
 577
 578        if (scope == FLUSH_SCOPE_ALL) {
 579                /* Only the superuser may explicitly flush the whole cache. */
 580                ret = -EPERM;
 581                if (!capable(CAP_SYS_ADMIN))
 582                        goto out;
 583        } else {
 584                /*
 585                 * Verify that the specified address region actually belongs
 586                 * to this process.
 587                 */
 588                vma = find_vma (current->mm, addr);
 589                ret = -EINVAL;
 590                /* Check for overflow.  */
 591                if (addr + len < addr)
 592                        goto out;
 593                if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
 594                        goto out;
 595        }
 596
 597        if (CPU_IS_020_OR_030) {
 598                if (scope == FLUSH_SCOPE_LINE && len < 256) {
 599                        unsigned long cacr;
 600                        __asm__ ("movec %%cacr, %0" : "=r" (cacr));
 601                        if (cache & FLUSH_CACHE_INSN)
 602                                cacr |= 4;
 603                        if (cache & FLUSH_CACHE_DATA)
 604                                cacr |= 0x400;
 605                        len >>= 2;
 606                        while (len--) {
 607                                __asm__ __volatile__ ("movec %1, %%caar\n\t"
 608                                                      "movec %0, %%cacr"
 609                                                      : /* no outputs */
 610                                                      : "r" (cacr), "r" (addr));
 611                                addr += 4;
 612                        }
 613                } else {
 614                        /* Flush the whole cache, even if page granularity requested. */
 615                        unsigned long cacr;
 616                        __asm__ ("movec %%cacr, %0" : "=r" (cacr));
 617                        if (cache & FLUSH_CACHE_INSN)
 618                                cacr |= 8;
 619                        if (cache & FLUSH_CACHE_DATA)
 620                                cacr |= 0x800;
 621                        __asm__ __volatile__ ("movec %0, %%cacr" : : "r" (cacr));
 622                }
 623                ret = 0;
 624                goto out;
 625        } else {
 626            /*
 627             * 040 or 060: don't blindly trust 'scope', someone could
 628             * try to flush a few megs of memory.
 629             */
 630
 631            if (len>=3*PAGE_SIZE && scope<FLUSH_SCOPE_PAGE)
 632                scope=FLUSH_SCOPE_PAGE;
 633            if (len>=10*PAGE_SIZE && scope<FLUSH_SCOPE_ALL)
 634                scope=FLUSH_SCOPE_ALL;
 635            if (CPU_IS_040) {
 636                ret = cache_flush_040 (addr, scope, cache, len);
 637            } else if (CPU_IS_060) {
 638                ret = cache_flush_060 (addr, scope, cache, len);
 639            }
 640        }
 641out:
 642        unlock_kernel();
 643        return ret;
 644}
 645
 646asmlinkage int sys_getpagesize(void)
 647{
 648        return PAGE_SIZE;
 649}
 650
 651/*
 652 * Do a system call from kernel instead of calling sys_execve so we
 653 * end up with proper pt_regs.
 654 */
 655int kernel_execve(const char *filename, char *const argv[], char *const envp[])
 656{
 657        register long __res asm ("%d0") = __NR_execve;
 658        register long __a asm ("%d1") = (long)(filename);
 659        register long __b asm ("%d2") = (long)(argv);
 660        register long __c asm ("%d3") = (long)(envp);
 661        asm volatile ("trap  #0" : "+d" (__res)
 662                        : "d" (__a), "d" (__b), "d" (__c));
 663        return __res;
 664}
 665