/* $Id$ */ /* Altivec support for RTEMS; vector register context management. */ /* * Authorship * ---------- * This software was created by * Till Straumann , 2009, * Stanford Linear Accelerator Center, Stanford University. * * Acknowledgement of sponsorship * ------------------------------ * This software was produced by * the Stanford Linear Accelerator Center, Stanford University, * under Contract DE-AC03-76SFO0515 with the Department of Energy. * * Government disclaimer of liability * ---------------------------------- * Neither the United States nor the United States Department of Energy, * nor any of their employees, makes any warranty, express or implied, or * assumes any legal liability or responsibility for the accuracy, * completeness, or usefulness of any data, apparatus, product, or process * disclosed, or represents that its use would not infringe privately owned * rights. * * Stanford disclaimer of liability * -------------------------------- * Stanford University makes no representations or warranties, express or * implied, nor assumes any liability for the use of this software. * * Stanford disclaimer of copyright * -------------------------------- * Stanford University, owner of the copyright, hereby disclaims its * copyright and all other rights in this software. Hence, anyone may * freely use it for any purpose without restriction. * * Maintenance of notices * ---------------------- * In the interest of clarity regarding the origin and status of this * SLAC software, this and all the preceding Stanford University notices * are to remain affixed to any copy or derivative of this software made * or distributed by the recipient and are to be affixed to any copy of * software made or distributed by the recipient that contains a copy or * derivative of this software. * * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 */ #ifdef __ALTIVEC__ #include #include #include #include #include #include #define STATIC static #define VEC_ALIGNMENT 16 #define NAM "AltiVec Support" #define ERRID(a,b,c,d) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)) typedef uint32_t _vu32 __attribute__((vector_size(VEC_ALIGNMENT))); #ifndef MSR_VE #define MSR_VE (1<<(31-6)) #endif /* NOTE: These two variables are accessed by assembly code * which assumes 32-bit data! */ uint32_t _CPU_altivec_ctxt_off = 0; uint32_t _CPU_altivec_psim_cpu = 0; static inline uint32_t mfmsr(void) { uint32_t v; _CPU_MSR_GET(v); return v; } static inline void mtmsr(uint32_t v) { _CPU_MSR_SET(v); } static inline void isync(void) { asm volatile("isync"); } static inline void dssall(void) { if ( !_CPU_altivec_psim_cpu) asm volatile("dssall"); } static inline uint32_t set_MSR_VE(void) { uint32_t rval; rval=mfmsr(); if ( ! (MSR_VE & rval ) ) { mtmsr(rval | MSR_VE); isync(); } return rval; } static inline void clr_MSR_VE(void) { dssall(); mtmsr(mfmsr() & ~MSR_VE); isync(); } static inline void rst_MSR_VE(uint32_t old) { if ( ! ( MSR_VE & old ) ) { dssall(); mtmsr(old); isync(); } } /* Code to probe the compiler's stack alignment (PowerPC); * The routine determines at run-time if the compiler generated * 8 or 16-byte aligned code. * * Till Straumann , 2005 */ static void dummy(void) __attribute__((noinline)); /* add (empty) asm-statement to make sure this isn't optimized away */ static void dummy(void) { __asm__ volatile(""); } static unsigned probe_r1(void) __attribute__((noinline)); static unsigned probe_r1(void) { unsigned r1; /* call something to enforce creation of a minimal stack frame; * (8 bytes: r1 and lr space for 'dummy' callee). If compiled * with -meabi -mno-altivec gcc allocates 8 bytes, if -mno-eabi * or -maltivec / -mabi=altivec then gcc allocates 16 bytes * according to the sysv / altivec ABI specs. */ dummy(); /* return stack pointer */ asm volatile("mr %0,1":"=r"(r1)); return r1; } static unsigned probe_ppc_stack_alignment(void) { unsigned r1; asm volatile("mr %0,1":"=r"(r1)); return (r1 - probe_r1()) & ~ 0xf; } STATIC int check_stack_alignment(void) { int rval = 0; if ( VEC_ALIGNMENT > PPC_STACK_ALIGNMENT ) { printk(NAM": CPU support has unsufficient stack alignment;\n"); printk("modify 'cpukit/score/cpu/powerpc/rtems/score/powerpc.h'\n"); printk("and choose PPC_ABI_SVR4. I'll enable a workaround for now.\n"); rval |= 1; } /* Run-time check; should compile with -mabi=altivec */ if ( probe_ppc_stack_alignment() < VEC_ALIGNMENT ) { printk(NAM": run-time stack alignment unsufficient; make sure you compile with -mabi=altivec\n"); rval |= 2; } return rval; } static uint32_t probe_vrsave(_vu32 *p_v) __attribute__((noinline)); /* Check if this code was compiled with -mvrsave=yes or no * so that we can set the default/init value accordingly. */ static uint32_t probe_vrsave(_vu32 *p_v) { _vu32 x; uint32_t vrsave; /* Explicitly clobber a volatile vector reg (0) that is * not used to pass return values. * If -mvrsave=yes was used this should cause gcc to * set bit 0 in vrsave. OTOH this bit cannot be set * because v0 is volatile and not used to pass a value * to the caller... */ asm volatile("vxor %0, 0, 0; mfvrsave %1":"=v"(x),"=r"(vrsave)::"v0"); if ( p_v ) { *p_v = x; } return vrsave; } static int vrsave_yes(void) __attribute__((noinline)); static int vrsave_yes(void) { uint32_t vrsave_pre; asm volatile("mfvrsave %0":"=r"(vrsave_pre)); if ( (vrsave_pre & 0x80000000) ) { printk(NAM": WARNING - unable to determine whether -mvrsave was used; assuming NO\n"); return 0; } return probe_vrsave(0) != vrsave_pre; } extern void _CPU_altivec_set_vrsave_initval(uint32_t); void _CPU_Initialize_altivec(void) { unsigned pvr; /* I don't like to have to #define the offset of the altivec area * for use by assembly code. * Therefore, we compute it here and store it in memory... */ _CPU_altivec_ctxt_off = (uint32_t) &((Context_Control*)0)->altivec; /* * Add space possibly needed for alignment */ _CPU_altivec_ctxt_off += PPC_CACHE_ALIGNMENT - 1; if ( ! vrsave_yes() ) { /* They seemed to compile with -mvrsave=no. Hence we * must set VRSAVE so that all registers are saved/restored * in case this support was not built with IGNORE_VRSAVE. */ _CPU_altivec_set_vrsave_initval( -1 ); } if ( check_stack_alignment() & 2 ) rtems_fatal_error_occurred(ERRID('V','E','C','1')); pvr = get_ppc_cpu_type(); /* psim has altivec but lacks the streaming instructions :-( */ _CPU_altivec_psim_cpu = (PPC_PSIM == pvr); if ( ! ppc_cpu_has_altivec() ) { printk(NAM": This CPU seems not to have AltiVec\n"); rtems_panic("Unable to initialize AltiVec Support\n"); } if ( ! (mfmsr() & MSR_VE) ) { printk(NAM": Warning: BSP should set MSR_VE early; doing it now...\n"); set_MSR_VE(); } } #endif