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#include <asm/system.h>
  16
  17#ifdef CONFIG_LAZY_SAVE_FPU
  18struct task_struct *fpu_state_owner;
  19#endif
  20
  21/*
  22 * error functions in FPU disabled exception
  23 */
  24asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
  25{
  26        die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
  27                        regs, EXCEP_FPU_DISABLED);
  28}
  29
  30/*
  31 * handle an FPU operational exception
  32 * - there's a possibility that if the FPU is asynchronous, the signal might
  33 *   be meant for a process other than the current one
  34 */
  35asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
  36{
  37        struct task_struct *tsk = current;
  38        siginfo_t info;
  39        u32 fpcr;
  40
  41        if (!user_mode(regs))
  42                die_if_no_fixup("An FPU Operation exception happened in"
  43                                " kernel space\n",
  44                                regs, code);
  45
  46        if (!is_using_fpu(tsk))
  47                die_if_no_fixup("An FPU Operation exception happened,"
  48                                " but the FPU is not in use",
  49                                regs, code);
  50
  51        info.si_signo = SIGFPE;
  52        info.si_errno = 0;
  53        info.si_addr = (void *) tsk->thread.uregs->pc;
  54        info.si_code = FPE_FLTINV;
  55
  56        unlazy_fpu(tsk);
  57
  58        fpcr = tsk->thread.fpu_state.fpcr;
  59
  60        if (fpcr & FPCR_EC_Z)
  61                info.si_code = FPE_FLTDIV;
  62        else if (fpcr & FPCR_EC_O)
  63                info.si_code = FPE_FLTOVF;
  64        else if (fpcr & FPCR_EC_U)
  65                info.si_code = FPE_FLTUND;
  66        else if (fpcr & FPCR_EC_I)
  67                info.si_code = FPE_FLTRES;
  68
  69        force_sig_info(SIGFPE, &info, tsk);
  70}
  71
  72/*
  73 * handle an FPU invalid_op exception
  74 * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
  75 */
  76asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
  77{
  78        siginfo_t info;
  79
  80        if (!user_mode(regs))
  81                die_if_no_fixup("FPU invalid opcode", regs, code);
  82
  83        info.si_signo = SIGILL;
  84        info.si_errno = 0;
  85        info.si_code = ILL_COPROC;
  86        info.si_addr = (void *) regs->pc;
  87        force_sig_info(info.si_signo, &info, current);
  88}
  89
  90/*
  91 * save the FPU state to a signal context
  92 */
  93int fpu_setup_sigcontext(struct fpucontext *fpucontext)
  94{
  95        struct task_struct *tsk = current;
  96
  97        if (!is_using_fpu(tsk))
  98                return 0;
  99
 100        /* transfer the current FPU state to memory and cause fpu_init() to be
 101         * triggered by the next attempted FPU operation by the current
 102         * process.
 103         */
 104        preempt_disable();
 105
 106#ifndef CONFIG_LAZY_SAVE_FPU
 107        if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
 108                fpu_save(&tsk->thread.fpu_state);
 109                tsk->thread.uregs->epsw &= ~EPSW_FE;
 110                tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
 111        }
 112#else /* !CONFIG_LAZY_SAVE_FPU */
 113        if (fpu_state_owner == tsk) {
 114                fpu_save(&tsk->thread.fpu_state);
 115                fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
 116                fpu_state_owner = NULL;
 117        }
 118#endif /* !CONFIG_LAZY_SAVE_FPU */
 119
 120        preempt_enable();
 121
 122        /* we no longer have a valid current FPU state */
 123        clear_using_fpu(tsk);
 124
 125        /* transfer the saved FPU state onto the userspace stack */
 126        if (copy_to_user(fpucontext,
 127                         &tsk->thread.fpu_state,
 128                         min(sizeof(struct fpu_state_struct),
 129                             sizeof(struct fpucontext))))
 130                return -1;
 131
 132        return 1;
 133}
 134
 135/*
 136 * kill a process's FPU state during restoration after signal handling
 137 */
 138void fpu_kill_state(struct task_struct *tsk)
 139{
 140        /* disown anything left in the FPU */
 141        preempt_disable();
 142
 143#ifndef CONFIG_LAZY_SAVE_FPU
 144        if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
 145                tsk->thread.uregs->epsw &= ~EPSW_FE;
 146                tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
 147        }
 148#else /* !CONFIG_LAZY_SAVE_FPU */
 149        if (fpu_state_owner == tsk) {
 150                fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
 151                fpu_state_owner = NULL;
 152        }
 153#endif /* !CONFIG_LAZY_SAVE_FPU */
 154
 155        preempt_enable();
 156
 157        /* we no longer have a valid current FPU state */
 158        clear_using_fpu(tsk);
 159}
 160
 161/*
 162 * restore the FPU state from a signal context
 163 */
 164int fpu_restore_sigcontext(struct fpucontext *fpucontext)
 165{
 166        struct task_struct *tsk = current;
 167        int ret;
 168
 169        /* load up the old FPU state */
 170        ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
 171                             min(sizeof(struct fpu_state_struct),
 172                                 sizeof(struct fpucontext)));
 173        if (!ret)
 174                set_using_fpu(tsk);
 175
 176        return ret;
 177}
 178
 179/*
 180 * fill in the FPU structure for a core dump
 181 */
 182int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
 183{
 184        struct task_struct *tsk = current;
 185        int fpvalid;
 186
 187        fpvalid = is_using_fpu(tsk);
 188        if (fpvalid) {
 189                unlazy_fpu(tsk);
 190                memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
 191        }
 192
 193        return fpvalid;
 194}
 195