1 | /* $Id$ */ |
---|
2 | |
---|
3 | /* Altivec support for RTEMS; vector register context management. |
---|
4 | * This is implemented as a user extension. |
---|
5 | * |
---|
6 | * Author: Till Straumann <strauman@slac.stanford.edu>, 2005 |
---|
7 | */ |
---|
8 | |
---|
9 | #ifdef __ALTIVEC__ |
---|
10 | |
---|
11 | #include <rtems.h> |
---|
12 | #include <libcpu/cpuIdent.h> |
---|
13 | #include <rtems/bspIo.h> |
---|
14 | #include <rtems/error.h> |
---|
15 | #include <rtems/score/cpu.h> |
---|
16 | #include <rtems/powerpc/powerpc.h> |
---|
17 | |
---|
18 | #define STATIC static |
---|
19 | |
---|
20 | #define VEC_ALIGNMENT 16 |
---|
21 | |
---|
22 | #define NAM "AltiVec Support" |
---|
23 | #define ERRID(a,b,c,d) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)) |
---|
24 | |
---|
25 | typedef uint32_t _vu32 __attribute__((vector_size(VEC_ALIGNMENT))); |
---|
26 | |
---|
27 | #ifndef MSR_VE |
---|
28 | #define MSR_VE (1<<(31-6)) |
---|
29 | #endif |
---|
30 | |
---|
31 | /* NOTE: These two variables are accessed by assembly code |
---|
32 | * which assumes 32-bit data! |
---|
33 | */ |
---|
34 | uint32_t _CPU_altivec_ctxt_off = 0; |
---|
35 | uint32_t _CPU_altivec_psim_cpu = 0; |
---|
36 | |
---|
37 | static inline uint32_t |
---|
38 | mfmsr(void) |
---|
39 | { |
---|
40 | uint32_t v; |
---|
41 | _CPU_MSR_GET(v); |
---|
42 | return v; |
---|
43 | } |
---|
44 | |
---|
45 | static inline void |
---|
46 | mtmsr(uint32_t v) |
---|
47 | { |
---|
48 | _CPU_MSR_SET(v); |
---|
49 | } |
---|
50 | |
---|
51 | static inline void |
---|
52 | isync(void) |
---|
53 | { |
---|
54 | asm volatile("isync"); |
---|
55 | } |
---|
56 | |
---|
57 | static inline void |
---|
58 | dssall(void) |
---|
59 | { |
---|
60 | if ( !_CPU_altivec_psim_cpu) |
---|
61 | asm volatile("dssall"); |
---|
62 | } |
---|
63 | |
---|
64 | static inline uint32_t |
---|
65 | set_MSR_VE(void) |
---|
66 | { |
---|
67 | uint32_t rval; |
---|
68 | rval=mfmsr(); |
---|
69 | if ( ! (MSR_VE & rval ) ) { |
---|
70 | mtmsr(rval | MSR_VE); |
---|
71 | isync(); |
---|
72 | } |
---|
73 | return rval; |
---|
74 | } |
---|
75 | |
---|
76 | static inline void |
---|
77 | clr_MSR_VE(void) |
---|
78 | { |
---|
79 | dssall(); |
---|
80 | mtmsr(mfmsr() & ~MSR_VE); |
---|
81 | isync(); |
---|
82 | } |
---|
83 | |
---|
84 | static inline void |
---|
85 | rst_MSR_VE(uint32_t old) |
---|
86 | { |
---|
87 | if ( ! ( MSR_VE & old ) ) { |
---|
88 | dssall(); |
---|
89 | mtmsr(old); |
---|
90 | isync(); |
---|
91 | } |
---|
92 | } |
---|
93 | |
---|
94 | |
---|
95 | /* Code to probe the compiler's stack alignment (PowerPC); |
---|
96 | * The routine determines at run-time if the compiler generated |
---|
97 | * 8 or 16-byte aligned code. |
---|
98 | * |
---|
99 | * Till Straumann <strauman@slac.stanford.edu>, 2005 |
---|
100 | */ |
---|
101 | |
---|
102 | static void dummy(void) __attribute__((noinline)); |
---|
103 | /* add (empty) asm statement to make sure this isn't optimized away */ |
---|
104 | static void dummy(void) { asm volatile(""); } |
---|
105 | |
---|
106 | static unsigned probe_r1(void) __attribute__((noinline)); |
---|
107 | static unsigned probe_r1(void) |
---|
108 | { |
---|
109 | unsigned r1; |
---|
110 | /* call something to enforce creation of a minimal stack frame; |
---|
111 | * (8 bytes: r1 and lr space for 'dummy' callee). If compiled |
---|
112 | * with -meabi -mno-altivec gcc allocates 8 bytes, if -mno-eabi |
---|
113 | * or -maltivec / -mabi=altivec then gcc allocates 16 bytes |
---|
114 | * according to the sysv / altivec ABI specs. |
---|
115 | */ |
---|
116 | dummy(); |
---|
117 | /* return stack pointer */ |
---|
118 | asm volatile("mr %0,1":"=r"(r1)); |
---|
119 | return r1; |
---|
120 | } |
---|
121 | |
---|
122 | static unsigned |
---|
123 | probe_ppc_stack_alignment(void) |
---|
124 | { |
---|
125 | unsigned r1; |
---|
126 | asm volatile("mr %0,1":"=r"(r1)); |
---|
127 | return (r1 - probe_r1()) & ~ 0xf; |
---|
128 | } |
---|
129 | |
---|
130 | STATIC int check_stack_alignment(void) |
---|
131 | { |
---|
132 | int rval = 0; |
---|
133 | if ( VEC_ALIGNMENT > PPC_STACK_ALIGNMENT ) { |
---|
134 | printk(NAM": CPU support has unsufficient stack alignment;\n"); |
---|
135 | printk("modify 'cpukit/score/cpu/powerpc/rtems/score/powerpc.h'\n"); |
---|
136 | printk("and choose PPC_ABI_SVR4. I'll enable a workaround for now.\n"); |
---|
137 | rval |= 1; |
---|
138 | } |
---|
139 | /* Run-time check; should compile with -mabi=altivec */ |
---|
140 | if ( probe_ppc_stack_alignment() < VEC_ALIGNMENT ) { |
---|
141 | printk(NAM": run-time stack alignment unsufficient; make sure you compile with -mabi=altivec\n"); |
---|
142 | rval |= 2; |
---|
143 | } |
---|
144 | return rval; |
---|
145 | } |
---|
146 | |
---|
147 | |
---|
148 | static uint32_t probe_vrsave(_vu32 *p_v) __attribute__((noinline)); |
---|
149 | |
---|
150 | /* Check if this code was compiled with -mvrsave=yes or no |
---|
151 | * so that we can set the default/init value accordingly. |
---|
152 | */ |
---|
153 | static uint32_t probe_vrsave(_vu32 *p_v) |
---|
154 | { |
---|
155 | _vu32 x; |
---|
156 | uint32_t vrsave; |
---|
157 | /* Explicitly clobber a volatile vector reg (0) that is |
---|
158 | * not used to pass return values. |
---|
159 | * If -mvrsave=yes was used this should cause gcc to |
---|
160 | * set bit 0 in vrsave. OTOH this bit cannot be set |
---|
161 | * because v0 is volatile and not used to pass a value |
---|
162 | * to the caller... |
---|
163 | */ |
---|
164 | asm volatile("vxor %0, 0, 0; mfvrsave %1":"=v"(x),"=r"(vrsave)::"v0"); |
---|
165 | if ( p_v ) { |
---|
166 | *p_v = x; |
---|
167 | } |
---|
168 | return vrsave; |
---|
169 | } |
---|
170 | |
---|
171 | static int vrsave_yes(void) __attribute__((noinline)); |
---|
172 | |
---|
173 | static int vrsave_yes(void) |
---|
174 | { |
---|
175 | uint32_t vrsave_pre; |
---|
176 | asm volatile("mfvrsave %0":"=r"(vrsave_pre)); |
---|
177 | if ( (vrsave_pre & 0x80000000) ) { |
---|
178 | printk(NAM": WARNING - unable to determine whether -mvrsave was used; assuming NO\n"); |
---|
179 | return 0; |
---|
180 | } |
---|
181 | return probe_vrsave(0) != vrsave_pre; |
---|
182 | } |
---|
183 | |
---|
184 | extern void |
---|
185 | _CPU_altivec_set_vrsave_initval(uint32_t); |
---|
186 | |
---|
187 | |
---|
188 | void |
---|
189 | _CPU_Initialize_altivec(void) |
---|
190 | { |
---|
191 | unsigned pvr; |
---|
192 | |
---|
193 | /* I don't like to have to #define the offset of the altivec area |
---|
194 | * for use by assembly code. |
---|
195 | * Therefore, we compute it here and store it in memory... |
---|
196 | */ |
---|
197 | _CPU_altivec_ctxt_off = (uint32_t) &((Context_Control*)0)->altivec; |
---|
198 | /* |
---|
199 | * Add space possibly needed for alignment |
---|
200 | */ |
---|
201 | _CPU_altivec_ctxt_off += PPC_CACHE_ALIGNMENT - 1; |
---|
202 | |
---|
203 | if ( ! vrsave_yes() ) { |
---|
204 | /* They seemed to compile with -mvrsave=no. Hence we |
---|
205 | * must set VRSAVE so that all registers are saved/restored |
---|
206 | * in case this support was not built with IGNORE_VRSAVE. |
---|
207 | */ |
---|
208 | _CPU_altivec_set_vrsave_initval( -1 ); |
---|
209 | } |
---|
210 | |
---|
211 | if ( check_stack_alignment() & 2 ) |
---|
212 | rtems_fatal_error_occurred(ERRID('V','E','C','1')); |
---|
213 | |
---|
214 | pvr = get_ppc_cpu_type(); |
---|
215 | /* psim has altivec but lacks the streaming instructions :-( */ |
---|
216 | _CPU_altivec_psim_cpu = (PPC_PSIM == pvr); |
---|
217 | |
---|
218 | if ( ! ppc_cpu_has_altivec() ) { |
---|
219 | printk(NAM": This CPU seems not to have AltiVec\n"); |
---|
220 | rtems_panic("Unable to initialize AltiVec Support\n"); |
---|
221 | } |
---|
222 | |
---|
223 | if ( ! (mfmsr() & MSR_VE) ) { |
---|
224 | printk(NAM": Warning: BSP should set MSR_VE early; doing it now...\n"); |
---|
225 | set_MSR_VE(); |
---|
226 | } |
---|
227 | } |
---|
228 | #endif |
---|