linux/arch/mn10300/kernel/fpu.c
<<
>>
Prefs
   1/* MN10300 FPU management
   2 *
   3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public Licence
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the Licence, or (at your option) any later version.
  10 */
  11#include <asm/uaccess.h>
  12#include <asm/fpu.h>
  13#include <asm/elf.h>
  14#include <asm/exceptions.h>
  15
  16struct task_struct *fpu_state_owner;
  17
  18/*
  19 * handle an exception due to the FPU being disabled
  20 */
  21asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code)
  22{
  23        struct task_struct *tsk = current;
  24
  25        if (!user_mode(regs))
  26                die_if_no_fixup("An FPU Disabled exception happened in"
  27                                " kernel space\n",
  28                                regs, code);
  29
  30#ifdef CONFIG_FPU
  31        preempt_disable();
  32
  33        /* transfer the last process's FPU state to memory */
  34        if (fpu_state_owner) {
  35                fpu_save(&fpu_state_owner->thread.fpu_state);
  36                fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
  37        }
  38
  39        /* the current process now owns the FPU state */
  40        fpu_state_owner = tsk;
  41        regs->epsw |= EPSW_FE;
  42
  43        /* load the FPU with the current process's FPU state or invent a new
  44         * clean one if the process doesn't have one */
  45        if (is_using_fpu(tsk)) {
  46                fpu_restore(&tsk->thread.fpu_state);
  47        } else {
  48                fpu_init_state();
  49                set_using_fpu(tsk);
  50        }
  51
  52        preempt_enable();
  53#else
  54        {
  55                siginfo_t info;
  56
  57                info.si_signo = SIGFPE;
  58                info.si_errno = 0;
  59                info.si_addr = (void *) tsk->thread.uregs->pc;
  60                info.si_code = FPE_FLTINV;
  61
  62                force_sig_info(SIGFPE, &info, tsk);
  63        }
  64#endif  /* CONFIG_FPU */
  65}
  66
  67/*
  68 * handle an FPU operational exception
  69 * - there's a possibility that if the FPU is asynchronous, the signal might
  70 *   be meant for a process other than the current one
  71 */
  72asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
  73{
  74        struct task_struct *tsk = fpu_state_owner;
  75        siginfo_t info;
  76
  77        if (!user_mode(regs))
  78                die_if_no_fixup("An FPU Operation exception happened in"
  79                                " kernel space\n",
  80                                regs, code);
  81
  82        if (!tsk)
  83                die_if_no_fixup("An FPU Operation exception happened,"
  84                                " but the FPU is not in use",
  85                                regs, code);
  86
  87        info.si_signo = SIGFPE;
  88        info.si_errno = 0;
  89        info.si_addr = (void *) tsk->thread.uregs->pc;
  90        info.si_code = FPE_FLTINV;
  91
  92#ifdef CONFIG_FPU
  93        {
  94                u32 fpcr;
  95
  96                /* get FPCR (we need to enable the FPU whilst we do this) */
  97                asm volatile("  or      %1,epsw         \n"
  98#ifdef CONFIG_MN10300_PROC_MN103E010
  99                             "  nop                     \n"
 100                             "  nop                     \n"
 101                             "  nop                     \n"
 102#endif
 103                             "  fmov    fpcr,%0         \n"
 104#ifdef CONFIG_MN10300_PROC_MN103E010
 105                             "  nop                     \n"
 106                             "  nop                     \n"
 107                             "  nop                     \n"
 108#endif
 109                             "  and     %2,epsw         \n"
 110                             : "=&d"(fpcr)
 111                             : "i"(EPSW_FE), "i"(~EPSW_FE)
 112                             );
 113
 114                if (fpcr & FPCR_EC_Z)
 115                        info.si_code = FPE_FLTDIV;
 116                else if (fpcr & FPCR_EC_O)
 117                        info.si_code = FPE_FLTOVF;
 118                else if (fpcr & FPCR_EC_U)
 119                        info.si_code = FPE_FLTUND;
 120                else if (fpcr & FPCR_EC_I)
 121                        info.si_code = FPE_FLTRES;
 122        }
 123#endif
 124
 125        force_sig_info(SIGFPE, &info, tsk);
 126}
 127
 128/*
 129 * save the FPU state to a signal context
 130 */
 131int fpu_setup_sigcontext(struct fpucontext *fpucontext)
 132{
 133#ifdef CONFIG_FPU
 134        struct task_struct *tsk = current;
 135
 136        if (!is_using_fpu(tsk))
 137                return 0;
 138
 139        /* transfer the current FPU state to memory and cause fpu_init() to be
 140         * triggered by the next attempted FPU operation by the current
 141         * process.
 142         */
 143        preempt_disable();
 144
 145        if (fpu_state_owner == tsk) {
 146                fpu_save(&tsk->thread.fpu_state);
 147                fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
 148                fpu_state_owner = NULL;
 149        }
 150
 151        preempt_enable();
 152
 153        /* we no longer have a valid current FPU state */
 154        clear_using_fpu(tsk);
 155
 156        /* transfer the saved FPU state onto the userspace stack */
 157        if (copy_to_user(fpucontext,
 158                         &tsk->thread.fpu_state,
 159                         min(sizeof(struct fpu_state_struct),
 160                             sizeof(struct fpucontext))))
 161                return -1;
 162
 163        return 1;
 164#else
 165        return 0;
 166#endif
 167}
 168
 169/*
 170 * kill a process's FPU state during restoration after signal handling
 171 */
 172void fpu_kill_state(struct task_struct *tsk)
 173{
 174#ifdef CONFIG_FPU
 175        /* disown anything left in the FPU */
 176        preempt_disable();
 177
 178        if (fpu_state_owner == tsk) {
 179                fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
 180                fpu_state_owner = NULL;
 181        }
 182
 183        preempt_enable();
 184#endif
 185        /* we no longer have a valid current FPU state */
 186        clear_using_fpu(tsk);
 187}
 188
 189/*
 190 * restore the FPU state from a signal context
 191 */
 192int fpu_restore_sigcontext(struct fpucontext *fpucontext)
 193{
 194        struct task_struct *tsk = current;
 195        int ret;
 196
 197        /* load up the old FPU state */
 198        ret = copy_from_user(&tsk->thread.fpu_state,
 199                             fpucontext,
 200                             min(sizeof(struct fpu_state_struct),
 201                                 sizeof(struct fpucontext)));
 202        if (!ret)
 203                set_using_fpu(tsk);
 204
 205        return ret;
 206}
 207
 208/*
 209 * fill in the FPU structure for a core dump
 210 */
 211int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
 212{
 213        struct task_struct *tsk = current;
 214        int fpvalid;
 215
 216        fpvalid = is_using_fpu(tsk);
 217        if (fpvalid) {
 218                unlazy_fpu(tsk);
 219                memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
 220        }
 221
 222        return fpvalid;
 223}
 224