From: kettenis Date: Fri, 26 Jan 2018 16:22:19 +0000 (+0000) Subject: Add kernel support for the VFP FPU/SIMD unit. Based on a diff by drahn@. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=c7b5578a715ca65ee46f2c1c48fca8629620c8ce;p=openbsd Add kernel support for the VFP FPU/SIMD unit. Based on a diff by drahn@. This allows us to use floating-pointer and vector instructions in userland code. The current implementation assumes all 32 VFP registers are present. This should be the case on all armv7 hardware currently supported by OpenBSD. ok patrick@ --- diff --git a/sys/arch/arm/arm/cpu.c b/sys/arch/arm/arm/cpu.c index 5b6a9ce1f8a..6a23a685288 100644 --- a/sys/arch/arm/arm/cpu.c +++ b/sys/arch/arm/arm/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.44 2018/01/15 14:11:16 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.45 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: cpu.c,v 1.56 2004/04/14 04:01:49 bsh Exp $ */ @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -116,6 +117,8 @@ cpu_attach(struct device *parent, struct device *dev, void *aux) identify_arm_cpu(dev, ci); + vfp_init(); + if (OF_getproplen(faa->fa_node, "clocks") > 0) { cpu_node = faa->fa_node; cpu_cpuspeed = cpu_clockspeed; diff --git a/sys/arch/arm/arm/exception.S b/sys/arch/arm/arm/exception.S index f1bceac6864..3cf4e2c266c 100644 --- a/sys/arch/arm/arm/exception.S +++ b/sys/arch/arm/arm/exception.S @@ -1,4 +1,4 @@ -/* $OpenBSD: exception.S,v 1.6 2016/09/21 11:33:05 kettenis Exp $ */ +/* $OpenBSD: exception.S,v 1.7 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: exception.S,v 1.13 2003/10/31 16:30:15 scw Exp $ */ /* @@ -54,6 +54,14 @@ .text .align 2 +#define RESTOREVFP \ + ldr r0, [sp] /* Get the SPSR from stack */ ;\ + and r0, r0, #(PSR_MODE) /* Returning to USR mode? */ ;\ + cmp r0, #(PSR_USR32_MODE) ;\ + bne 1f ;\ + bl _C_LABEL(vfp_enable) ;\ +1: + AST_LOCALS /* @@ -194,6 +202,7 @@ Laddress_exception_msg: exception_exit: DO_AST + RESTOREVFP PULLFRAMEFROMSVCANDEXIT /* diff --git a/sys/arch/arm/arm/fault.c b/sys/arch/arm/arm/fault.c index 96c0e1b54a5..8ece22e083b 100644 --- a/sys/arch/arm/arm/fault.c +++ b/sys/arch/arm/arm/fault.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fault.c,v 1.31 2018/01/15 14:11:16 kettenis Exp $ */ +/* $OpenBSD: fault.c,v 1.32 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: fault.c,v 1.46 2004/01/21 15:39:21 skrll Exp $ */ /* @@ -99,6 +99,7 @@ #include #include #include +#include #ifdef DEBUG int last_fault_code; /* For the benefit of pmap_fault_fixup() */ @@ -188,6 +189,9 @@ data_abort_handler(trapframe_t *tf) /* Update vmmeter statistics */ uvmexp.traps++; + /* Before enabling interrupts, save FPU state */ + vfp_save(); + /* Re-enable interrupts if they were enabled previously */ if (__predict_true((tf->tf_spsr & PSR_I) == 0)) enable_interrupts(PSR_I); @@ -581,6 +585,9 @@ prefetch_abort_handler(trapframe_t *tf) if (__predict_false(!TRAP_USERMODE(tf))) dab_fatal(tf, fsr, far, NULL, NULL); + /* Before enabling interrupts, save FPU state */ + vfp_save(); + /* * Enable IRQ's (disabled by the abort) This always comes * from user mode so we know interrupts were not disabled. diff --git a/sys/arch/arm/arm/irq_dispatch.S b/sys/arch/arm/arm/irq_dispatch.S index 63ce56424db..015c7bb6cb4 100644 --- a/sys/arch/arm/arm/irq_dispatch.S +++ b/sys/arch/arm/arm/irq_dispatch.S @@ -1,4 +1,4 @@ -/* $OpenBSD: irq_dispatch.S,v 1.13 2017/01/06 00:06:02 jsg Exp $ */ +/* $OpenBSD: irq_dispatch.S,v 1.14 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: irq_dispatch.S,v 1.5 2003/10/30 08:57:24 scw Exp $ */ /* @@ -90,6 +90,9 @@ .Lcpu_info_primary: .word _C_LABEL(cpu_info_primary) +#define STOREVFP \ + bl _C_LABEL(vfp_save) + AST_LOCALS ASENTRY_NP(irq_entry) @@ -97,6 +100,8 @@ ASENTRY_NP(irq_entry) PUSHFRAMEINSVC /* Push an interrupt frame */ + STOREVFP /* save fpu state before irq handler */ + /* * Increment the interrupt nesting depth and call the interrupt * dispatch routine. We've pushed a frame, so we can safely use diff --git a/sys/arch/arm/arm/syscall.c b/sys/arch/arm/arm/syscall.c index 7ffa07e22bb..adc0c07c78a 100644 --- a/sys/arch/arm/arm/syscall.c +++ b/sys/arch/arm/arm/syscall.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall.c,v 1.18 2016/01/31 00:14:50 jsg Exp $ */ +/* $OpenBSD: syscall.c,v 1.19 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: syscall.c,v 1.24 2003/11/14 19:03:17 scw Exp $ */ /*- @@ -89,6 +89,7 @@ #include #include #include +#include #define MAXARGS 8 @@ -103,6 +104,9 @@ swi_handler(trapframe_t *frame) uvmexp.syscalls++; + /* Before enabling interrupts, save FPU state */ + vfp_save(); + /* Re-enable interrupts if they were enabled previously */ if (__predict_true((frame->tf_spsr & PSR_I) == 0)) enable_interrupts(PSR_I); diff --git a/sys/arch/arm/arm/undefined.c b/sys/arch/arm/arm/undefined.c index afe94ef85b7..2120de0ca2a 100644 --- a/sys/arch/arm/arm/undefined.c +++ b/sys/arch/arm/arm/undefined.c @@ -1,4 +1,4 @@ -/* $OpenBSD: undefined.c,v 1.10 2017/04/30 16:45:45 mpi Exp $ */ +/* $OpenBSD: undefined.c,v 1.11 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */ /* @@ -62,6 +62,7 @@ #include #include #include +#include #include @@ -150,6 +151,9 @@ undefinedinstruction(trapframe_t *frame) #endif union sigval sv; + /* Before enabling interrupts, save FPU state */ + vfp_save(); + /* Enable interrupts if they were enabled before the exception. */ if (!(frame->tf_spsr & PSR_I)) enable_interrupts(PSR_I); @@ -194,10 +198,14 @@ undefinedinstruction(trapframe_t *frame) * instruction trap. */ + coprocessor = 0; if ((fault_instruction & (1 << 27)) != 0) coprocessor = (fault_instruction >> 8) & 0x0f; - else - coprocessor = 0; + else { /* check for special instructions */ + if (((fault_instruction & 0xfe000000) == 0xf2000000) || + ((fault_instruction & 0xff100000) == 0xf4000000)) + coprocessor = 10; /* vfp / simd */ + } if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { /* diff --git a/sys/arch/arm/arm/vfp.c b/sys/arch/arm/arm/vfp.c new file mode 100644 index 00000000000..ec5b4a28504 --- /dev/null +++ b/sys/arch/arm/arm/vfp.c @@ -0,0 +1,197 @@ +/* $OpenBSD: vfp.c,v 1.1 2018/01/26 16:22:19 kettenis Exp $ */ + +/* + * Copyright (c) 2011 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +static inline void +set_vfp_fpexc(uint32_t val) +{ + __asm __volatile("vmsr fpexc, %0" :: "r" (val)); +} + +static inline uint32_t +get_vfp_fpexc(void) +{ + uint32_t val; + __asm __volatile("vmrs %0, fpexc" : "=r" (val)); + return val; +} + +int vfp_fault(unsigned int, unsigned int, trapframe_t *, int); +void vfp_load(struct proc *p); +void vfp_store(struct fpreg *vfpsave); + +void +vfp_init(void) +{ + uint32_t val; + + install_coproc_handler(10, vfp_fault); + install_coproc_handler(11, vfp_fault); + + __asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val)); + val |= COPROC10 | COPROC11; + __asm volatile("mcr p15, 0, %0, c1, c0, 2" :: "r" (val)); + __asm volatile("isb"); +} + +void +vfp_store(struct fpreg *vfpsave) +{ + uint32_t scratch; + + if (get_vfp_fpexc() & VFPEXC_EN) { + __asm __volatile( + "vstmia %1!, {d0-d15}\n" /* d0-d15 */ + "vstmia %1!, {d16-d31}\n" /* d16-d31 */ + "vmrs %0, fpscr\n" + "str %0, [%1]\n" /* save vfpscr */ + : "=&r" (scratch) : "r" (vfpsave)); + } + + /* disable FPU */ + set_vfp_fpexc(0); +} + +void +vfp_save(void) +{ + struct cpu_info *ci = curcpu(); + struct pcb *pcb = curpcb; + struct proc *p = curproc; + uint32_t cr_8; + + if (ci->ci_fpuproc == 0) + return; + + cr_8 = get_vfp_fpexc(); + + if ((cr_8 & VFPEXC_EN) == 0) + return; /* not enabled, nothing to do */ + + if (cr_8 & VFPEXC_EX) + panic("vfp exceptional data fault, time to write more code"); + + if (pcb->pcb_fpcpu == NULL || ci->ci_fpuproc == NULL || + !(pcb->pcb_fpcpu == ci && ci->ci_fpuproc == p)) { + /* disable fpu before panic, otherwise recurse */ + set_vfp_fpexc(0); + + panic("FPU unit enabled when curproc and curcpu dont agree %p %p %p %p", pcb->pcb_fpcpu, ci, ci->ci_fpuproc, p); + } + + vfp_store(&p->p_addr->u_pcb.pcb_fpstate); + + /* + * NOTE: fpu state is saved but remains 'valid', as long as + * curpcb()->pcb_fpucpu == ci && ci->ci_fpuproc == curproc() + * is true FPU state is valid and can just be enabled without reload. + */ +} + +void +vfp_enable(void) +{ + struct cpu_info *ci = curcpu(); + + if (curproc->p_addr->u_pcb.pcb_fpcpu == ci && + ci->ci_fpuproc == curproc) { + disable_interrupts(PSR_I|PSR_F); + + /* FPU state is still valid, just enable and go */ + set_vfp_fpexc(VFPEXC_EN); + } +} + +void +vfp_load(struct proc *p) +{ + struct cpu_info *ci = curcpu(); + struct pcb *pcb = &p->p_addr->u_pcb; + uint32_t scratch = 0; + int psw; + + /* do not allow a partially synced state here */ + psw = disable_interrupts(PSR_I|PSR_F); + + /* + * p->p_pcb->pcb_fpucpu _may_ not be NULL here, but the FPU state + * was synced on kernel entry, so we can steal the FPU state + * instead of signalling and waiting for it to save + */ + + /* enable to be able to load ctx */ + set_vfp_fpexc(VFPEXC_EN); + + __asm __volatile( + "vldmia %1!, {d0-d15}\n" /* d0-d15 */ + "vldmia %1!, {d16-d31}\n" /* d16-d31 */ + "ldr %0, [%1]\n" /* set old vfpscr */ + "vmsr fpscr, %0\n" + : "=&r" (scratch) : "r" (&pcb->pcb_fpstate)); + + ci->ci_fpuproc = p; + pcb->pcb_fpcpu = ci; + + /* disable until return to userland */ + set_vfp_fpexc(0); + + restore_interrupts(psw); +} + +int +vfp_fault(unsigned int pc, unsigned int insn, trapframe_t *tf, int fault_code) +{ + struct proc *p = curproc; + struct pcb *pcb = &p->p_addr->u_pcb; + + if (get_vfp_fpexc() & VFPEXC_EN) { + /* + * We probably ran into an unsupported instruction, + * like NEON on a non-NEON system. Let the process know. + */ + return 1; + } + + /* we should be able to ignore old state of pcb_fpcpu ci_fpuproc */ + if ((pcb->pcb_flags & PCB_FPU) == 0) { + pcb->pcb_flags |= PCB_FPU; + memset(&pcb->pcb_fpstate, 0, sizeof(pcb->pcb_fpstate)); + } + vfp_load(p); + + return 0; +} + +void +vfp_discard(void) +{ + struct cpu_info *ci = curcpu(); + + if (curpcb->pcb_fpcpu == ci && ci->ci_fpuproc == curproc) { + ci->ci_fpuproc = NULL; + curpcb->pcb_fpcpu = NULL; + } +} diff --git a/sys/arch/arm/arm/vm_machdep.c b/sys/arch/arm/arm/vm_machdep.c index d755b8cd59f..87e0032cfaa 100644 --- a/sys/arch/arm/arm/vm_machdep.c +++ b/sys/arch/arm/arm/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.21 2017/08/17 20:50:51 tom Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.22 2018/01/26 16:22:19 kettenis Exp $ */ /* $NetBSD: vm_machdep.c,v 1.31 2004/01/04 11:33:29 jdolecek Exp $ */ /* @@ -62,6 +62,8 @@ #include #include +#include + extern pv_addr_t systempage; int process_read_regs (struct proc *p, struct reg *regs); @@ -132,6 +134,7 @@ cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void cpu_exit(struct proc *p) { + vfp_discard(); pmap_deactivate(p); sched_exit(p); } diff --git a/sys/arch/arm/conf/files.arm b/sys/arch/arm/conf/files.arm index 9dae5a17689..b694cfe3127 100644 --- a/sys/arch/arm/conf/files.arm +++ b/sys/arch/arm/conf/files.arm @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm,v 1.46 2017/04/30 13:04:49 mpi Exp $ +# $OpenBSD: files.arm,v 1.47 2018/01/26 16:22:20 kettenis Exp $ # $NetBSD: files.arm,v 1.76 2003/11/05 12:53:15 scw Exp $ # generic networking files @@ -73,6 +73,7 @@ file arch/arm/armv7/armv7_a4x_space.c cpu_armv7 file arch/arm/armv7/armv7_a4x_io.S cpu_armv7 file arch/arm/armv7/armv7_mutex.c cpu_armv7 file arch/arm/armv7/bus_space_asm_armv7.S cpu_armv7 +file arch/arm/arm/vfp.c cpu_armv7 pseudo-device openprom file arch/arm/arm/openprom.c openprom needs-flag diff --git a/sys/arch/arm/include/cpu.h b/sys/arch/arm/include/cpu.h index 2d783aaf374..9fd273e6dcf 100644 --- a/sys/arch/arm/include/cpu.h +++ b/sys/arch/arm/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.49 2018/01/15 14:11:16 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.50 2018/01/26 16:22:20 kettenis Exp $ */ /* $NetBSD: cpu.h,v 1.34 2003/06/23 11:01:08 martin Exp $ */ /* @@ -185,6 +185,7 @@ struct cpu_info { struct schedstate_percpu ci_schedstate; /* scheduler state */ struct proc *ci_curproc; + struct proc *ci_fpuproc; u_int32_t ci_cpuid; u_int32_t ci_randseed; diff --git a/sys/arch/arm/include/fp.h b/sys/arch/arm/include/fp.h index 5d9096e0fa5..d503ea1167f 100644 --- a/sys/arch/arm/include/fp.h +++ b/sys/arch/arm/include/fp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: fp.h,v 1.1 2004/02/01 05:09:49 drahn Exp $ */ +/* $OpenBSD: fp.h,v 1.2 2018/01/26 16:22:20 kettenis Exp $ */ /* $NetBSD: fp.h,v 1.1 2001/01/10 19:02:06 bjh21 Exp $ */ /* @@ -59,19 +59,6 @@ typedef struct fp_extended_precision { typedef struct fp_extended_precision fp_reg_t; -/* - * Information about the FPE-SP state that is stored in the pcb - * - * This needs to move and be hidden from userland. - */ - -struct fpe_sp_state { - unsigned int fp_flags; - unsigned int fp_sr; - unsigned int fp_cr; - fp_reg_t fp_registers[16]; -}; - /* * Type for a saved FP context, if we want to translate the context to a * user-readable form diff --git a/sys/arch/arm/include/pcb.h b/sys/arch/arm/include/pcb.h index 1f63d2bf044..fc43b1c9a21 100644 --- a/sys/arch/arm/include/pcb.h +++ b/sys/arch/arm/include/pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcb.h,v 1.7 2016/09/24 21:02:31 patrick Exp $ */ +/* $OpenBSD: pcb.h,v 1.8 2018/01/26 16:22:20 kettenis Exp $ */ /* $NetBSD: pcb.h,v 1.10 2003/10/13 21:46:39 scw Exp $ */ /* @@ -38,9 +38,9 @@ #define _ARM_PCB_H_ #include -#include #include +#include struct trapframe; @@ -77,13 +77,14 @@ struct pcb_arm32 { */ struct pcb { u_int pcb_flags; -#define PCB_OWNFPU 0x00000001 +#define PCB_FPU 0x00000001 /* Process had FPU initialized */ struct trapframe *pcb_tf; caddr_t pcb_onfault; /* On fault handler */ union { struct pcb_arm32 un_32; } pcb_un; - struct fpe_sp_state pcb_fpstate; /* Floating Point state */ + struct fpreg pcb_fpstate; /* Floating Point state */ + struct cpu_info *pcb_fpcpu; void *pcb_tcb; }; diff --git a/sys/arch/arm/include/vfp.h b/sys/arch/arm/include/vfp.h new file mode 100644 index 00000000000..511be722ec0 --- /dev/null +++ b/sys/arch/arm/include/vfp.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012 Mark Tinguely + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * $FreeBSD$ + */ + + +#ifndef _MACHINE__VFP_H_ +#define _MACHINE__VFP_H_ + +#ifdef _KERNEL +/* Only kernel defines exist here */ + +/* fpsid, fpscr, fpexc are defined in the newer gas */ +#define VFPSID cr0 +#define VFPSCR cr1 +#define VMVFR1 cr6 +#define VMVFR0 cr7 +#define VFPEXC cr8 +#define VFPINST cr9 /* vfp 1 and 2 except instruction */ +#define VFPINST2 cr10 /* vfp 2? */ + +/* VFPSID */ +#define VFPSID_IMPLEMENTOR_OFF 24 +#define VFPSID_IMPLEMENTOR_MASK (0xff000000) +#define VFPSID_HARDSOFT_IMP (0x00800000) +#define VFPSID_SINGLE_PREC 20 /* version 1 and 2 */ +#define VFPSID_SUBVERSION_OFF 16 +#define VFPSID_SUBVERSION2_MASK (0x000f0000) /* version 1 and 2 */ +#define VFPSID_SUBVERSION3_MASK (0x007f0000) /* version 3 */ +#define VFP_ARCH3 (0x00030000) +#define VFPSID_PARTNUMBER_OFF 8 +#define VFPSID_PARTNUMBER_MASK (0x0000ff00) +#define VFPSID_VARIANT_OFF 4 +#define VFPSID_VARIANT_MASK (0x000000f0) +#define VFPSID_REVISION_MASK 0x0f + +/* VFPSCR */ +#define VFPSCR_CC_N (0x80000000) /* comparison less than */ +#define VFPSCR_CC_Z (0x40000000) /* comparison equal */ +#define VFPSCR_CC_C (0x20000000) /* comparison = > unordered */ +#define VFPSCR_CC_V (0x10000000) /* comparison unordered */ +#define VFPSCR_QC (0x08000000) /* saturation cululative */ +#define VFPSCR_DN (0x02000000) /* default NaN enable */ +#define VFPSCR_FZ (0x01000000) /* flush to zero enabled */ + +#define VFPSCR_RMODE_OFF 22 /* rounding mode offset */ +#define VFPSCR_RMODE_MASK (0x00c00000) /* rounding mode mask */ +#define VFPSCR_RMODE_RN (0x00000000) /* round nearest */ +#define VFPSCR_RMODE_RPI (0x00400000) /* round to plus infinity */ +#define VFPSCR_RMODE_RNI (0x00800000) /* round to neg infinity */ +#define VFPSCR_RMODE_RM (0x00c00000) /* round to zero */ + +#define VFPSCR_STRIDE_OFF 20 /* vector stride -1 */ +#define VFPSCR_STRIDE_MASK (0x00300000) +#define VFPSCR_LEN_OFF 16 /* vector length -1 */ +#define VFPSCR_LEN_MASK (0x00070000) +#define VFPSCR_IDE (0x00008000) /* input subnormal exc enable */ +#define VFPSCR_IXE (0x00001000) /* inexact exception enable */ +#define VFPSCR_UFE (0x00000800) /* underflow exception enable */ +#define VFPSCR_OFE (0x00000400) /* overflow exception enable */ +#define VFPSCR_DNZ (0x00000200) /* div by zero exception en */ +#define VFPSCR_IOE (0x00000100) /* invalid op exec enable */ +#define VFPSCR_IDC (0x00000080) /* input subnormal cumul */ +#define VFPSCR_IXC (0x00000010) /* Inexact cumulative flag */ +#define VFPSCR_UFC (0x00000008) /* underflow cumulative flag */ +#define VFPSCR_OFC (0x00000004) /* overflow cumulative flag */ +#define VFPSCR_DZC (0x00000002) /* division by zero flag */ +#define VFPSCR_IOC (0x00000001) /* invalid operation cumul */ + +/* VFPEXC */ +#define VFPEXC_EX (0x80000000) /* exception v1 v2 */ +#define VFPEXC_EN (0x40000000) /* vfp enable */ + +/* version 3 registers */ +/* VMVFR0 */ +#define VMVFR0_RM_OFF 28 +#define VMVFR0_RM_MASK (0xf0000000) /* VFP rounding modes */ + +#define VMVFR0_SV_OFF 24 +#define VMVFR0_SV_MASK (0x0f000000) /* VFP short vector supp */ +#define VMVFR0_SR_OFF 20 +#define VMVFR0_SR (0x00f00000) /* VFP hw sqrt supp */ +#define VMVFR0_D_OFF 16 +#define VMVFR0_D_MASK (0x000f0000) /* VFP divide supp */ +#define VMVFR0_TE_OFF 12 +#define VMVFR0_TE_MASK (0x0000f000) /* VFP trap exception supp */ +#define VMVFR0_DP_OFF 8 +#define VMVFR0_DP_MASK (0x00000f00) /* VFP double prec support */ +#define VMVFR0_SP_OFF 4 +#define VMVFR0_SP_MASK (0x000000f0) /* VFP single prec support */ +#define VMVFR0_RB_MASK (0x0000000f) /* VFP 64 bit media support */ + +/* VMVFR1 */ +#define VMVFR1_SP_OFF 16 +#define VMVFR1_SP_MASK (0x000f0000) /* Neon single prec support */ +#define VMVFR1_I_OFF 12 +#define VMVFR1_I_MASK (0x0000f000) /* Neon integer support */ +#define VMVFR1_LS_OFF 8 +#define VMVFR1_LS_MASK (0x00000f00) /* Neon ld/st instr support */ +#define VMVFR1_DN_OFF 4 +#define VMVFR1_DN_MASK (0x000000f0) /* Neon prop NaN support */ +#define VMVFR1_FZ_MASK (0x0000000f) /* Neon denormal arith supp */ + +#define COPROC10 (0x3 << 20) +#define COPROC11 (0x3 << 22) + +void vfp_init(void); +void vfp_discard(void); +void vfp_save(void); +void vfp_enable(void); + +#endif /* _KERNEL */ +#endif /* _MACHINE__VFP_H_ */