1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @ingroup ppc_exc |
---|
5 | * |
---|
6 | * @brief PowerPC Exceptions implementation. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Copyright (C) 1999 Eric Valette (valette@crf.canon.fr) |
---|
11 | * Canon Centre Recherche France. |
---|
12 | * |
---|
13 | * Copyright (C) 2007 Till Straumann <strauman@slac.stanford.edu> |
---|
14 | * |
---|
15 | * Copyright (C) 2009-2012 embedded brains GmbH. |
---|
16 | * |
---|
17 | * Derived from file "libcpu/powerpc/new-exceptions/bspsupport/vectors_init.c". |
---|
18 | * Derived from file "libcpu/powerpc/new-exceptions/e500_raw_exc_init.c". |
---|
19 | * |
---|
20 | * The license and distribution terms for this file may be |
---|
21 | * found in the file LICENSE in this distribution or at |
---|
22 | * http://www.rtems.com/license/LICENSE. |
---|
23 | */ |
---|
24 | |
---|
25 | #include <rtems.h> |
---|
26 | |
---|
27 | #include <bsp/vectors.h> |
---|
28 | #include <bsp/bootcard.h> |
---|
29 | |
---|
30 | #define PPC_EXC_ASSERT_OFFSET(field, off) \ |
---|
31 | RTEMS_STATIC_ASSERT( \ |
---|
32 | offsetof(CPU_Exception_frame, field) + FRAME_LINK_SPACE == off, \ |
---|
33 | CPU_Exception_frame_offset_ ## field \ |
---|
34 | ) |
---|
35 | |
---|
36 | #define PPC_EXC_ASSERT_CANONIC_OFFSET(field) \ |
---|
37 | PPC_EXC_ASSERT_OFFSET(field, field ## _OFFSET) |
---|
38 | |
---|
39 | PPC_EXC_ASSERT_OFFSET(EXC_SRR0, SRR0_FRAME_OFFSET); |
---|
40 | PPC_EXC_ASSERT_OFFSET(EXC_SRR1, SRR1_FRAME_OFFSET); |
---|
41 | PPC_EXC_ASSERT_OFFSET(_EXC_number, EXCEPTION_NUMBER_OFFSET); |
---|
42 | PPC_EXC_ASSERT_CANONIC_OFFSET(EXC_CR); |
---|
43 | PPC_EXC_ASSERT_CANONIC_OFFSET(EXC_CTR); |
---|
44 | PPC_EXC_ASSERT_CANONIC_OFFSET(EXC_XER); |
---|
45 | PPC_EXC_ASSERT_CANONIC_OFFSET(EXC_LR); |
---|
46 | #ifdef __SPE__ |
---|
47 | PPC_EXC_ASSERT_OFFSET(EXC_SPEFSCR, PPC_EXC_SPEFSCR_OFFSET); |
---|
48 | PPC_EXC_ASSERT_OFFSET(EXC_ACC, PPC_EXC_ACC_OFFSET); |
---|
49 | #endif |
---|
50 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR0); |
---|
51 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR1); |
---|
52 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR2); |
---|
53 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR3); |
---|
54 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR4); |
---|
55 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR5); |
---|
56 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR6); |
---|
57 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR7); |
---|
58 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR8); |
---|
59 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR9); |
---|
60 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR10); |
---|
61 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR11); |
---|
62 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR12); |
---|
63 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR13); |
---|
64 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR14); |
---|
65 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR15); |
---|
66 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR16); |
---|
67 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR17); |
---|
68 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR18); |
---|
69 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR19); |
---|
70 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR20); |
---|
71 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR21); |
---|
72 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR22); |
---|
73 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR23); |
---|
74 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR24); |
---|
75 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR25); |
---|
76 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR26); |
---|
77 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR27); |
---|
78 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR28); |
---|
79 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR29); |
---|
80 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR30); |
---|
81 | PPC_EXC_ASSERT_CANONIC_OFFSET(GPR31); |
---|
82 | |
---|
83 | RTEMS_STATIC_ASSERT( |
---|
84 | PPC_EXC_MINIMAL_FRAME_SIZE % CPU_STACK_ALIGNMENT == 0, |
---|
85 | PPC_EXC_MINIMAL_FRAME_SIZE |
---|
86 | ); |
---|
87 | |
---|
88 | RTEMS_STATIC_ASSERT( |
---|
89 | PPC_EXC_FRAME_SIZE % CPU_STACK_ALIGNMENT == 0, |
---|
90 | PPC_EXC_FRAME_SIZE |
---|
91 | ); |
---|
92 | |
---|
93 | RTEMS_STATIC_ASSERT( |
---|
94 | sizeof(CPU_Exception_frame) + FRAME_LINK_SPACE <= PPC_EXC_FRAME_SIZE, |
---|
95 | CPU_Exception_frame |
---|
96 | ); |
---|
97 | |
---|
98 | uint32_t ppc_exc_cache_wb_check = 1; |
---|
99 | |
---|
100 | #define MTIVPR(prefix) __asm__ volatile ("mtivpr %0" : : "r" (prefix)) |
---|
101 | #define MTIVOR(x, vec) __asm__ volatile ("mtivor"#x" %0" : : "r" (vec)) |
---|
102 | |
---|
103 | static void ppc_exc_initialize_booke(void) |
---|
104 | { |
---|
105 | /* Interupt vector prefix register */ |
---|
106 | MTIVPR(ppc_exc_vector_base); |
---|
107 | |
---|
108 | if ( |
---|
109 | ppc_cpu_is_specific_e200(PPC_e200z0) |
---|
110 | || ppc_cpu_is_specific_e200(PPC_e200z1) |
---|
111 | ) { |
---|
112 | /* |
---|
113 | * These cores have hard wired IVOR registers. An access will case a |
---|
114 | * program exception. |
---|
115 | */ |
---|
116 | return; |
---|
117 | } |
---|
118 | |
---|
119 | /* Interupt vector offset registers */ |
---|
120 | MTIVOR(0, ppc_exc_vector_address(ASM_BOOKE_CRIT_VECTOR)); |
---|
121 | MTIVOR(1, ppc_exc_vector_address(ASM_MACH_VECTOR)); |
---|
122 | MTIVOR(2, ppc_exc_vector_address(ASM_PROT_VECTOR)); |
---|
123 | MTIVOR(3, ppc_exc_vector_address(ASM_ISI_VECTOR)); |
---|
124 | MTIVOR(4, ppc_exc_vector_address(ASM_EXT_VECTOR)); |
---|
125 | MTIVOR(5, ppc_exc_vector_address(ASM_ALIGN_VECTOR)); |
---|
126 | MTIVOR(6, ppc_exc_vector_address(ASM_PROG_VECTOR)); |
---|
127 | MTIVOR(7, ppc_exc_vector_address(ASM_FLOAT_VECTOR)); |
---|
128 | MTIVOR(8, ppc_exc_vector_address(ASM_SYS_VECTOR)); |
---|
129 | MTIVOR(9, ppc_exc_vector_address(ASM_BOOKE_APU_VECTOR)); |
---|
130 | MTIVOR(10, ppc_exc_vector_address(ASM_BOOKE_DEC_VECTOR)); |
---|
131 | MTIVOR(11, ppc_exc_vector_address(ASM_BOOKE_FIT_VECTOR)); |
---|
132 | MTIVOR(12, ppc_exc_vector_address(ASM_BOOKE_WDOG_VECTOR)); |
---|
133 | MTIVOR(13, ppc_exc_vector_address(ASM_BOOKE_DTLBMISS_VECTOR)); |
---|
134 | MTIVOR(14, ppc_exc_vector_address(ASM_BOOKE_ITLBMISS_VECTOR)); |
---|
135 | MTIVOR(15, ppc_exc_vector_address(ASM_BOOKE_DEBUG_VECTOR)); |
---|
136 | if (ppc_cpu_is_e200() || ppc_cpu_is_e500()) { |
---|
137 | MTIVOR(32, ppc_exc_vector_address(ASM_E500_SPE_UNAVAILABLE_VECTOR)); |
---|
138 | MTIVOR(33, ppc_exc_vector_address(ASM_E500_EMB_FP_DATA_VECTOR)); |
---|
139 | MTIVOR(34, ppc_exc_vector_address(ASM_E500_EMB_FP_ROUND_VECTOR)); |
---|
140 | } |
---|
141 | if (ppc_cpu_is_specific_e200(PPC_e200z7) || ppc_cpu_is_e500()) { |
---|
142 | MTIVOR(35, ppc_exc_vector_address(ASM_E500_PERFMON_VECTOR)); |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | static void ppc_exc_fatal_error(void) |
---|
147 | { |
---|
148 | rtems_fatal( |
---|
149 | RTEMS_FATAL_SOURCE_BSP_GENERIC, |
---|
150 | BSP_GENERIC_FATAL_EXCEPTION_INITIALIZATION |
---|
151 | ); |
---|
152 | } |
---|
153 | |
---|
154 | void ppc_exc_initialize( |
---|
155 | uint32_t interrupt_disable_mask, |
---|
156 | uintptr_t interrupt_stack_begin, |
---|
157 | uintptr_t interrupt_stack_size |
---|
158 | ) |
---|
159 | { |
---|
160 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
161 | const ppc_exc_categories *const categories = ppc_exc_current_categories(); |
---|
162 | uintptr_t const interrupt_stack_end = interrupt_stack_begin + interrupt_stack_size; |
---|
163 | uintptr_t interrupt_stack_pointer = interrupt_stack_end - PPC_MINIMUM_STACK_FRAME_SIZE; |
---|
164 | unsigned vector = 0; |
---|
165 | uint32_t sda_base = 0; |
---|
166 | uint32_t r13 = 0; |
---|
167 | |
---|
168 | if (categories == NULL) { |
---|
169 | ppc_exc_fatal_error(); |
---|
170 | } |
---|
171 | |
---|
172 | /* Assembly code needs SDA_BASE in r13 (SVR4 or EABI). Make sure |
---|
173 | * early init code put it there. |
---|
174 | */ |
---|
175 | __asm__ volatile ( |
---|
176 | "lis %0, _SDA_BASE_@h\n" |
---|
177 | "ori %0, %0, _SDA_BASE_@l\n" |
---|
178 | "mr %1, 13\n" |
---|
179 | : "=r" (sda_base), "=r"(r13) |
---|
180 | ); |
---|
181 | |
---|
182 | if (sda_base != r13) { |
---|
183 | ppc_exc_fatal_error(); |
---|
184 | } |
---|
185 | |
---|
186 | /* Ensure proper interrupt stack alignment */ |
---|
187 | interrupt_stack_pointer &= ~((uintptr_t) CPU_STACK_ALIGNMENT - 1); |
---|
188 | |
---|
189 | /* Tag interrupt stack bottom */ |
---|
190 | *(uint32_t *) interrupt_stack_pointer = 0; |
---|
191 | |
---|
192 | /* Move interrupt stack values to special purpose registers */ |
---|
193 | PPC_SET_SPECIAL_PURPOSE_REGISTER(SPRG1, interrupt_stack_pointer); |
---|
194 | PPC_SET_SPECIAL_PURPOSE_REGISTER(SPRG2, interrupt_stack_begin); |
---|
195 | |
---|
196 | ppc_interrupt_set_disable_mask(interrupt_disable_mask); |
---|
197 | |
---|
198 | #ifndef PPC_EXC_CONFIG_BOOKE_ONLY |
---|
199 | |
---|
200 | /* Use current MMU / RI settings when running C exception handlers */ |
---|
201 | ppc_exc_msr_bits = ppc_machine_state_register() & (MSR_DR | MSR_IR | MSR_RI); |
---|
202 | |
---|
203 | #ifdef __ALTIVEC__ |
---|
204 | /* Need vector unit enabled to save/restore altivec context */ |
---|
205 | ppc_exc_msr_bits |= MSR_VE; |
---|
206 | #endif |
---|
207 | |
---|
208 | #endif /* PPC_EXC_CONFIG_BOOKE_ONLY */ |
---|
209 | |
---|
210 | if (ppc_cpu_is_bookE() == PPC_BOOKE_STD || ppc_cpu_is_bookE() == PPC_BOOKE_E500) { |
---|
211 | ppc_exc_initialize_booke(); |
---|
212 | } |
---|
213 | |
---|
214 | for (vector = 0; vector <= LAST_VALID_EXC; ++vector) { |
---|
215 | ppc_exc_category category = ppc_exc_category_for_vector(categories, vector); |
---|
216 | |
---|
217 | if (category != PPC_EXC_INVALID) { |
---|
218 | void *const vector_address = ppc_exc_vector_address(vector); |
---|
219 | uint32_t prologue [16]; |
---|
220 | size_t prologue_size = sizeof(prologue); |
---|
221 | |
---|
222 | sc = ppc_exc_make_prologue(vector, category, prologue, &prologue_size); |
---|
223 | if (sc != RTEMS_SUCCESSFUL) { |
---|
224 | ppc_exc_fatal_error(); |
---|
225 | } |
---|
226 | |
---|
227 | ppc_code_copy(vector_address, prologue, prologue_size); |
---|
228 | } |
---|
229 | } |
---|
230 | |
---|
231 | #ifndef PPC_EXC_CONFIG_BOOKE_ONLY |
---|
232 | /* If we are on a classic PPC with MSR_DR enabled then |
---|
233 | * assert that the mapping for at least this task's |
---|
234 | * stack is write-back-caching enabled (see README/CAVEATS) |
---|
235 | * Do this only if the cache is physically enabled. |
---|
236 | * Since it is not easy to figure that out in a |
---|
237 | * generic way we need help from the BSP: BSPs |
---|
238 | * which run entirely w/o the cache may set |
---|
239 | * ppc_exc_cache_wb_check to zero prior to calling |
---|
240 | * this routine. |
---|
241 | * |
---|
242 | * We run this check only after exception handling is |
---|
243 | * initialized so that we have some chance to get |
---|
244 | * information printed if it fails. |
---|
245 | * |
---|
246 | * Note that it is unsafe to ignore this issue; if |
---|
247 | * the check fails, do NOT disable it unless caches |
---|
248 | * are always physically disabled. |
---|
249 | */ |
---|
250 | if (ppc_exc_cache_wb_check && (MSR_DR & ppc_exc_msr_bits)) { |
---|
251 | /* The size of 63 assumes cache lines are at most 32 bytes */ |
---|
252 | uint8_t dummy[63]; |
---|
253 | uintptr_t p = (uintptr_t) dummy; |
---|
254 | /* If the dcbz instruction raises an alignment exception |
---|
255 | * then the stack is mapped as write-thru or caching-disabled. |
---|
256 | * The low-level code is not capable of dealing with this |
---|
257 | * ATM. |
---|
258 | */ |
---|
259 | p = (p + 31U) & ~31U; |
---|
260 | __asm__ volatile ("dcbz 0, %0"::"b" (p)); |
---|
261 | /* If we make it thru here then things seem to be OK */ |
---|
262 | } |
---|
263 | #endif /* PPC_EXC_CONFIG_BOOKE_ONLY */ |
---|
264 | } |
---|