Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: errors_l2_cache.s | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | #pragma ident "@(#)errors_l2_cache.s 1.12 07/09/18 SMI" | |
50 | ||
51 | #include <sys/asm_linkage.h> | |
52 | #include <hypervisor.h> | |
53 | #include <vcpu.h> | |
54 | #include <asi.h> | |
55 | #include <mmu.h> | |
56 | #include <hprivregs.h> | |
57 | #include <dram.h> | |
58 | #include <abort.h> | |
59 | ||
60 | #include <offsets.h> | |
61 | #include <util.h> | |
62 | #include <error_defs.h> | |
63 | #include <error_regs.h> | |
64 | #include <error_asm.h> | |
65 | #include <cmp.h> | |
66 | #include <traps.h> | |
67 | ||
68 | /* | |
69 | * Clear and correct L2 cache LDAU/LDAC error | |
70 | */ | |
71 | ENTRY(correct_l2_ildau) | |
72 | ba correct_l2_lda_common | |
73 | nop | |
74 | SET_SIZE(correct_l2_ildau) | |
75 | ||
76 | ENTRY(correct_l2_dldac) | |
77 | ALTENTRY(correct_l2_dldau) | |
78 | ba correct_l2_lda_common | |
79 | nop | |
80 | SET_SIZE(correct_l2_dldac) | |
81 | SET_SIZE(correct_l2_dldau) | |
82 | ||
83 | ENTRY(correct_l2_lda_common) | |
84 | ||
85 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
86 | brz,pn %g1, .correct_lda_exit | |
87 | nop | |
88 | ||
89 | /* | |
90 | * Find the bank/PA in error | |
91 | */ | |
92 | set (NO_L2_BANKS - 1), %g3 | |
93 | .correct_lda_next_bank: | |
94 | add %g1, ERR_DIAG_BUF_L2_CACHE_ESR, %g2 | |
95 | mulx %g3, ERR_DIAG_BUF_L2_CACHE_ESR_INCR, %g5 | |
96 | add %g2, %g5, %g2 | |
97 | ldx [%g2], %g5 | |
98 | brz,pn %g5, .correct_lda_next_ear ! no error on this bank | |
99 | nop | |
100 | ||
101 | add %g1, ERR_DIAG_BUF_L2_CACHE_EAR, %g2 | |
102 | mulx %g3, ERR_DIAG_BUF_L2_CACHE_EAR_INCR, %g5 | |
103 | add %g2, %g5, %g2 | |
104 | ldx [%g2], %g6 | |
105 | ||
106 | ! %g3 bank | |
107 | ! %g6 PA | |
108 | ||
109 | /* | |
110 | * Check if L2 cache index hashing is enabled | |
111 | */ | |
112 | setx L2_IDX_HASH_EN_STATUS, %g5, %g4 | |
113 | ldx [%g4], %g4 | |
114 | btst L2_IDX_HASH_EN_STATUS_MASK, %g4 | |
115 | bz,pt %xcc, .correct_lda_no_idx_hashing | |
116 | nop | |
117 | ||
118 | N2_PERFORM_IDX_HASH(%g6, %g2, %g4) ! %g6 = IDX'd flush addr | |
119 | .correct_lda_no_idx_hashing: | |
120 | prefetch [%g6], INVALIDATE_CACHE_LINE | |
121 | .correct_lda_next_ear: | |
122 | brgz,pt %g3, .correct_lda_next_bank | |
123 | dec %g3 | |
124 | .correct_lda_exit: | |
125 | HVRET | |
126 | ||
127 | SET_SIZE(correct_l2_lda_common) | |
128 | ||
129 | ||
130 | /* | |
131 | * Dump L2 cache diagnostic data for all L2 errors | |
132 | * %g7 return address | |
133 | * | |
134 | * Note: Erratum 116 requires FBD Syndrome register to be written | |
135 | * twice to clear the value. Recommended to do this for all | |
136 | * DRAM ESRs. | |
137 | */ | |
138 | ENTRY(dump_l2_cache) | |
139 | ||
140 | /* | |
141 | * save our return address | |
142 | */ | |
143 | STORE_ERR_RETURN_ADDR(%g7, %g3, %g4) | |
144 | ||
145 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
146 | ||
147 | /* | |
148 | * Store L2 ESR/EAR/ND for the bank in error into the DIAG_BUF | |
149 | */ | |
150 | set (NO_L2_BANKS - 1), %g3 | |
151 | .dump_l2c_l2_banks: | |
152 | STRAND_PUSH(%g3, %g4, %g5) | |
153 | ||
154 | ! skip banks which are disabled. causes hang. | |
155 | SKIP_DISABLED_L2_BANK(%g3, %g4, %g5, .dump_l2c_no_l2_error) | |
156 | ||
157 | setx L2_ERROR_STATUS_REG, %g4, %g5 | |
158 | sllx %g3, L2_BANK_SHIFT, %g2 | |
159 | or %g5, %g2, %g2 | |
160 | ldx [%g2], %g4 | |
161 | setx (L2_ESR_VEU | L2_ESR_VEC | L2_ESR_DSC | L2_ESR_DSU), %g5, %g6 | |
162 | btst %g6, %g4 | |
163 | stx %g4, [%g2] ! clear ESR RW1C | |
164 | stx %g0, [%g2] ! clear ESR RW | |
165 | ||
166 | add %g1, ERR_DIAG_BUF_L2_CACHE_ESR, %g2 | |
167 | mulx %g3, ERR_DIAG_BUF_L2_CACHE_ESR_INCR, %g5 | |
168 | add %g2, %g5, %g2 | |
169 | ! %g2 diag_buf->l2_cache.esr | |
170 | bz,pt %xcc, 0f | |
171 | stx %g4, [%g2] | |
172 | ||
173 | ! don't save the bank number if CEEN already off, | |
174 | ! this bank did not generate the trap | |
175 | setx L2_ERROR_ENABLE_REG, %g5, %g6 | |
176 | sllx %g3, L2_BANK_SHIFT, %g5 | |
177 | or %g6, %g5, %g6 | |
178 | ldx [%g6], %g5 | |
179 | btst L2_CEEN, %g5 | |
180 | ! save the bank number | |
181 | bnz,a,pn %xcc, 0f ! CEEN on, store bank in delay slot | |
182 | stx %g3, [%g1 + ERR_DIAG_L2_BANK] | |
183 | 0: | |
184 | ||
185 | ! No L2 data encoded for DSC/DSU | |
186 | ! %g4 ESR | |
187 | setx (L2_ESR_DSC | L2_ESR_DSU), %g5, %g6 | |
188 | btst %g6, %g4 | |
189 | bnz %xcc, .dump_l2c_no_l2_error | |
190 | nop | |
191 | ||
192 | add %g1, ERR_DIAG_BUF_L2_CACHE_ND, %g2 | |
193 | mulx %g3, ERR_DIAG_BUF_L2_CACHE_ND_INCR, %g5 | |
194 | add %g2, %g5, %g2 | |
195 | setx L2_ERROR_NOTDATA_REG, %g4, %g5 | |
196 | sllx %g3, L2_BANK_SHIFT, %g4 | |
197 | or %g5, %g4, %g4 | |
198 | ldx [%g4], %g5 | |
199 | stx %g5, [%g4] ! clear NDESR RW1C | |
200 | stx %g0, [%g4] ! clear NDESR RW | |
201 | stx %g5, [%g2] | |
202 | ||
203 | brnz,a,pt %g5, 1f ! store bank info in delay slot | |
204 | stx %g3, [%g1 + ERR_DIAG_L2_BANK] | |
205 | 1: | |
206 | add %g1, ERR_DIAG_BUF_L2_CACHE_EAR, %g2 | |
207 | mulx %g3, ERR_DIAG_BUF_L2_CACHE_EAR_INCR, %g5 | |
208 | add %g2, %g5, %g2 | |
209 | setx L2_ERROR_ADDRESS_REG, %g4, %g5 | |
210 | sllx %g3, L2_BANK_SHIFT, %g4 | |
211 | or %g5, %g4, %g4 | |
212 | ldx [%g4], %g5 | |
213 | stx %g0, [%g4] ! clear L2 EAR | |
214 | stx %g5, [%g2] | |
215 | ||
216 | ! %g5 PA | |
217 | brz,pt %g5, .dump_l2c_no_l2_error | |
218 | stx %g5, [%g1 + ERR_DIAG_L2_PA] ! delay slot | |
219 | ||
220 | /* | |
221 | * get line state | |
222 | * %g1 PA | |
223 | * %g4 return value | |
224 | */ | |
225 | mov %g5, %g1 | |
226 | HVCALL(check_l2_state) | |
227 | ! %g4 == line_state | |
228 | GET_ERR_DIAG_DATA_BUF(%g3, %g2) | |
229 | stx %g4, [%g3 + ERR_DIAG_L2_LINE_STATE] | |
230 | ||
231 | /* | |
232 | * dump L2 tag/data | |
233 | * %g1 physical address | |
234 | * %g2 dump area | |
235 | * %g7 return address | |
236 | * %g3-%g6 clobbered | |
237 | */ | |
238 | ldx [%g3 + ERR_DIAG_L2_PA], %g1 | |
239 | add %g3, ERR_DIAG_BUF_DIAG_DATA, %g3 | |
240 | add %g3, ERR_DIAG_DATA_L2_CACHE, %g2 | |
241 | HVCALL(dump_l2_set_tag_data_ecc) | |
242 | ||
243 | /* | |
244 | * Dump the contents of DRAM into the diag buf | |
245 | */ | |
246 | GET_ERR_DIAG_DATA_BUF(%g3, %g2) | |
247 | ldx [%g3 + ERR_DIAG_L2_PA], %g4 | |
248 | ! %g4 PA | |
249 | add %g3, ERR_DIAG_BUF_DIAG_DATA, %g3 ! err_diag_buf.err_diag_data | |
250 | add %g3, ERR_DIAG_DATA_L2_CACHE, %g3 ! err_diag_buf.err_diag_data.err_l2_cache | |
251 | add %g3, ERR_DRAM_CONTENTS, %g2 ! err_diag_buf.err_diag_data.err_l2_cache.dram_contents | |
252 | add %g4, L2_LINE_SIZE, %g4 ! align PA | |
253 | andn %g4, L2_LINE_SIZE, %g4 ! ... | |
254 | ldx [%g4 + (0 * SIZEOF_UI64)], %g3 | |
255 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 0] | |
256 | ldx [%g4 + (1 * SIZEOF_UI64)], %g3 | |
257 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 1] | |
258 | ldx [%g4 + (0 * SIZEOF_UI64)], %g3 | |
259 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 2] | |
260 | ldx [%g4 + (3 * SIZEOF_UI64)], %g3 | |
261 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 3] | |
262 | ldx [%g4 + (4 * SIZEOF_UI64)], %g3 | |
263 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 4] | |
264 | ldx [%g4 + (5 * SIZEOF_UI64)], %g3 | |
265 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 5] | |
266 | ldx [%g4 + (6 * SIZEOF_UI64)], %g3 | |
267 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 6] | |
268 | ldx [%g4 + (7 * SIZEOF_UI64)], %g3 | |
269 | stx %g3, [%g2 + ERR_DRAM_CONTENTS_INCR * 7] | |
270 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
271 | .dump_l2c_no_l2_error: | |
272 | ! next bank | |
273 | STRAND_POP(%g3, %g4) | |
274 | brgz,pt %g3, .dump_l2c_l2_banks | |
275 | dec %g3 | |
276 | ||
277 | ! fallthrough | |
278 | ||
279 | dump_l2_cache_dram_esrs: | |
280 | ||
281 | ! check whether this error requires the DRAM data to be dumped/cleared | |
282 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
283 | ld [%g1 + ERR_FLAGS], %g1 | |
284 | set ERR_NO_DRAM_DUMP, %g2 | |
285 | btst %g1, %g2 | |
286 | bnz,pn %xcc, dump_l2_cache_exit | |
287 | nop | |
288 | ||
289 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
290 | ! DIAG_BUF in %g1 | |
291 | ||
292 | /* | |
293 | * Store DRAM ESR/EAR/ND for the bank in error into the DIAG_BUF | |
294 | */ | |
295 | set (NO_DRAM_BANKS - 1), %g3 | |
296 | .dump_l2c_dram_banks: | |
297 | ! skip banks which are disabled. causes hang. | |
298 | SKIP_DISABLED_DRAM_BANK(%g3, %g4, %g5, .dump_l2c_no_dram_error) | |
299 | ||
300 | setx DRAM_ESR_BASE, %g4, %g5 | |
301 | sllx %g3, DRAM_BANK_SHIFT, %g2 | |
302 | or %g5, %g2, %g2 | |
303 | ldx [%g2], %g4 | |
304 | brz,pt %g4, .dump_l2c_no_dram_error ! no error on this bank | |
305 | nop | |
306 | ||
307 | stx %g4, [%g2] ! clear DRAM ESR RW1C | |
308 | stx %g0, [%g2] ! clear DRAM ESR RW | |
309 | add %g1, ERR_DIAG_BUF_DRAM_ESR, %g2 | |
310 | mulx %g3, ERR_DIAG_BUF_DRAM_ESR_INCR, %g5 | |
311 | add %g2, %g5, %g2 | |
312 | ||
313 | stx %g4, [%g2] ! store DRAM ESR | |
314 | ||
315 | add %g1, ERR_DIAG_BUF_DRAM_EAR, %g2 | |
316 | mulx %g3, ERR_DIAG_BUF_DRAM_EAR_INCR, %g5 | |
317 | add %g2, %g5, %g2 | |
318 | setx DRAM_EAR_BASE, %g4, %g5 | |
319 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
320 | or %g5, %g4, %g4 | |
321 | ldx [%g4], %g5 | |
322 | stx %g0, [%g4] ! clear DRAM EAR register | |
323 | stx %g0, [%g4] | |
324 | ! %g5 PA | |
325 | stx %g5, [%g2] | |
326 | stx %g5, [%g1 + ERR_DIAG_L2_PA] | |
327 | ||
328 | add %g1, ERR_DIAG_BUF_DRAM_LOC, %g2 | |
329 | mulx %g3, ERR_DIAG_BUF_DRAM_LOC_INCR, %g5 | |
330 | add %g2, %g5, %g2 | |
331 | setx DRAM_ELR_BASE, %g4, %g5 | |
332 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
333 | or %g5, %g4, %g4 | |
334 | ldx [%g4], %g5 | |
335 | stx %g0, [%g4] ! clear DRAM LOC register | |
336 | stx %g0, [%g4] | |
337 | stx %g5, [%g2] | |
338 | ||
339 | add %g1, ERR_DIAG_BUF_DRAM_CTR, %g2 | |
340 | mulx %g3, ERR_DIAG_BUF_DRAM_CTR_INCR, %g5 | |
341 | add %g2, %g5, %g2 | |
342 | setx DRAM_ECR_BASE, %g4, %g5 | |
343 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
344 | or %g5, %g4, %g4 | |
345 | ldx [%g4], %g5 | |
346 | stx %g0, [%g4] ! clear DRAM COUNTER register | |
347 | stx %g0, [%g4] | |
348 | stx %g5, [%g2] | |
349 | ||
350 | add %g1, ERR_DIAG_BUF_DRAM_FBD, %g2 | |
351 | mulx %g3, ERR_DIAG_BUF_DRAM_FBD_INCR, %g5 | |
352 | add %g2, %g5, %g2 | |
353 | setx DRAM_FBD_BASE, %g4, %g5 | |
354 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
355 | or %g5, %g4, %g4 | |
356 | ldx [%g4], %g5 | |
357 | stx %g0, [%g4] ! clear FBD syndrome register | |
358 | stx %g0, [%g4] | |
359 | stx %g5, [%g2] | |
360 | ||
361 | add %g1, ERR_DIAG_BUF_DRAM_RETRY, %g2 | |
362 | mulx %g3, ERR_DIAG_BUF_DRAM_RETRY_INCR, %g5 | |
363 | add %g2, %g5, %g2 | |
364 | setx DRAM_RETRY_BASE, %g4, %g5 | |
365 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
366 | or %g5, %g4, %g4 | |
367 | ldx [%g4], %g5 | |
368 | stx %g0, [%g4] ! clear DRAM error retry register | |
369 | stx %g0, [%g4] | |
370 | stx %g5, [%g2] | |
371 | ||
372 | .dump_l2c_no_dram_error: | |
373 | ! next bank | |
374 | brgz,pt %g3, .dump_l2c_dram_banks | |
375 | dec %g3 | |
376 | ||
377 | dump_l2_cache_exit: | |
378 | ||
379 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g3) | |
380 | brz,pn %g2, .dump_l2c_no_l2_guest_report | |
381 | GET_ERR_DIAG_DATA_BUF(%g4, %g5) | |
382 | brz,pn %g4, .dump_l2c_no_l2_guest_report | |
383 | nop | |
384 | ldx [%g4 + ERR_DIAG_L2_PA], %g5 | |
385 | VCPU_STRUCT(%g1) | |
386 | CPU_ERR_PA_TO_RA(%g1, %g5, %g4, %g3, %g6) | |
387 | stx %g4, [%g2 + ERR_SUN4V_RPRT_ADDR] | |
388 | .dump_l2c_no_l2_guest_report: | |
389 | ! all done | |
390 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
391 | ||
392 | HVRET | |
393 | ||
394 | SET_SIZE(dump_l2_cache) | |
395 | ||
396 | /* | |
397 | * Populate a sun4v ereport packet for L2$ errors | |
398 | * SZ == ERPT_MEM_SIZE | |
399 | * | |
400 | * We don't have the L2 EAR data yet. Fill it in in dump_l2_cache above | |
401 | * | |
402 | * %g7 return address | |
403 | */ | |
404 | ENTRY(l2_sun4v_report) | |
405 | ||
406 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g3) | |
407 | brz,pn %g2, l2_sun4v_report_exit | |
408 | mov ERPT_MEM_SIZE, %g5 | |
409 | stx %g5, [%g2 + ERR_SUN4V_RPRT_SZ] | |
410 | l2_sun4v_report_exit: | |
411 | HVRET | |
412 | ||
413 | SET_SIZE(l2_sun4v_report) | |
414 | ||
415 | ||
416 | ENTRY(correct_l2_dac) | |
417 | HVRET | |
418 | SET_SIZE(correct_l2_dac) | |
419 | ENTRY(correct_l2_drc) | |
420 | HVRET | |
421 | SET_SIZE(correct_l2_drc) | |
422 | ||
423 | ENTRY(l2_ce_storm) | |
424 | ||
425 | ! first verify that storm prevention is enabled | |
426 | CHECK_BLACKOUT_INTERVAL(%g4) | |
427 | ||
428 | /* | |
429 | * save our return address | |
430 | */ | |
431 | STORE_ERR_RETURN_ADDR(%g7, %g4, %g5) | |
432 | ||
433 | /* | |
434 | * bank in error | |
435 | */ | |
436 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
437 | ldx [%g1 + ERR_DIAG_L2_BANK], %g2 | |
438 | and %g2, (NO_L2_BANKS - 1), %g2 | |
439 | ||
440 | ! skip banks which are disabled. causes hang. | |
441 | SKIP_DISABLED_L2_BANK(%g2, %g4, %g5, 9f) | |
442 | ||
443 | setx L2_ERROR_ENABLE_REG, %g4, %g5 | |
444 | sllx %g2, L2_BANK_SHIFT, %g3 | |
445 | add %g5, %g3, %g5 | |
446 | ldx [%g5], %g3 | |
447 | btst L2_CEEN, %g3 | |
448 | bz,pn %xcc, 9f ! CEEN already off | |
449 | andn %g3, L2_CEEN, %g3 | |
450 | stx %g3, [%g5] ! disable CEEN | |
451 | ||
452 | /* | |
453 | * Set up a cyclic on this strand to re-enable the CEEN bit | |
454 | * after an interval of 6 seconds. Set a flag in the | |
455 | * strand struct to indicate that the cyclic has been set | |
456 | * for this bank. | |
457 | */ | |
458 | mov STRAND_ERR_FLAG_L2DRAM, %g4 ! L2DRAM flag | |
459 | sllx %g4, %g2, %g4 ! << bank# | |
460 | STRAND_STRUCT(%g6) | |
461 | lduw [%g6 + STRAND_ERR_FLAG], %g3 ! installed flags | |
462 | btst %g4, %g3 ! handler installed? | |
463 | bnz,pn %xcc, 9f ! yes | |
464 | ||
465 | or %g3, %g4, %g3 ! no: set it | |
466 | STRAND2CONFIG_STRUCT(%g6, %g4) | |
467 | ldx [%g4 + CONFIG_CE_BLACKOUT], %g1 | |
468 | brz,pn %g1, 9f ! zero: blackout disabled | |
469 | nop | |
470 | SET_STRAND_ERR_FLAG(%g6, %g3, %g5) | |
471 | mov %g2, %g4 ! g4 = arg 1 : B5-0: bank # | |
472 | setx l2_set_err_bits, %g5, %g2 | |
473 | RELOC_OFFSET(%g3, %g5) | |
474 | sub %g2, %g5, %g2 ! g2 = handler address | |
475 | mov L2_CEEN, %g3 ! g3 = arg 0 : bit(s) to set | |
476 | ! g1 = delta tick | |
477 | VCPU_STRUCT(%g6) | |
478 | ! g6 - CPU struct | |
479 | HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */ | |
480 | 9: | |
481 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
482 | HVRET | |
483 | SET_SIZE(l2_ce_storm) | |
484 | ||
485 | /* | |
486 | * Callback from interrupt: | |
487 | * | |
488 | * Entry Data: | |
489 | * %g1: bit(s) to set | |
490 | * %g2: B:5-0 = bank # | |
491 | * | |
492 | * Return Data: | |
493 | * none | |
494 | * | |
495 | * Registers modified: | |
496 | * %g1-6 | |
497 | */ | |
498 | ENTRY(l2_set_err_bits) | |
499 | mov %g1, %g5 ! bits | |
500 | and %g2, NO_L2_BANKS - 1, %g6 ! bank # | |
501 | !! %g5 = bits to set | |
502 | !! %g6 = bank# | |
503 | ||
504 | ! skip banks which are disabled. causes hang. | |
505 | SKIP_DISABLED_L2_BANK(%g6, %g4, %g2, 9f) | |
506 | ||
507 | setx DRAM_ESR_CE_BITS | DRAM_ESR_MEC, %g1, %g2 | |
508 | setx DRAM_ESR_BASE, %g1, %g3 ! DRAM base | |
509 | srlx %g6, 1, %g1 ! L2 bank -> DRAM bank | |
510 | sllx %g1, DRAM_BANK_SHIFT, %g4 ! + bank offset | |
511 | ldx [%g3 + %g4], %g1 ! get ESR[bank] | |
512 | and %g1, %g2, %g1 ! reset CE bits only (W1C) | |
513 | stx %g1, [%g3 + %g4] | |
514 | ||
515 | !! %g6 = bank# | |
516 | setx L2_ESR_CE_ERRORS, %g1, %g2 | |
517 | setx L2_ERROR_STATUS_REG, %g1, %g3 | |
518 | sllx %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
519 | ldx [%g3 + %g4], %g1 ! get ESR[bank] | |
520 | and %g1, %g2, %g1 ! reset CE bits only (W1C) | |
521 | stx %g1, [%g3 + %g4] | |
522 | ||
523 | ! clear FBD Error Syndrome register for this bank | |
524 | setx DRAM_FBD_BASE, %g1, %g3 | |
525 | sllx %g6, DRAM_BANK_SHIFT, %g4 | |
526 | stx %g0, [%g3 + %g4] ! clear DRAM FBD SYND RW | |
527 | stx %g0, [%g3 + %g4] | |
528 | ||
529 | !! %g6 = bank# | |
530 | STRAND_STRUCT(%g3) ! %g3->cpu | |
531 | mov STRAND_ERR_FLAG_L2DRAM, %g1 ! L2DRAM flag | |
532 | sllx %g1, %g6, %g1 ! << bank# | |
533 | CLEAR_STRAND_ERR_FLAG(%g3, %g1, %g4) | |
534 | ||
535 | !! %g6 = bank# | |
536 | setx L2_ERROR_ENABLE_REG, %g4, %g5 | |
537 | sllx %g6, L2_BANK_SHIFT, %g3 | |
538 | or %g5, %g3, %g5 | |
539 | ldx [%g5], %g3 | |
540 | or %g3, L2_CEEN, %g3 | |
541 | stx %g3, [%g5] ! enable CEEN | |
542 | 9: | |
543 | HVRET | |
544 | SET_SIZE(l2_set_err_bits) | |
545 | ||
546 | ENTRY(dram_storm) | |
547 | ||
548 | ! first verify that storm prevention is enabled | |
549 | CHECK_BLACKOUT_INTERVAL(%g4) | |
550 | ||
551 | /* | |
552 | * save our return address | |
553 | */ | |
554 | STORE_ERR_RETURN_ADDR(%g7, %g4, %g5) | |
555 | ||
556 | /* | |
557 | * Disable DRAM errors | |
558 | */ | |
559 | setx CORE_DRAM_ERRORS_ENABLE , %g4, %g1 | |
560 | mov CORE_ERR_REPORT_EN, %g4 | |
561 | ldxa [%g4]ASI_ERR_EN, %g6 | |
562 | andn %g6, %g1, %g6 | |
563 | stxa %g6, [%g4]ASI_ERR_EN | |
564 | ||
565 | /* | |
566 | * Set up a cyclic on this strand to re-enable the CERER bits | |
567 | * after an interval of (default) 6 seconds. Set a flag in the | |
568 | * strand struct to indicate that the cyclic has been set | |
569 | * for these errors. | |
570 | */ | |
571 | setx STRAND_ERR_FLAG_DRAM, %g6, %g4 | |
572 | STRAND_STRUCT(%g6) | |
573 | lduw [%g6 + STRAND_ERR_FLAG], %g3 ! installed flags | |
574 | btst %g4, %g3 ! handler installed? | |
575 | bnz,pn %xcc, 9f ! yes | |
576 | ||
577 | or %g3, %g4, %g3 ! no: set it | |
578 | STRAND2CONFIG_STRUCT(%g6, %g4) | |
579 | ldx [%g4 + CONFIG_CE_BLACKOUT], %g1 | |
580 | brz,pn %g1, 9f ! zero: blackout disabled | |
581 | nop | |
582 | SET_STRAND_ERR_FLAG(%g6, %g3, %g5) | |
583 | setx STRAND_ERR_FLAG_DRAM, %g5, %g4 ! g4 = arg 1, flags to clear | |
584 | setx CORE_DRAM_ERRORS_ENABLE, %g5, %g3 ! g3 = arg 0 : bit(s) to set | |
585 | setx cerer_set_error_bits, %g5, %g2 | |
586 | RELOC_OFFSET(%g6, %g5) | |
587 | sub %g2, %g5, %g2 ! g2 = handler address | |
588 | ! g1 = delta tick | |
589 | VCPU_STRUCT(%g6) | |
590 | ! g6 - CPU struct | |
591 | HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */ | |
592 | 9: | |
593 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
594 | HVRET | |
595 | SET_SIZE(dram_storm) | |
596 | ||
597 | /* | |
598 | * print the bank and PA in error | |
599 | * and the diag-buf L2 cache | |
600 | * %g7 return address | |
601 | */ | |
602 | ENTRY(l2_cache_print) | |
603 | #ifdef DEBUG_LEGION | |
604 | mov %g7, %g6 | |
605 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
606 | ||
607 | PRINT("L2 BANK: 0x"); | |
608 | ldx [%g1 + ERR_DIAG_L2_BANK], %g4 | |
609 | PRINTX(%g4) | |
610 | PRINT("\r\n") | |
611 | PRINT("L2 PA: 0x"); | |
612 | ldx [%g1 + ERR_DIAG_L2_PA], %g4 | |
613 | PRINTX(%g4) | |
614 | PRINT("\r\n") | |
615 | ||
616 | add %g1, ERR_DIAG_BUF_DIAG_DATA, %g1 | |
617 | add %g1, ERR_DIAG_DATA_L2_CACHE, %g1 | |
618 | ||
619 | PRINT("L2 VDBITS: 0x"); | |
620 | ldx [%g1 + ERR_L2_VDBITS], %g4 | |
621 | PRINTX(%g4) | |
622 | PRINT("\r\n") | |
623 | PRINT("L2 UABITS: 0x"); | |
624 | ldx [%g1 + ERR_L2_UABITS], %g4 | |
625 | PRINTX(%g4) | |
626 | PRINT("\r\n") | |
627 | ||
628 | /* | |
629 | * DIAG_BUF for L2 ways in %g1 | |
630 | */ | |
631 | add %g1, ERR_L2_WAYS, %g1 | |
632 | mov 0, %g2 | |
633 | 1: | |
634 | PRINT("L2 WAYS: 0x"); | |
635 | srlx %g2, 3, %g4 | |
636 | PRINTX(%g4) | |
637 | PRINT(" : TAG ECC: 0x") | |
638 | ldx [%g1 + ERR_WAY_TAG_AND_ECC], %g4 | |
639 | PRINTX(%g4) | |
640 | PRINT("\r\n") | |
641 | ||
642 | /* | |
643 | * for each L2 way, print each data ecc, starting from word 0 | |
644 | */ | |
645 | add %g1, ERR_WAY_DATA_AND_ECC, %g1 | |
646 | mov 0, %g3 | |
647 | 2: | |
648 | PRINT("DATA ECC: 0x"); | |
649 | srlx %g3, 3, %g4 | |
650 | PRINTX(%g4) | |
651 | PRINT(" : 0x") | |
652 | ldx [%g1], %g4 | |
653 | PRINTX(%g4) | |
654 | PRINT("\r\n") | |
655 | add %g3, 0x8, %g3 | |
656 | cmp %g3, L2_NUM_WAYS * 8 | |
657 | bnz 2b | |
658 | add %g1, ERR_WAY_DATA_AND_ECC_INCR, %g1 | |
659 | ||
660 | /* | |
661 | * next L2 way | |
662 | */ | |
663 | add %g2, 0x8, %g2 | |
664 | cmp %g2, L2_NUM_WAYS * 8 | |
665 | bnz 1b | |
666 | add %g1, ERR_L2_WAYS_INCR, %g1 /* increment */ | |
667 | ||
668 | mov %g6, %g7 | |
669 | #endif /* DEBUG */ | |
670 | ||
671 | HVRET | |
672 | SET_SIZE(l2_cache_print) | |
673 | ||
674 | /* | |
675 | * FBDIMM bug around FBRs (fbdimm serdes corrrectable errors). | |
676 | * Metrax id is 125737 | |
677 | * | |
678 | * There is an interaction between the scrub logic and the retry | |
679 | * logic for link CRC errors. The SW visible symptom is a DSU | |
680 | * (scrub uncorrectable error) sent to the error steering thread. | |
681 | * It will log a DSU with valid address and 0xffff syndrome | |
682 | * (address parity error). The error is totally bogus and should | |
683 | * be ignored (data in DRAM is perfectly correct). | |
684 | * | |
685 | * This function will read the DSU error address from the bank | |
686 | * in error under protection. If another error occurs this is a | |
687 | * valid DSU. If not, this is a bogus error and we just exit | |
688 | * error handling with a RETRY. | |
689 | */ | |
690 | ENTRY_NP(verify_dsu_error) | |
691 | ||
692 | STRAND_PUSH(%g7, %g2, %g3) | |
693 | ||
694 | #ifdef DEBUG | |
695 | HV_PRINT_NOTRAP("VERIFY DSU\r\n") | |
696 | #endif | |
697 | ||
698 | /* | |
699 | * Find the DRAM bank which got the DSU | |
700 | */ | |
701 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
702 | brz,pn %g1, .dsu_genuine_error ! nothing to do | |
703 | nop | |
704 | ||
705 | set (NO_DRAM_BANKS - 1), %g3 | |
706 | add %g1, ERR_DIAG_BUF_DRAM_ESR, %g2 | |
707 | setx DRAM_ESR_DSU, %g5, %g4 | |
708 | set DRAM_ESR_SYND_MASK, %g6 | |
709 | .verify_dsu_dram_banks: | |
710 | mulx %g3, ERR_DIAG_BUF_DRAM_ESR_INCR, %g5 | |
711 | add %g5, %g2, %g5 | |
712 | ldx [%g5], %g5 ! DRAM ESR | |
713 | ||
714 | #ifdef DEBUG | |
715 | STRAND_PUSH(%g1, %g6, %g7) | |
716 | STRAND_PUSH(%g2, %g6, %g7) | |
717 | STRAND_PUSH(%g3, %g6, %g7) | |
718 | mov %g5, %g6 | |
719 | HV_PRINT_NOTRAP("ESR 0x") | |
720 | HV_PRINTX_NOTRAP(%g6) | |
721 | HV_PRINT_NOTRAP("\r\n") | |
722 | mov %g6, %g5 | |
723 | setx DRAM_ESR_DSU, %g6, %g4 | |
724 | STRAND_POP(%g3, %g6) | |
725 | STRAND_POP(%g2, %g6) | |
726 | STRAND_POP(%g1, %g6) | |
727 | set DRAM_ESR_SYND_MASK, %g6 | |
728 | #endif | |
729 | ||
730 | btst %g5, %g4 ! DSU on this bank ? | |
731 | bz,pt %xcc, .verify_dsu_dram_banks_loop | |
732 | nop | |
733 | ||
734 | /* | |
735 | * DRAM_ESR.DSU, look for syndrome 0xffff | |
736 | */ | |
737 | and %g5, %g6, %g5 | |
738 | cmp %g5, %g6 ! syndrome == 0xffff ? | |
739 | bne,pt %xcc, .verify_dsu_dram_banks_loop | |
740 | nop | |
741 | ||
742 | /* | |
743 | * DSU on this bank, read the address | |
744 | * If there is an error at this location we should get | |
745 | * a precise data_access_error trap for critical load | |
746 | * data delivered before linefill. | |
747 | */ | |
748 | add %g1, ERR_DIAG_BUF_DRAM_EAR, %g2 | |
749 | mulx %g3, ERR_DIAG_BUF_DRAM_EAR_INCR, %g5 | |
750 | add %g5, %g2, %g5 | |
751 | ldx [%g5], %g4 | |
752 | #ifdef DEBUG | |
753 | STRAND_PUSH(%g1, %g6, %g7) | |
754 | STRAND_PUSH(%g2, %g6, %g7) | |
755 | STRAND_PUSH(%g3, %g6, %g7) | |
756 | mov %g4, %g6 | |
757 | HV_PRINT_NOTRAP("EAR 0x") | |
758 | HV_PRINTX_NOTRAP(%g6) | |
759 | HV_PRINT_NOTRAP("\r\n") | |
760 | mov %g6, %g4 | |
761 | STRAND_POP(%g3, %g6) | |
762 | STRAND_POP(%g2, %g6) | |
763 | STRAND_POP(%g1, %g6) | |
764 | #endif | |
765 | andn %g4, 0xf, %g4 ! force alignment | |
766 | ! %g4 PA of DSU error | |
767 | ||
768 | setx L2_IDX_HASH_EN_STATUS, %g3, %g5 | |
769 | ldx [%g5], %g5 | |
770 | and %g5, L2_IDX_HASH_EN_STATUS_MASK, %g5 | |
771 | brz,pn %g5, .verify_dsu_no_idx_hashing ! no index hashing | |
772 | nop | |
773 | ||
774 | N2_PERFORM_IDX_HASH(%g4, %g3, %g5) | |
775 | .verify_dsu_no_idx_hashing: | |
776 | ! %g4 PA | |
777 | STRAND_STRUCT(%g2) | |
778 | set STRAND_ERR_FLAG_PROTECTION, %g5 | |
779 | SET_STRAND_ERR_FLAG(%g2, %g5, %g3) | |
780 | ||
781 | ldx [%g4], %g0 | |
782 | ldx [%g4 + 8], %g0 | |
783 | ||
784 | CLEAR_STRAND_ERR_FLAG(%g2, %g5, %g3) | |
785 | ||
786 | /* | |
787 | * If this is a genuine DSU error, the IO_ERROR flag should be | |
788 | * set now. | |
789 | */ | |
790 | ||
791 | add %g2, STRAND_IO_ERROR, %g2 | |
792 | ldx [%g2], %g3 | |
793 | brnz,a,pt %g3, .dsu_genuine_error | |
794 | stx %g0, [%g2] ! clear strand.io_error if set | |
795 | ||
796 | /* | |
797 | * No error on access to DSU PA so we consider this a bogus error | |
798 | */ | |
799 | ba .dsu_bogus_error | |
800 | nop | |
801 | ||
802 | .verify_dsu_dram_banks_loop: | |
803 | ! check next DRAM bank | |
804 | brgz,pt %g3, .verify_dsu_dram_banks | |
805 | dec %g3 | |
806 | ||
807 | /* | |
808 | * Nothing found in the DRAM ESRs, fall through and allow | |
809 | * standard error handling to proceed | |
810 | */ | |
811 | ||
812 | .dsu_genuine_error: | |
813 | /* | |
814 | * strand.io_error was set, so the access to the DSU EAR caused | |
815 | * another error, so this is a genuine DSU, (or no ESR.DSU bit | |
816 | * set), so continue standard error processing | |
817 | */ | |
818 | STRAND_POP(%g7, %g2) | |
819 | ba,a correct_l2_lda_common ! tail call | |
820 | .empty | |
821 | ||
822 | .dsu_bogus_error: | |
823 | /* | |
824 | * strand.io_error was not set, so the access to the DSU EAR | |
825 | * did not cause another error, this is a bogus DSU, clean up and get out ! | |
826 | */ | |
827 | #ifdef DEBUG | |
828 | HV_PRINT_NOTRAP("Bogus DSU\r\n") | |
829 | #endif | |
830 | ||
831 | /* | |
832 | * Find the DRAM bank which got the DSU | |
833 | */ | |
834 | ||
835 | GET_ERR_DIAG_DATA_BUF(%g1, %g2) | |
836 | set (NO_DRAM_BANKS - 1), %g3 | |
837 | add %g1, ERR_DIAG_BUF_DRAM_ESR, %g2 | |
838 | setx (DRAM_ESR_MEU | DRAM_ESR_MEC | DRAM_ESR_DSU), %g5, %g4 | |
839 | 0: | |
840 | mulx %g3, ERR_DIAG_BUF_DRAM_ESR_INCR, %g5 | |
841 | add %g5, %g2, %g7 | |
842 | ldx [%g7], %g5 ! DRAM ESR | |
843 | brz,pt %g5, 1f | |
844 | andn %g5, %g4, %g6 ! clear out DSU/ME bits | |
845 | brz,pt %g6, .dsu_bogus_single_error | |
846 | nop | |
847 | ! multiple errors seen, DSU is bogus, clear DSU bit from ESR and | |
848 | ! continue processing the error | |
849 | setx DRAM_ESR_DSU, %g2, %g4 | |
850 | andn %g5, %g4, %g5 | |
851 | stx %g5, [%g7] | |
852 | STRAND_POP(%g7, %g2) | |
853 | HVRET | |
854 | 1: | |
855 | brz,pt %g3, 0b | |
856 | dec %g3 | |
857 | ||
858 | ||
859 | /* NOTREACHED */ | |
860 | ||
861 | .dsu_bogus_single_error: | |
862 | ||
863 | HVCALL(clear_dram_l2c_esr_regs) | |
864 | ||
865 | /* | |
866 | * Clear the error report in_use field | |
867 | */ | |
868 | GET_ERR_DIAG_BUF(%g4, %g5) | |
869 | brnz,a,pt %g4, 1f | |
870 | stub %g0, [%g4 + ERR_DIAG_RPRT_IN_USE] | |
871 | 1: | |
872 | /* | |
873 | * Clear the sun4v report in_use field | |
874 | */ | |
875 | GET_ERR_SUN4V_RPRT_BUF(%g4, %g5) | |
876 | brnz,a,pt %g4, 1f | |
877 | stub %g0, [%g4 + ERR_SUN4V_RPRT_IN_USE] | |
878 | 1: | |
879 | /* | |
880 | * Does the trap handler for this error park the strands ? | |
881 | * If yes, resume them here. | |
882 | */ | |
883 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
884 | ld [%g1 + ERR_FLAGS], %g6 | |
885 | btst ERR_STRANDS_PARKED, %g6 | |
886 | bz,pn %xcc, 1f | |
887 | nop | |
888 | ||
889 | RESUME_ALL_STRANDS(%g2, %g3, %g5, %g4) | |
890 | 1: | |
891 | /* | |
892 | * check whether we stored the globals and re-used | |
893 | * at MAXPTL | |
894 | */ | |
895 | btst ERR_GL_STORED, %g6 | |
896 | bz,pt %xcc, 1f | |
897 | nop | |
898 | ||
899 | RESTORE_GLOBALS(retry) | |
900 | 1: | |
901 | retry | |
902 | ||
903 | /* NOTREACHED */ | |
904 | ||
905 | SET_SIZE(verify_dsu_error) | |
906 | ||
907 | ||
908 | /* | |
909 | * DRAM / L2 ESR registers must be cleared after an error which | |
910 | * occurred under protection as we will not go through the full | |
911 | * error handling sequence for these errors. If the errors | |
912 | * are not cleared further errors are blocked. | |
913 | * | |
914 | * Note: Erratum 116 requires FBD Syndrome register to be written | |
915 | * twice to clear the value. Recommended to do this for all | |
916 | * DRAM ESRs. | |
917 | */ | |
918 | ENTRY_NP(clear_dram_l2c_esr_regs) | |
919 | ||
920 | set (NO_L2_BANKS - 1), %g3 | |
921 | 1: | |
922 | ! skip banks which are disabled. causes hang. | |
923 | SKIP_DISABLED_L2_BANK(%g3, %g4, %g6, 2f) | |
924 | ||
925 | setx L2_ERROR_STATUS_REG, %g4, %g5 | |
926 | sllx %g3, L2_BANK_SHIFT, %g4 | |
927 | ldx [%g5 + %g4], %g6 | |
928 | stx %g6, [%g5 + %g4] ! L2 ESR RW1C | |
929 | stx %g0, [%g5 + %g4] ! L2 ESR RW | |
930 | ||
931 | setx L2_ERROR_ADDRESS_REG, %g4, %g5 | |
932 | sllx %g3, L2_BANK_SHIFT, %g4 | |
933 | stx %g0, [%g5 + %g4] | |
934 | 2: | |
935 | brgz,pt %g3, 1b | |
936 | dec %g3 | |
937 | ||
938 | set (NO_DRAM_BANKS - 1), %g3 | |
939 | 1: | |
940 | ! skip banks which are disabled. causes hang. | |
941 | SKIP_DISABLED_DRAM_BANK(%g3, %g4, %g6, 2f) | |
942 | ||
943 | setx DRAM_ESR_BASE, %g4, %g5 | |
944 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
945 | ldx [%g5 + %g4], %g6 | |
946 | stx %g6, [%g5 + %g4] | |
947 | stx %g6, [%g5 + %g4] ! DRAM ESR RW1C | |
948 | stx %g0, [%g5 + %g4] | |
949 | stx %g0, [%g5 + %g4] | |
950 | ||
951 | setx DRAM_EAR_BASE, %g4, %g5 | |
952 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
953 | stx %g0, [%g5 + %g4] ! clear DRAM EAR RW | |
954 | stx %g0, [%g5 + %g4] | |
955 | ||
956 | setx DRAM_ELR_BASE, %g4, %g5 | |
957 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
958 | stx %g0, [%g5 + %g4] ! clear DRAM LOC RW | |
959 | stx %g0, [%g5 + %g4] | |
960 | ||
961 | setx DRAM_FBD_BASE, %g4, %g5 | |
962 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
963 | stx %g0, [%g5 + %g4] ! clear FBD Syndrome register | |
964 | stx %g0, [%g5 + %g4] | |
965 | 2: | |
966 | brgz,pt %g3, 1b | |
967 | dec %g3 | |
968 | ||
969 | HVRET | |
970 | SET_SIZE(clear_dram_l2c_esr_regs) | |
971 | ||
972 | ||
973 | #ifdef CONFIG_CLEANSER | |
974 | ||
975 | /* | |
976 | * L2 cache cleanser | |
977 | * | |
978 | * %g1 next cache entry to clean (clobbered) | |
979 | * %g2 - %g6 clobbered | |
980 | * %g7 return address | |
981 | */ | |
982 | ENTRY(l2_cache_cleanser) | |
983 | ||
984 | STRAND_STRUCT(%g6) | |
985 | STRAND2CONFIG_STRUCT(%g6, %g6) | |
986 | ldx [%g6 + CONFIG_L2SCRUB_ENTRIES], %g6 ! config->l2scrub_entries | |
987 | brnz,pn %g6, 1f | |
988 | srl %g6, 0, %g6 ! keep it sane, max number of lines | |
989 | ||
990 | ! if #entries is 0, cleanser is disabled | |
991 | HVRET | |
992 | ||
993 | 1: | |
994 | STRAND_PUSH(%g7, %g2, %g3) ! save return address | |
995 | ||
996 | /* | |
997 | * key:way:set:bank is only initialised if we enter with %g1 == 0. | |
998 | * otherwise, %g1 is the index of the next entry to be cleansed | |
999 | * and we continue from there. | |
1000 | */ | |
1001 | brnz,pn %g1, l2_cache_cleanser_start | |
1002 | mov %g0, %g4 | |
1003 | ||
1004 | ! %g1 == 0, initialise L2 Cache PA | |
1005 | ||
1006 | setx PREFETCHICE_KEY, %g2, %g1 | |
1007 | setx PREFETCHICE_WAY_MAX, %g2, %g3 | |
1008 | or %g1, %g3, %g1 | |
1009 | ! %g1 key[39:37]:rsvd[36:22]:way[21:18] | |
1010 | ! rsvd is 0, no effect on index hashing | |
1011 | ||
1012 | /* | |
1013 | * Check which L2 cache banks are enabled | |
1014 | * Note: We need this value for the loop end calculation | |
1015 | * so we stash it in the top 32 bits of %g6, (the | |
1016 | * scrub entries value). | |
1017 | */ | |
1018 | setx L2_BANK_ENABLE, %g4, %g5 | |
1019 | ldx [%g5], %g5 | |
1020 | srlx %g5, L2_BANK_ENABLE_SHIFT, %g5 | |
1021 | and %g5, L2_BANK_ENABLE_MASK, %g5 | |
1022 | ||
1023 | #define STASH_BANK_ENABLE_MODE_SHIFT 32 | |
1024 | ||
1025 | sllx %g5, STASH_BANK_ENABLE_MODE_SHIFT, %g4 | |
1026 | or %g6, %g4, %g6 | |
1027 | ||
1028 | cmp %g5, 0xff ! 8-bank mode | |
1029 | be %xcc, l2_cache_cleanser_8banks | |
1030 | cmp %g5, 0xf0 ! 4-bank mode | |
1031 | be %xcc, l2_cache_cleanser_4banks | |
1032 | cmp %g5, 0xcc ! 4-bank mode | |
1033 | be %xcc, l2_cache_cleanser_4banks | |
1034 | cmp %g5, 0xc3 ! 4-bank mode | |
1035 | be %xcc, l2_cache_cleanser_4banks | |
1036 | cmp %g5, 0x3c ! 4-bank mode | |
1037 | be %xcc, l2_cache_cleanser_4banks | |
1038 | cmp %g5, 0x33 ! 4-bank mode | |
1039 | be %xcc, l2_cache_cleanser_4banks | |
1040 | cmp %g5, 0xf ! 4-bank mode | |
1041 | be %xcc, l2_cache_cleanser_4banks | |
1042 | nop | |
1043 | ba l2_cache_cleanser_2banks | |
1044 | .empty | |
1045 | ||
1046 | l2_cache_cleanser_8banks: | |
1047 | setx PREFETCHICE_8BANK_SET_MAX, %g2, %g3 | |
1048 | ! %g3 set[17:9] | |
1049 | setx PREFETCHICE_8BANK_MAX, %g2, %g4 | |
1050 | ! %g4 bank[8:6] | |
1051 | ba l2_cache_cleanser_start | |
1052 | or %g4, %g3, %g4 | |
1053 | ! %g4 set[17:9]:bank[8:6] | |
1054 | ||
1055 | l2_cache_cleanser_4banks: | |
1056 | setx PREFETCHICE_4BANK_SET_MAX, %g2, %g3 | |
1057 | ! %g3 set[16:8] | |
1058 | setx PREFETCHICE_4BANK_MAX, %g2, %g4 | |
1059 | ! %g4 bank[7:6] | |
1060 | ba l2_cache_cleanser_start | |
1061 | or %g4, %g3, %g4 | |
1062 | ! %g4 set[16:8]:bank[7:6] | |
1063 | ||
1064 | l2_cache_cleanser_2banks: | |
1065 | setx PREFETCHICE_2BANK_SET_MAX, %g2, %g3 | |
1066 | ! %g3 set[15:7] | |
1067 | setx PREFETCHICE_2BANK_MAX, %g2, %g4 | |
1068 | ! %g4 bank[6] | |
1069 | or %g4, %g3, %g4 | |
1070 | ! %g4 set[15:7]:bank[6] | |
1071 | /* FALLTHRU */ | |
1072 | ||
1073 | l2_cache_cleanser_start: | |
1074 | or %g1, %g4, %g1 ! %g1 key[39:37]:way[21:18]:set[17:9]:bank[8:6] | |
1075 | ||
1076 | ! Check if L2 cache index hash enabled | |
1077 | setx L2_IDX_HASH_EN_STATUS, %g3, %g4 | |
1078 | ldx [%g4], %g4 | |
1079 | and %g4, L2_IDX_HASH_EN_STATUS_MASK, %g4 | |
1080 | ||
1081 | ! %g1 L2 Cache Entry key:way:set:bank | |
1082 | ! %g4 Set if L2 index hashing is enabled | |
1083 | ! %g6 Number of entries to be cleansed | |
1084 | ||
1085 | l2_cache_cleanser_check_ECC: | |
1086 | /* | |
1087 | * Check ECC here. If there is a multi-bit | |
1088 | * error, generate an error report now and abort. | |
1089 | * | |
1090 | * use %g2, %g3, %g5, %g7 only | |
1091 | */ | |
1092 | set L2_TAG_DIAG_SELECT, %g3 | |
1093 | sllx %g3, L2_TAG_DIAG_SELECT_SHIFT, %g3 | |
1094 | ! %g3 L2 Tag Diag Select | |
1095 | ||
1096 | ! get way:set:bank bits[21:6] from %g1 | |
1097 | setx L2_BANK_SET | (L2_WAY_MASK << L2_WAY_SHIFT), %g5, %g2 | |
1098 | and %g1, %g2, %g2 | |
1099 | ||
1100 | or %g3, %g2, %g3 ! %g3 select:way:set:bank | |
1101 | ldx [%g3], %g3 ! %g3 tag[27:6]:ecc[5:0] | |
1102 | ||
1103 | /* | |
1104 | * Need to save the ECC bits for later, but we don't have a | |
1105 | * spare register. We know that %g4 has a single bit set, | |
1106 | * L2_IDX_HASH_EN_STATUS, so we will use a few of it's high | |
1107 | * bits. | |
1108 | */ | |
1109 | #define STASH_ECC_BITS_SHIFT 9 | |
1110 | ||
1111 | and %g3, L2_TAG_DIAG_ECC_MASK, %g7 ! ecc | |
1112 | sllx %g7, STASH_ECC_BITS_SHIFT, %g7 | |
1113 | or %g4, %g7, %g4 | |
1114 | ||
1115 | ! clear tag bits (63:28) | |
1116 | set L2_TAG_MASK, %g5 | |
1117 | and %g3, %g5, %g3 | |
1118 | srlx %g3, L2_TAG_SHIFT, %g3 ! lose ecc bits[5:0], %g3 = tag | |
1119 | ||
1120 | /* | |
1121 | * return the checkbits for an integer 'tag' | |
1122 | * ('tag' is the tag bits, right-justified; unused high-order bits are zero) | |
1123 | * | |
1124 | * uint64_t | |
1125 | * calc_ecc(uint64_t tag) | |
1126 | * { | |
1127 | * ecc_syndrome_table_entry *ep; | |
1128 | * uint64_t ecc; | |
1129 | * | |
1130 | * for (ep = &l2_tag_ecc_table[0], ecc = 0; | |
1131 | * tag != 0; ep++, x >>= 1) { | |
1132 | * if (tag & 1) | |
1133 | * ecc ^= *ep; | |
1134 | * } | |
1135 | * return (ecc); | |
1136 | * } | |
1137 | */ | |
1138 | ||
1139 | setx l2_tag_ecc_table, %g7, %g5 | |
1140 | RELOC_ADDR(%g5, %g7) ! %g5 ep = &l2_tag_ecc_table[0] | |
1141 | mov %g0, %g7 ! %g7 ecc = 0 | |
1142 | 1: | |
1143 | btst 1, %g3 ! if (tag & 1) | |
1144 | bz,pn %xcc, 2f | |
1145 | nop | |
1146 | ldub [%g5], %g2 ! %g2 *ep | |
1147 | xor %g7, %g2, %g7 ! ecc ^= *ep | |
1148 | ||
1149 | 2: | |
1150 | srlx %g3, 1, %g3 ! tag >> 1 | |
1151 | brnz,pt %g3, 1b ! tag != 0 | |
1152 | add %g5, ECC_SYNDROME_TABLE_ENTRY_SIZE, %g5 ! ep++ | |
1153 | ||
1154 | ! %g7 calculated ECC | |
1155 | ||
1156 | ! get the ECC from the diagnostic register | |
1157 | ! from where we stashed it in (%g4 << STASH_ECC_BITS_SHIFT) | |
1158 | srlx %g4, STASH_ECC_BITS_SHIFT, %g3 ! ecc from diag reg | |
1159 | and %g3, L2_TAG_DIAG_ECC_MASK, %g3 | |
1160 | ||
1161 | ||
1162 | ! %g3 ECC from diagnostic register | |
1163 | ! %g7 calculated ECC | |
1164 | xor %g7, %g3, %g7 | |
1165 | brz,pt %g7, l2_cache_cleanser_ECC_OK ! no error | |
1166 | .empty | |
1167 | ||
1168 | /* | |
1169 | * single-bit or check-bit error. PrefetchICE should | |
1170 | * clean it out. | |
1171 | */ | |
1172 | ! %g4 L2 cache index hashing if set | |
1173 | and %g4, L2_IDX_HASH_EN_STATUS_MASK, %g4 ! restore %g4 | |
1174 | brz,pn %g4, l2_cache_cleanser_prefetch | |
1175 | mov %g1, %g5 | |
1176 | ||
1177 | /* | |
1178 | * index hashing is enabled | |
1179 | * PA[17:11] = PA[32:28] XOR PA[17:13] | PA[19:18] XOR PA[12:11] | |
1180 | */ | |
1181 | N2_PERFORM_IDX_HASH(%g5, %g2, %g3) | |
1182 | ||
1183 | l2_cache_cleanser_prefetch: | |
1184 | ||
1185 | /* | |
1186 | * Invalidate the cache entry (%g5) | |
1187 | */ | |
1188 | prefetch [%g5], INVALIDATE_CACHE_LINE | |
1189 | ||
1190 | l2_cache_cleanser_ECC_OK: | |
1191 | ||
1192 | /* | |
1193 | * To simplify matters, we will abort the loop when %g1, | |
1194 | * the last cache index cleaned, is 0. This will restart | |
1195 | * the cleanser with (index == 0) which will | |
1196 | * re-initialise everything cleanly for us. | |
1197 | */ | |
1198 | setx PREFETCHICE_KEY, %g3, %g7 | |
1199 | andn %g5, %g7, %g7 | |
1200 | brz,a,pn %g7, l2_cache_cleanser_exit | |
1201 | mov %g0, %g1 | |
1202 | dec L2_LINE_SIZE, %g1 ! %g1 next cache index | |
1203 | ||
1204 | ! remember we stashed the bank enable value in the top | |
1205 | ! STASH_BANK_ENABLE_MODE_SHIFT (32) bits of %g6 | |
1206 | ! so we need to clear it before checking (l2scrub_entries-- == 0) | |
1207 | srl %g6, 0, %g3 | |
1208 | brz,pn %g3, l2_cache_cleanser_exit ! %g3 l2scrub_entries | |
1209 | dec %g6 ! %g6 (bank_enable << 32 | l2scrub_entries) | |
1210 | ||
1211 | ! fix up the set:bank | |
1212 | ! again, remember we stashed the bank enable value in the top | |
1213 | ! STASH_BANK_ENABLE_MODE_SHIFT (32) bits of %g6 | |
1214 | srlx %g6, STASH_BANK_ENABLE_MODE_SHIFT, %g3 ! bank enable mode | |
1215 | cmp %g3, 0xff ! 8-bank mode | |
1216 | be %xcc, 1f | |
1217 | cmp %g3, 0xf0 ! 4-bank mode | |
1218 | be %xcc, 2f | |
1219 | cmp %g3, 0xcc ! 4-bank mode | |
1220 | be %xcc, 2f | |
1221 | cmp %g3, 0xc3 ! 4-bank mode | |
1222 | be %xcc, 2f | |
1223 | cmp %g3, 0x3c ! 4-bank mode | |
1224 | be %xcc, 2f | |
1225 | cmp %g3, 0x33 ! 4-bank mode | |
1226 | be %xcc, 2f | |
1227 | cmp %g3, 0xf ! 4-bank mode | |
1228 | be %xcc, 2f | |
1229 | nop | |
1230 | ba 3f | |
1231 | .empty | |
1232 | 1: | |
1233 | setx PREFETCHICE_8BANK_SET_MAX, %g2, %g3 | |
1234 | ! %g3 set[17:9] | |
1235 | setx PREFETCHICE_8BANK_MAX, %g2, %g7 | |
1236 | ! %g7 bank[8:6] | |
1237 | ba 4f | |
1238 | or %g7, %g3, %g7 | |
1239 | ! %g7 set[17:9]:bank[8:6] | |
1240 | ||
1241 | 2: | |
1242 | setx PREFETCHICE_4BANK_SET_MAX, %g2, %g3 | |
1243 | ! %g3 set[16:8] | |
1244 | setx PREFETCHICE_4BANK_MAX, %g2, %g7 | |
1245 | ! %g7 bank[7:6] | |
1246 | ba 4f | |
1247 | or %g7, %g3, %g7 | |
1248 | ! %g7 set[16:8]:bank[7:6] | |
1249 | ||
1250 | 3: | |
1251 | setx PREFETCHICE_2BANK_SET_MAX, %g2, %g3 | |
1252 | ! %g3 set[15:7] | |
1253 | setx PREFETCHICE_2BANK_MAX, %g2, %g7 | |
1254 | ! %g7 bank[6] | |
1255 | or %g7, %g3, %g7 | |
1256 | ! %g7 set[15:7]:bank[6] | |
1257 | ||
1258 | 4: | |
1259 | ! clean up %g1 (key:way:set:bank) | |
1260 | ! with bank mode enabled mask (%g7 set:bank mask) | |
1261 | setx PREFETCHICE_WAY_MAX, %g2, %g3 | |
1262 | or %g7, %g3, %g7 ! way:set:bank mask for bank mode enabled | |
1263 | setx PREFETCHICE_KEY, %g2, %g3 | |
1264 | or %g7, %g3, %g7 ! key:way:set:bank mask for bank mode enabled | |
1265 | ||
1266 | and %g1, %g7, %g1 ! %g1 key:way:set:bank | |
1267 | ||
1268 | ! next line | |
1269 | ba l2_cache_cleanser_check_ECC | |
1270 | nop | |
1271 | ||
1272 | l2_cache_cleanser_exit: | |
1273 | ||
1274 | STRAND_POP(%g7, %g2) ! reload return address | |
1275 | ! set up cyclic for next invocation | |
1276 | ! %g1 next entry to be cleaned | |
1277 | ba l2_cache_cleanser_setup | |
1278 | nop | |
1279 | /*NOTREACHED*/ | |
1280 | ||
1281 | SET_SIZE(l2_cache_cleanser) | |
1282 | ||
1283 | /* | |
1284 | * This function initialises the L2 cache cleanser at startup, | |
1285 | * and also rearms the cyclic after each invocation, (see | |
1286 | * l2_cache_cleanser() above). | |
1287 | * | |
1288 | * %g1 last entry cleaned (clobbered) | |
1289 | * %g2 - %g6 clobbered | |
1290 | * %g7 return address | |
1291 | */ | |
1292 | ENTRY(l2_cache_cleanser_setup) | |
1293 | STRAND_STRUCT(%g6) ! %g6 strand struct | |
1294 | brz,pn %g6, 1f | |
1295 | nop | |
1296 | STRAND2CONFIG_STRUCT(%g6, %g5) | |
1297 | brz,pn %g5, 1f | |
1298 | nop | |
1299 | ldx [%g5 + CONFIG_L2SCRUB_INTERVAL], %g5 | |
1300 | ! if interval is 0, cleanser is disabled | |
1301 | brz,pn %g5, 1f | |
1302 | nop | |
1303 | setx l2_cache_cleanser, %g4, %g2 | |
1304 | RELOC_ADDR(%g2, %g4) ! %g2 = handler address | |
1305 | mov %g1, %g3 ! %g3 = arg 0 : last entry cleaned | |
1306 | VCPU_STRUCT(%g6) | |
1307 | ba cyclic_add_rel ! tail call | |
1308 | mov %g5, %g1 ! %g1 = delta tick | |
1309 | /* NOTREACHED*/ | |
1310 | 1: | |
1311 | HVRET | |
1312 | SET_SIZE(l2_cache_cleanser_setup) | |
1313 | ||
1314 | #endif /* CONFIG_CLEANSER */ | |
1315 | ||
1316 | #ifdef TEST_ERRORS | |
1317 | /* | |
1318 | * Inject errors | |
1319 | */ | |
1320 | ENTRY(inject_l2_errors) | |
1321 | STORE_ERR_RETURN_ADDR(%g7, %g3, %g4) | |
1322 | set (NO_L2_BANKS - 1), %g3 | |
1323 | 1: | |
1324 | ! skip banks which are disabled. causes hang. | |
1325 | SKIP_DISABLED_L2_BANK(%g3, %g4, %g5, 2f) | |
1326 | ||
1327 | mov %g3, %g6 | |
1328 | PRINT_NOTRAP("Active Bank : "); | |
1329 | mov %g6, %g3 | |
1330 | PRINTX(%g3) | |
1331 | PRINT_NOTRAP("\r\n"); | |
1332 | mov %g6, %g3 | |
1333 | ||
1334 | ! L2 Cache LRF error | |
1335 | ! will cause a non-resumable error report to guest | |
1336 | ||
1337 | setx L2_ERROR_INJECTOR, %g4, %g5 | |
1338 | sllx %g3, L2_BANK_SHIFT, %g4 | |
1339 | or %g5, %g4, %g5 | |
1340 | set (L2_ERROR_INJECTOR_ENB_HP), %g4 | |
1341 | stx %g4, [%g5] | |
1342 | 2: | |
1343 | brgz,pt %g3, 1b | |
1344 | dec %g3 | |
1345 | ||
1346 | set (NO_DRAM_BANKS - 1), %g3 | |
1347 | 1: | |
1348 | ! skip banks which are disabled. causes hang. | |
1349 | SKIP_DISABLED_DRAM_BANK(%g3, %g4, %g5, 2f) | |
1350 | ||
1351 | ! cause DAC and resumable error report to guest | |
1352 | setx DRAM_EIR_BASE, %g4, %g5 | |
1353 | sllx %g3, DRAM_BANK_SHIFT, %g4 | |
1354 | or %g5, %g4, %g5 | |
1355 | set ((1 << 31) | 1), %g4 | |
1356 | stx %g4, [%g5] | |
1357 | 2: | |
1358 | brgz,pt %g3, 1b | |
1359 | dec %g3 | |
1360 | ||
1361 | GET_ERR_RETURN_ADDR(%g7, %g3, %g4) | |
1362 | HVRET | |
1363 | ||
1364 | SET_SIZE(inject_l2_errors) | |
1365 | #endif /* TEST_ERRORS */ |