linux/arch/mips/kernel/kspd.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
   3 *
   4 *  This program is free software; you can distribute it and/or modify it
   5 *  under the terms of the GNU General Public License (Version 2) as
   6 *  published by the Free Software Foundation.
   7 *
   8 *  This program is distributed in the hope it will be useful, but WITHOUT
   9 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  11 *  for more details.
  12 *
  13 *  You should have received a copy of the GNU General Public License along
  14 *  with this program; if not, write to the Free Software Foundation, Inc.,
  15 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  16 *
  17 */
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/sched.h>
  21#include <linux/unistd.h>
  22#include <linux/file.h>
  23#include <linux/fdtable.h>
  24#include <linux/fs.h>
  25#include <linux/syscalls.h>
  26#include <linux/workqueue.h>
  27#include <linux/errno.h>
  28#include <linux/list.h>
  29
  30#include <asm/vpe.h>
  31#include <asm/rtlx.h>
  32#include <asm/kspd.h>
  33
  34static struct workqueue_struct *workqueue = NULL;
  35static struct work_struct work;
  36
  37extern unsigned long cpu_khz;
  38
  39struct mtsp_syscall {
  40        int cmd;
  41        unsigned char abi;
  42        unsigned char size;
  43};
  44
  45struct mtsp_syscall_ret {
  46        int retval;
  47        int errno;
  48};
  49
  50struct mtsp_syscall_generic {
  51        int arg0;
  52        int arg1;
  53        int arg2;
  54        int arg3;
  55        int arg4;
  56        int arg5;
  57        int arg6;
  58};
  59
  60static struct list_head kspd_notifylist;
  61static int sp_stopping = 0;
  62
  63/* these should match with those in the SDE kit */
  64#define MTSP_SYSCALL_BASE       0
  65#define MTSP_SYSCALL_EXIT       (MTSP_SYSCALL_BASE + 0)
  66#define MTSP_SYSCALL_OPEN       (MTSP_SYSCALL_BASE + 1)
  67#define MTSP_SYSCALL_READ       (MTSP_SYSCALL_BASE + 2)
  68#define MTSP_SYSCALL_WRITE      (MTSP_SYSCALL_BASE + 3)
  69#define MTSP_SYSCALL_CLOSE      (MTSP_SYSCALL_BASE + 4)
  70#define MTSP_SYSCALL_LSEEK32    (MTSP_SYSCALL_BASE + 5)
  71#define MTSP_SYSCALL_ISATTY     (MTSP_SYSCALL_BASE + 6)
  72#define MTSP_SYSCALL_GETTIME    (MTSP_SYSCALL_BASE + 7)
  73#define MTSP_SYSCALL_PIPEFREQ   (MTSP_SYSCALL_BASE + 8)
  74#define MTSP_SYSCALL_GETTOD     (MTSP_SYSCALL_BASE + 9)
  75#define MTSP_SYSCALL_IOCTL     (MTSP_SYSCALL_BASE + 10)
  76
  77#define MTSP_O_RDONLY           0x0000
  78#define MTSP_O_WRONLY           0x0001
  79#define MTSP_O_RDWR             0x0002
  80#define MTSP_O_NONBLOCK         0x0004
  81#define MTSP_O_APPEND           0x0008
  82#define MTSP_O_SHLOCK           0x0010
  83#define MTSP_O_EXLOCK           0x0020
  84#define MTSP_O_ASYNC            0x0040
  85#define MTSP_O_FSYNC            O_SYNC
  86#define MTSP_O_NOFOLLOW         0x0100
  87#define MTSP_O_SYNC             0x0080
  88#define MTSP_O_CREAT            0x0200
  89#define MTSP_O_TRUNC            0x0400
  90#define MTSP_O_EXCL             0x0800
  91#define MTSP_O_BINARY           0x8000
  92
  93extern int tclimit;
  94
  95struct apsp_table  {
  96        int sp;
  97        int ap;
  98};
  99
 100/* we might want to do the mode flags too */
 101struct apsp_table open_flags_table[] = {
 102        { MTSP_O_RDWR, O_RDWR },
 103        { MTSP_O_WRONLY, O_WRONLY },
 104        { MTSP_O_CREAT, O_CREAT },
 105        { MTSP_O_TRUNC, O_TRUNC },
 106        { MTSP_O_NONBLOCK, O_NONBLOCK },
 107        { MTSP_O_APPEND, O_APPEND },
 108        { MTSP_O_NOFOLLOW, O_NOFOLLOW }
 109};
 110
 111struct apsp_table syscall_command_table[] = {
 112        { MTSP_SYSCALL_OPEN, __NR_open },
 113        { MTSP_SYSCALL_CLOSE, __NR_close },
 114        { MTSP_SYSCALL_READ, __NR_read },
 115        { MTSP_SYSCALL_WRITE, __NR_write },
 116        { MTSP_SYSCALL_LSEEK32, __NR_lseek },
 117        { MTSP_SYSCALL_IOCTL, __NR_ioctl }
 118};
 119
 120static int sp_syscall(int num, int arg0, int arg1, int arg2, int arg3)
 121{
 122        register long int _num  __asm__("$2") = num;
 123        register long int _arg0  __asm__("$4") = arg0;
 124        register long int _arg1  __asm__("$5") = arg1;
 125        register long int _arg2  __asm__("$6") = arg2;
 126        register long int _arg3  __asm__("$7") = arg3;
 127
 128        mm_segment_t old_fs;
 129
 130        old_fs = get_fs();
 131        set_fs(KERNEL_DS);
 132
 133        __asm__ __volatile__ (
 134        "       syscall                                 \n"
 135        : "=r" (_num), "=r" (_arg3)
 136        : "r" (_num), "r" (_arg0), "r" (_arg1), "r" (_arg2), "r" (_arg3));
 137
 138        set_fs(old_fs);
 139
 140        /* $a3 is error flag */
 141        if (_arg3)
 142                return -_num;
 143
 144        return _num;
 145}
 146
 147static int translate_syscall_command(int cmd)
 148{
 149        int i;
 150        int ret = -1;
 151
 152        for (i = 0; i < ARRAY_SIZE(syscall_command_table); i++) {
 153                if ((cmd == syscall_command_table[i].sp))
 154                        return syscall_command_table[i].ap;
 155        }
 156
 157        return ret;
 158}
 159
 160static unsigned int translate_open_flags(int flags)
 161{
 162        int i;
 163        unsigned int ret = 0;
 164
 165        for (i = 0; i < ARRAY_SIZE(open_flags_table); i++) {
 166                if( (flags & open_flags_table[i].sp) ) {
 167                        ret |= open_flags_table[i].ap;
 168                }
 169        }
 170
 171        return ret;
 172}
 173
 174
 175static void sp_setfsuidgid( uid_t uid, gid_t gid)
 176{
 177        current->cred->fsuid = uid;
 178        current->cred->fsgid = gid;
 179
 180        key_fsuid_changed(current);
 181        key_fsgid_changed(current);
 182}
 183
 184/*
 185 * Expects a request to be on the sysio channel. Reads it.  Decides whether
 186 * its a linux syscall and runs it, or whatever.  Puts the return code back
 187 * into the request and sends the whole thing back.
 188 */
 189void sp_work_handle_request(void)
 190{
 191        struct mtsp_syscall sc;
 192        struct mtsp_syscall_generic generic;
 193        struct mtsp_syscall_ret ret;
 194        struct kspd_notifications *n;
 195        unsigned long written;
 196        mm_segment_t old_fs;
 197        struct timeval tv;
 198        struct timezone tz;
 199        int cmd;
 200
 201        char *vcwd;
 202        int size;
 203
 204        ret.retval = -1;
 205
 206        old_fs = get_fs();
 207        set_fs(KERNEL_DS);
 208
 209        if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall))) {
 210                set_fs(old_fs);
 211                printk(KERN_ERR "Expected request but nothing to read\n");
 212                return;
 213        }
 214
 215        size = sc.size;
 216
 217        if (size) {
 218                if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size)) {
 219                        set_fs(old_fs);
 220                        printk(KERN_ERR "Expected request but nothing to read\n");
 221                        return;
 222                }
 223        }
 224
 225        /* Run the syscall at the privilege of the user who loaded the
 226           SP program */
 227
 228        if (vpe_getuid(tclimit))
 229                sp_setfsuidgid(vpe_getuid(tclimit), vpe_getgid(tclimit));
 230
 231        switch (sc.cmd) {
 232        /* needs the flags argument translating from SDE kit to
 233           linux */
 234        case MTSP_SYSCALL_PIPEFREQ:
 235                ret.retval = cpu_khz * 1000;
 236                ret.errno = 0;
 237                break;
 238
 239        case MTSP_SYSCALL_GETTOD:
 240                memset(&tz, 0, sizeof(tz));
 241                if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv,
 242                                             (int)&tz, 0, 0)) == 0)
 243                ret.retval = tv.tv_sec;
 244                break;
 245
 246        case MTSP_SYSCALL_EXIT:
 247                list_for_each_entry(n, &kspd_notifylist, list)
 248                        n->kspd_sp_exit(tclimit);
 249                sp_stopping = 1;
 250
 251                printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n",
 252                       generic.arg0);
 253                break;
 254
 255        case MTSP_SYSCALL_OPEN:
 256                generic.arg1 = translate_open_flags(generic.arg1);
 257
 258                vcwd = vpe_getcwd(tclimit);
 259
 260                /* change to cwd of the process that loaded the SP program */
 261                old_fs = get_fs();
 262                set_fs(KERNEL_DS);
 263                sys_chdir(vcwd);
 264                set_fs(old_fs);
 265
 266                sc.cmd = __NR_open;
 267
 268                /* fall through */
 269
 270        default:
 271                if ((sc.cmd >= __NR_Linux) &&
 272                    (sc.cmd <= (__NR_Linux +  __NR_Linux_syscalls)) )
 273                        cmd = sc.cmd;
 274                else
 275                        cmd = translate_syscall_command(sc.cmd);
 276
 277                if (cmd >= 0) {
 278                        ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1,
 279                                                generic.arg2, generic.arg3);
 280                } else
 281                        printk(KERN_WARNING
 282                               "KSPD: Unknown SP syscall number %d\n", sc.cmd);
 283                break;
 284        } /* switch */
 285
 286        if (vpe_getuid(tclimit))
 287                sp_setfsuidgid( 0, 0);
 288
 289        old_fs = get_fs();
 290        set_fs(KERNEL_DS);
 291        written = rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(ret));
 292        set_fs(old_fs);
 293        if (written < sizeof(ret))
 294                printk("KSPD: sp_work_handle_request failed to send to SP\n");
 295}
 296
 297static void sp_cleanup(void)
 298{
 299        struct files_struct *files = current->files;
 300        int i, j;
 301        struct fdtable *fdt;
 302
 303        j = 0;
 304
 305        /*
 306         * It is safe to dereference the fd table without RCU or
 307         * ->file_lock
 308         */
 309        fdt = files_fdtable(files);
 310        for (;;) {
 311                unsigned long set;
 312                i = j * __NFDBITS;
 313                if (i >= fdt->max_fds)
 314                        break;
 315                set = fdt->open_fds->fds_bits[j++];
 316                while (set) {
 317                        if (set & 1) {
 318                                struct file * file = xchg(&fdt->fd[i], NULL);
 319                                if (file)
 320                                        filp_close(file, files);
 321                        }
 322                        i++;
 323                        set >>= 1;
 324                }
 325        }
 326
 327        /* Put daemon cwd back to root to avoid umount problems */
 328        sys_chdir("/");
 329}
 330
 331static int channel_open = 0;
 332
 333/* the work handler */
 334static void sp_work(struct work_struct *unused)
 335{
 336        if (!channel_open) {
 337                if( rtlx_open(RTLX_CHANNEL_SYSIO, 1) != 0) {
 338                        printk("KSPD: unable to open sp channel\n");
 339                        sp_stopping = 1;
 340                } else {
 341                        channel_open++;
 342                        printk(KERN_DEBUG "KSPD: SP channel opened\n");
 343                }
 344        } else {
 345                /* wait for some data, allow it to sleep */
 346                rtlx_read_poll(RTLX_CHANNEL_SYSIO, 1);
 347
 348                /* Check we haven't been woken because we are stopping */
 349                if (!sp_stopping)
 350                        sp_work_handle_request();
 351        }
 352
 353        if (!sp_stopping)
 354                queue_work(workqueue, &work);
 355        else
 356                sp_cleanup();
 357}
 358
 359static void startwork(int vpe)
 360{
 361        sp_stopping = channel_open = 0;
 362
 363        if (workqueue == NULL) {
 364                if ((workqueue = create_singlethread_workqueue("kspd")) == NULL) {
 365                        printk(KERN_ERR "unable to start kspd\n");
 366                        return;
 367                }
 368
 369                INIT_WORK(&work, sp_work);
 370        }
 371
 372        queue_work(workqueue, &work);
 373}
 374
 375static void stopwork(int vpe)
 376{
 377        sp_stopping = 1;
 378
 379        printk(KERN_DEBUG "KSPD: SP stopping\n");
 380}
 381
 382void kspd_notify(struct kspd_notifications *notify)
 383{
 384        list_add(&notify->list, &kspd_notifylist);
 385}
 386
 387static struct vpe_notifications notify;
 388static int kspd_module_init(void)
 389{
 390        INIT_LIST_HEAD(&kspd_notifylist);
 391
 392        notify.start = startwork;
 393        notify.stop = stopwork;
 394        vpe_notify(tclimit, &notify);
 395
 396        return 0;
 397}
 398
 399static void kspd_module_exit(void)
 400{
 401
 402}
 403
 404module_init(kspd_module_init);
 405module_exit(kspd_module_exit);
 406
 407MODULE_DESCRIPTION("MIPS KSPD");
 408MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
 409MODULE_LICENSE("GPL");
 410