Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: cpu_errs.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 | .ident "@(#)cpu_errs.s 1.82 07/05/03 SMI" | |
50 | ||
51 | #include <sys/asm_linkage.h> | |
52 | #include <sys/htypes.h> | |
53 | #include <hypervisor.h> | |
54 | #include <sparcv9/misc.h> | |
55 | #include <sparcv9/asi.h> | |
56 | #include <asi.h> | |
57 | #include <mmu.h> | |
58 | #include <dram.h> | |
59 | #include <hprivregs.h> | |
60 | #include <sun4v/traps.h> | |
61 | #include <sun4v/asi.h> | |
62 | #include <sun4v/mmu.h> | |
63 | #include <sun4v/queue.h> | |
64 | #include <sun4v/errs_defs.h> | |
65 | #include <fpga.h> | |
66 | ||
67 | #include <offsets.h> | |
68 | #include <cyclic.h> | |
69 | #include <guest.h> | |
70 | #include <strand.h> | |
71 | #include <config.h> | |
72 | #include <cpu_errs.h> | |
73 | #include <errs_common.h> | |
74 | #include <util.h> | |
75 | #include <debug.h> | |
76 | #include <cpu_errs_defs.h> | |
77 | #include <abort.h> | |
78 | #include <iob.h> | |
79 | #include <jbi_regs.h> | |
80 | #include <util.h> | |
81 | ||
82 | ||
83 | /* | |
84 | * HW issues err. HV attempts to handle the error where appropiate. | |
85 | * HV translates it to a sun4v format. Sends it to the queue. | |
86 | */ | |
87 | /* | |
88 | * Macro that calls the function to dump the L2$ set diagnostic | |
89 | * data into the error report. | |
90 | * arg1 MUST be specified as %g1, used as arg1 to function | |
91 | * arg2 MUST be specified as %g2, used as arg2 to function | |
92 | * ret7 MUST be specified as %g7, used for return address | |
93 | * scr1 is scratch register | |
94 | */ | |
95 | /* BEGIN CSTYLED */ | |
96 | #define ASMCALL_DUMP_L2_DATA_FOR_CE(arg1, arg2, scr1, ret7) \ | |
97 | STRAND_STRUCT(scr1) ;\ | |
98 | add scr1, STRAND_CE_RPT, arg2 /* set %g2 to ce_rpt pointer */ ;\ | |
99 | add arg2, STRAND_EVBSC_L2_AFAR(0), arg1 ;\ | |
100 | ldx [arg1], arg1 /* %g1 has physical address */ ;\ | |
101 | ba dump_l2_set_tag_data_ecc ;\ | |
102 | rd %pc, ret7 | |
103 | /* END CSTYLED */ | |
104 | ||
105 | /* BEGIN CSTYLED */ | |
106 | #define SET_CPU_IN_ERROR(scr1, scr2) \ | |
107 | VCPU_STRUCT(scr1) /* FIXME: or strand? */ ;\ | |
108 | mov CPU_STATE_ERROR, scr2 ;\ | |
109 | stx scr2, [scr1 + CPU_STATUS] | |
110 | /* END CSTYLED */ | |
111 | ||
112 | /* | |
113 | * Queue the UE error report as a resumable error to the guest | |
114 | */ | |
115 | /* BEGIN CSTYLED */ | |
116 | #define ASMCALL_RQ_ERPT(E_OFFT, reg1, reg2, reg3, reg4, reg5, reg6, reg7)\ | |
117 | PRINT("queue RESUMABLE\r\n") ;\ | |
118 | STRAND_STRUCT(reg1) ;\ | |
119 | add reg1, E_OFFT, reg2 /* erpt buf ptr */ ;\ | |
120 | ba queue_resumable_erpt /* %g1 = strand, %g2 = erpt */ ;\ | |
121 | rd %pc, reg7 | |
122 | /* END CSTYLED */ | |
123 | ||
124 | /* | |
125 | * The erpt pointer should be passed in %g6 as %g6 is preserved across | |
126 | * print routines. The second argument, reg1, should be %g1, which is | |
127 | * used as the argument to PRINTX. | |
128 | * Arguments: | |
129 | * %g6 - as erpt - pointer to the strand error buffer | |
130 | * %g1 - as reg1 | |
131 | * all registers are used. | |
132 | */ | |
133 | #ifdef NIAGARA_BRINGUP | |
134 | /* BEGIN CSTYLED */ | |
135 | #define CONSOLE_PRINT_DIAG_ERPT(erpt, reg1) \ | |
136 | PRINT("ehdl = ") ;\ | |
137 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_EHDL], reg1 /* ehdl */ ;\ | |
138 | PRINTX(reg1) ;\ | |
139 | PRINT("\r\n") ;\ | |
140 | PRINT("stick = ") ;\ | |
141 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_STICK], reg1 /* stick */ ;\ | |
142 | PRINTX(reg1) ;\ | |
143 | PRINT("\r\n") ;\ | |
144 | PRINT("cpuver = ") ;\ | |
145 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_CPUVER], reg1 /* cpuver */;\ | |
146 | PRINTX(reg1) ;\ | |
147 | PRINT("\r\n") ;\ | |
148 | PRINT("sparc_afsr = ") ;\ | |
149 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR], reg1 /* sparc afsr */;\ | |
150 | PRINTX(reg1) ;\ | |
151 | PRINT("\r\n") ;\ | |
152 | PRINT("sparc_afar = ") ;\ | |
153 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], reg1 /* sparc afar */;\ | |
154 | PRINTX(reg1) ;\ | |
155 | PRINT("\r\n") ;\ | |
156 | PRINT("jbus_err_log = ") ;\ | |
157 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_JBI_ERR_LOG], reg1 ;\ | |
158 | PRINTX(reg1) ;\ | |
159 | PRINT("\r\n") ;\ | |
160 | PRINT("L2 ESRs\r\n") ;\ | |
161 | ldx [erpt + STRAND_EVBSC_L2_AFSR(0)], reg1 ;\ | |
162 | PRINTX(reg1) ;\ | |
163 | PRINT(" ") ;\ | |
164 | ldx [erpt + STRAND_EVBSC_L2_AFSR(1)], reg1 ;\ | |
165 | PRINTX(reg1) ;\ | |
166 | PRINT(" ") ;\ | |
167 | ldx [erpt + STRAND_EVBSC_L2_AFSR(2)], reg1 ;\ | |
168 | PRINTX(reg1) ;\ | |
169 | PRINT(" ") ;\ | |
170 | ldx [erpt + STRAND_EVBSC_L2_AFSR(3)], reg1 ;\ | |
171 | PRINTX(reg1) ;\ | |
172 | PRINT("\r\n") ;\ | |
173 | PRINT("L2 EARs\r\n") ;\ | |
174 | ldx [erpt + STRAND_EVBSC_L2_AFAR(0)], reg1 ;\ | |
175 | PRINTX(reg1) ;\ | |
176 | PRINT(" ") ;\ | |
177 | ldx [erpt + STRAND_EVBSC_L2_AFAR(1)], reg1 ;\ | |
178 | PRINTX(reg1) ;\ | |
179 | PRINT(" ") ;\ | |
180 | ldx [erpt + STRAND_EVBSC_L2_AFAR(2)], reg1 ;\ | |
181 | PRINTX(reg1) ;\ | |
182 | PRINT(" ") ;\ | |
183 | ldx [erpt + STRAND_EVBSC_L2_AFAR(3)], reg1 ;\ | |
184 | PRINTX(reg1) ;\ | |
185 | PRINT("\r\n") ;\ | |
186 | PRINT("DRAM ESRs\r\n") ;\ | |
187 | ldx [erpt + STRAND_EVBSC_DRAM_AFSR(0)], reg1 ;\ | |
188 | PRINTX(reg1) ;\ | |
189 | PRINT(" ") ;\ | |
190 | ldx [erpt + STRAND_EVBSC_DRAM_AFSR(1)], reg1 ;\ | |
191 | PRINTX(reg1) ;\ | |
192 | PRINT(" ") ;\ | |
193 | ldx [erpt + STRAND_EVBSC_DRAM_AFSR(2)], reg1 ;\ | |
194 | PRINTX(reg1) ;\ | |
195 | PRINT(" ") ;\ | |
196 | ldx [erpt + STRAND_EVBSC_DRAM_AFSR(3)], reg1 ;\ | |
197 | PRINTX(reg1) ;\ | |
198 | PRINT("\r\n") ;\ | |
199 | PRINT("DRAM EARs\r\n") ;\ | |
200 | ldx [erpt + STRAND_EVBSC_L2_AFAR(0)], reg1 ;\ | |
201 | PRINTX(reg1) ;\ | |
202 | PRINT(" ") ;\ | |
203 | ldx [erpt + STRAND_EVBSC_L2_AFAR(1)], reg1 ;\ | |
204 | PRINTX(reg1) ;\ | |
205 | PRINT(" ") ;\ | |
206 | ldx [erpt + STRAND_EVBSC_L2_AFAR(2)], reg1 ;\ | |
207 | PRINTX(reg1) ;\ | |
208 | PRINT(" ") ;\ | |
209 | ldx [erpt + STRAND_EVBSC_L2_AFAR(3)], reg1 ;\ | |
210 | PRINTX(reg1) ;\ | |
211 | PRINT("\r\n") ;\ | |
212 | PRINT("DRAM ELRs\r\n") ;\ | |
213 | ldx [erpt + STRAND_EVBSC_DRAM_LOC(0)], reg1 ;\ | |
214 | PRINTX(reg1) ;\ | |
215 | PRINT(" ") ;\ | |
216 | ldx [erpt + STRAND_EVBSC_DRAM_LOC(1)], reg1 ;\ | |
217 | PRINTX(reg1) ;\ | |
218 | PRINT(" ") ;\ | |
219 | ldx [erpt + STRAND_EVBSC_DRAM_LOC(2)], reg1 ;\ | |
220 | PRINTX(reg1) ;\ | |
221 | PRINT(" ") ;\ | |
222 | ldx [erpt + STRAND_EVBSC_DRAM_LOC(3)], reg1 ;\ | |
223 | PRINTX(reg1) ;\ | |
224 | PRINT("\r\n") ;\ | |
225 | PRINT("DRAM ECRs\r\n") ;\ | |
226 | ldx [erpt + STRAND_EVBSC_DRAM_CNTR(0)], reg1 ;\ | |
227 | PRINTX(reg1) ;\ | |
228 | PRINT(" ") ;\ | |
229 | ldx [erpt + STRAND_EVBSC_DRAM_CNTR(1)], reg1 ;\ | |
230 | PRINTX(reg1) ;\ | |
231 | PRINT(" ") ;\ | |
232 | ldx [erpt + STRAND_EVBSC_DRAM_CNTR(2)], reg1 ;\ | |
233 | PRINTX(reg1) ;\ | |
234 | PRINT(" ") ;\ | |
235 | ldx [erpt + STRAND_EVBSC_DRAM_CNTR(3)], reg1 ;\ | |
236 | PRINTX(reg1) ;\ | |
237 | PRINT("\r\n") ;\ | |
238 | PRINT("tstate = ") ;\ | |
239 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_TSTATE], reg1 /* tstate */;\ | |
240 | PRINTX(reg1) ;\ | |
241 | PRINT("\r\n") ;\ | |
242 | PRINT("htstate = ") ;\ | |
243 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_HTSTATE], reg1 /* htstate */;\ | |
244 | PRINTX(reg1) ;\ | |
245 | PRINT("\r\n") ;\ | |
246 | PRINT("tpc = ") ;\ | |
247 | ldx [erpt + STRAND_VBSC_ERPT + EVBSC_TPC], reg1 /* tpc */ ;\ | |
248 | PRINTX(reg1) ;\ | |
249 | PRINT("\r\n") ;\ | |
250 | PRINT("cpuid = ") ;\ | |
251 | lduh [erpt + STRAND_VBSC_ERPT + EVBSC_CPUID], reg1 /* cpuid */;\ | |
252 | PRINTX(reg1) ;\ | |
253 | PRINT("\r\n") ;\ | |
254 | PRINT("TT = ") ;\ | |
255 | lduh [erpt + STRAND_VBSC_ERPT + EVBSC_TT], reg1 /* tt */;\ | |
256 | PRINTX(reg1) ;\ | |
257 | PRINT("\r\n") ;\ | |
258 | PRINT("TL = ") ;\ | |
259 | ldub [erpt + STRAND_VBSC_ERPT + EVBSC_TL], reg1 /* tl */;\ | |
260 | PRINTX(reg1) ;\ | |
261 | PRINT("\r\n") ;\ | |
262 | PRINT("------END-------\r\n") | |
263 | /* END CSTYLED */ | |
264 | #else /* NIAGARA_BRINGUP */ | |
265 | #define CONSOLE_PRINT_DIAG_ERPT(erpt, reg1) | |
266 | #endif /* NIAGARA_BRINGUP */ | |
267 | ||
268 | /* | |
269 | * Correctable error traps can be taken only if PSTATE.IE = 1. | |
270 | * The hypervisor is run with PSTATE.IE = 0, so no CE traps | |
271 | * will be taken when running in hypervisor. Therefore, CE | |
272 | * trap handler is entered only from supervisor which means: | |
273 | * - no need to check for %htstate.hpriv | |
274 | * - no need to check for %tstate.gl == MAXGL | |
275 | * Assume the CE trap taken when executing in supervisor mode. | |
276 | * If TL > MAXPTL | |
277 | * then | |
278 | * watchdog_reset | |
279 | * else | |
280 | * handle error | |
281 | * | |
282 | * For CEs no error report is sent to the sun4v guest. Hence | |
283 | * the sun4v guest error report members of the erpt struct | |
284 | * are not filled in. Only the diagnostic error report is | |
285 | * constructed and sent. | |
286 | * | |
287 | * At entry, PSTATE.IE = 0. | |
288 | * | |
289 | * Register usage: where ever possible | |
290 | * g1-3 = scratch | |
291 | * g4-6 : preserved across PRINT* macros | |
292 | * g5 : error report pointer | |
293 | * g6 : strand struct pointer | |
294 | */ | |
295 | ENTRY_NP(ce_poll_entry) /* entry point for the error daemon */ | |
296 | stx %g7, [%g6 + STRAND_ERR_RET] ! save return address | |
297 | ||
298 | ENTRY_NP(ce_err) | |
299 | ||
300 | /* get strand, CE buffer in %g6-5, they are safe across calls */ | |
301 | STRAND_ERPT_STRUCT(STRAND_CE_RPT, %g6, %g5) ! g6->strand, g5->strand.ce_rpt | |
302 | ||
303 | ! get the lock | |
304 | SPINLOCK_ENTER_ERRORLOCK(%g1, %g2, %g3) | |
305 | ! XXX set the buffer busy flag | |
306 | ||
307 | PRINT("CE_ERR\r\n") | |
308 | CONSOLE_PRINT_ESRS(%g1, %g2, %g3, %g4) | |
309 | ||
310 | /* | |
311 | * Niagara PRM Programming Note: To minimize the possibility of | |
312 | * missing notification of another error, software should clear any | |
313 | * multiple error indication as soon as possible. | |
314 | * | |
315 | * Note: - hardware insures that we will not clear a non-CE error | |
316 | * See PRM 12.4.2 Table 12-6. | |
317 | */ | |
318 | .ce_0: | |
319 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! SPARC afsr | |
320 | .ce_rd_sa: | |
321 | ldxa [%g0]ASI_SPARC_ERR_ADDR, %g3 ! SPARC afar | |
322 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g1 ! re-read afsr | |
323 | cmp %g1, %g4 ! same? | |
324 | bnz,a %xcc, .ce_rd_sa ! no: read both again | |
325 | mov %g1, %g4 ! save last status | |
326 | ||
327 | stxa %g4, [%g0]ASI_SPARC_ERR_STATUS ! clear everything seen | |
328 | ||
329 | stx %g4, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR] ! save afsr | |
330 | stx %g3, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR] ! save afar | |
331 | stx %g0, [%g5 + STRAND_VBSC_ERPT + EVBSC_JBI_ERR_LOG] | |
332 | ||
333 | /* | |
334 | * Check to see if there is any error to process | |
335 | */ | |
336 | CE_CHECK(%g6, %g4, %g1, %g2, %g3) ! strand, spesr, | |
337 | bz,a %xcc, .ce_unlock_exit ! none: exit | |
338 | nop | |
339 | ||
340 | /* | |
341 | * Generate a basic error report | |
342 | * | |
343 | * Sparc status & address are already loaded | |
344 | */ | |
345 | LOAD_BASIC_ERPT(%g6, %g5, %g1, %g2) | |
346 | ||
347 | ! now we have a base diagnostic error report captured that | |
348 | ! can be sent to the SC or diagnosis service provider | |
349 | ||
350 | !! %g5 -> ce_rpt | |
351 | !! %g6 -> strand | |
352 | ||
353 | ! XXX check for TL saturation - why do this for CEs? | |
354 | ! Too drastic to watchdog reset a guest on a corrected error! | |
355 | ! rdpr %tl, %g3 ! get trap level | |
356 | ! cmp %g3, MAXPTL ! is it at max? | |
357 | ! bg,pn %xcc, 1f ! if TL > MAXPTL, watchdog reset | |
358 | ! nop | |
359 | #ifdef DEBUG | |
360 | .pushlocals | |
361 | setx 0xdeadbeefdeadbeef,%g3, %g4 | |
362 | set STRAND_VBSC_ERPT + EVBSC_DIAG_BUF + DIAG_BUF_SIZE-8, %g3 | |
363 | 1: stx %g4, [%g5 + %g3] | |
364 | cmp %g3, STRAND_VBSC_ERPT + EVBSC_DIAG_BUF | |
365 | bgu,pt %xcc, 1b | |
366 | dec 8, %g3 | |
367 | .poplocals | |
368 | #endif /* DEBUG */ | |
369 | ||
370 | /* | |
371 | * At this point we now look for the specific errors: | |
372 | */ | |
373 | lduw [%g6 + STRAND_ERR_FLAG], %g3 | |
374 | btst ERR_FLAG_SPARC, %g3 ! blackout? | |
375 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR], %g3 ! sparc status | |
376 | bnz %xcc, .ce_check_l2 ! yes: check l2 dram | |
377 | ||
378 | set SPARC_CE_BITS, %g4 | |
379 | btst %g4, %g3 ! any valid CE bit set? | |
380 | bz %xcc, .ce_check_l2 ! no SPARC, check L2DRAM | |
381 | nop | |
382 | ||
383 | /* | |
384 | * Sparc Errors: | |
385 | */ | |
386 | mov %g5, %g2 ! g2 = cpu.ce_erpt | |
387 | set SPARC_ESR_IRC, %g4 | |
388 | btst %g4, %g3 ! is IRC set? | |
389 | bnz %xcc, .ce_irc_err | |
390 | nop | |
391 | ||
392 | set SPARC_ESR_FRC, %g4 | |
393 | btst %g4, %g3 ! is FRC set? | |
394 | bnz %xcc, .ce_frc_err | |
395 | nop | |
396 | ||
397 | set SPARC_ESR_DTC, %g4 | |
398 | btst %g4, %g3 ! is DTC set? | |
399 | bnz %xcc, .ce_dtc_err | |
400 | nop | |
401 | ||
402 | set SPARC_ESR_DDC, %g4 | |
403 | btst %g4, %g3 ! is DDC set? | |
404 | bnz %xcc, .ce_ddc_err | |
405 | nop | |
406 | ||
407 | set SPARC_ESR_IDC, %g4 | |
408 | btst %g4, %g3 ! is IDC set? | |
409 | bnz %xcc, .ce_idc_err | |
410 | nop | |
411 | ||
412 | set SPARC_ESR_ITC, %g4 | |
413 | btst %g4, %g3 ! is ITC set? | |
414 | bnz %xcc, .ce_itc_err | |
415 | nop | |
416 | ||
417 | ! SPARC ESR may have a CE bit and/or MEC bit set | |
418 | set SPARC_ESR_MEC, %g4 | |
419 | btst %g4, %g3 ! MEC bit set? | |
420 | bnz %xcc, .ce_just_mec | |
421 | nop | |
422 | ||
423 | ! should not get here as all CE conditions have been tested | |
424 | PRINT("NOTE: Sparc CE: failed to find error bit set!!") | |
425 | ba,a .ce_no_error | |
426 | ||
427 | ||
428 | ! IRC error handler | |
429 | .ce_irc_err: | |
430 | PRINT("IRC DIAG\r\n") | |
431 | ! set up %g1 as first arg to irc_check() | |
432 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 ! arg1 = EAR | |
433 | HVCALL(irc_check) ! %g2 is return value | |
434 | cmp %g2, RF_TRANSIENT | |
435 | be 1f ! transient IRC | |
436 | nop | |
437 | ! persistent IRC error, | |
438 | ! let storm protection throttle irc and iru reports | |
439 | PRINT("persistent IRC error\r\n") | |
440 | ba .ce_sparc_storm ! finish up | |
441 | clr %g1 ! no print or send | |
442 | ||
443 | 1: | |
444 | ! send the sparc_err_ebl reg to the diag eng | |
445 | ldxa [%g0]ASI_SPARC_ERR_EN, %g1 | |
446 | stx %g1, [%g5 + EVBSC_DIAG_BUF + DIAG_BUF_REG_INFO] | |
447 | ba,a .ce_send_sparc_erpt ! send report & finish up | |
448 | ||
449 | ||
450 | ! Default CE error handler. | |
451 | ! This just sends the CE diagnostic error report to the | |
452 | ! vBSC to generate an FMA error report. | |
453 | /* | |
454 | * L1 Instruction Cache: | |
455 | */ | |
456 | .ce_itc_err: /* Tag */ | |
457 | PRINT("ITC DIAG\r\n") | |
458 | DUMP_ICACHE_INFO(STRAND_CE_RPT, %g1, %g5, %g3, %g4, %g2, %g6, %g7) | |
459 | ba,a .ce_send_sparc_erpt | |
460 | ||
461 | .ce_idc_err: | |
462 | PRINT("IDC DIAG\r\n") | |
463 | DUMP_ICACHE_INFO(STRAND_CE_RPT, %g1, %g5, %g3, %g4, %g2, %g6, %g7) | |
464 | ba,a .ce_send_sparc_erpt | |
465 | ||
466 | /* | |
467 | * L1 Data Cache: | |
468 | */ | |
469 | .ce_dtc_err: /* Tag */ | |
470 | PRINT("DTC DIAG\r\n") | |
471 | DUMP_DCACHE_INFO(STRAND_CE_RPT, %g6, %g5, %g1, %g2, %g3, %g4, %g7) | |
472 | ba,a .ce_send_sparc_erpt | |
473 | ||
474 | ||
475 | .ce_ddc_err: /* Data */ | |
476 | PRINT("DDC DIAG\r\n") | |
477 | DUMP_DCACHE_INFO(STRAND_CE_RPT, %g6, %g5, %g1, %g2, %g3, %g4, %g7) | |
478 | ba,a .ce_send_sparc_erpt | |
479 | /* | |
480 | * Float Register Correctable: | |
481 | */ | |
482 | .ce_frc_err: | |
483 | PRINT("FRC DIAG\r\n") | |
484 | ! set up %g1 as first arg to frc_check() | |
485 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 | |
486 | !! %g1 = EAR | |
487 | HVCALL(frc_check) | |
488 | !! %g2 = return value | |
489 | cmp %g2, RF_TRANSIENT | |
490 | be 1f ! transient FRC | |
491 | nop | |
492 | ! persistent FRC error, | |
493 | ! let storm protection throttle frc and fru reports | |
494 | PRINT("persistent FRC error\r\n") | |
495 | ba .ce_sparc_storm ! finish up | |
496 | clr %g1 ! no print or send | |
497 | 1: | |
498 | ! send the sparc_err_ebl reg to the diag eng | |
499 | ldxa [%g0]ASI_SPARC_ERR_EN, %g1 | |
500 | stx %g1, [%g5 + EVBSC_DIAG_BUF + DIAG_BUF_REG_INFO] | |
501 | ba,a .ce_send_sparc_erpt ! send report & finish up | |
502 | ||
503 | .ce_just_mec: | |
504 | PRINT("JUST MEC\r\n") | |
505 | ba,a .ce_send_sparc_erpt ! send report & finish up | |
506 | ||
507 | .ce_send_sparc_erpt: | |
508 | /* | |
509 | * Note: this path is taken also for "MEC only" and "nothing found". | |
510 | * It will throttle "false" interrupts. | |
511 | */ | |
512 | STRAND_STRUCT(%g6) | |
513 | add %g6, STRAND_CE_RPT, %g5 ! g5 -> strand.ce_rpt | |
514 | ||
515 | set ERR_SEND_DIAG, %g1 | |
516 | SET_STRAND_RPTFLAGS(%g6, %g1) | |
517 | ||
518 | /* | |
519 | * Storm Prevention: | |
520 | * | |
521 | * This code prevents more than one error every time period from | |
522 | * the group: SPARC Register File & L1$ | |
523 | */ | |
524 | .ce_sparc_storm: | |
525 | lduw [%g6 + STRAND_ERR_FLAG], %g2 | |
526 | btst ERR_FLAG_SPARC, %g2 ! handler installed? | |
527 | bnz,pn %xcc, .ce_sparc_storm_done ! yes | |
528 | ||
529 | bset ERR_FLAG_SPARC, %g2 ! no: set it | |
530 | STRAND2CONFIG_STRUCT(%g6, %g1) ! ->configp | |
531 | ldx [%g1 + CONFIG_CE_BLACKOUT], %g1 | |
532 | brz,a,pn %g1, .ce_sparc_storm_done ! zero: blackout disabled | |
533 | nop | |
534 | stw %g2, [%g6 + STRAND_ERR_FLAG] ! flag as installed | |
535 | ! g1 = delta tick | |
536 | HVCALL(err_set_sparc_bits) ! g2 = handler address | |
537 | set CEEN, %g3 ! g3 = arg 0 : bit(s) to set | |
538 | clr %g4 ! g4 = arg 1 : not used | |
539 | HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */ | |
540 | .ce_sparc_storm_done: | |
541 | ba,a ce_err_ret | |
542 | ||
543 | /* | |
544 | * L2DRAM Error Handling: | |
545 | */ | |
546 | /* g6->strand, g5->ce_rpt */ | |
547 | .ce_check_l2: | |
548 | /* | |
549 | * L2DRAM errors are global and may not be valid for this cpu. | |
550 | * Process if PID == ERRORSTEER, or this cpu was sent the error. | |
551 | */ | |
552 | DUMP_L2_DRAM_ERROR_LOGS(%g6, %g5, %g1, %g2, %g3, %g4, %g7) | |
553 | /* | |
554 | * Only one error in one bank will be processed | |
555 | * each pass through here. | |
556 | * | |
557 | * Note: storm prevention will block processing of banks | |
558 | * in a blackout | |
559 | */ | |
560 | ! go through each L2 bank and check for valid CE bits | |
561 | .ce_check_l2_b0: | |
562 | CE_CHECK_L2_ESR(0, %g6, %g4, %g1, %g2) | |
563 | bz %xcc, .ce_check_l2_b1 ! check next bank | |
564 | nop | |
565 | SET_STRAND_L2BANK(0, %g6, %g7) ! save bank# | |
566 | ! dump all of the l2 info. must pass the registers as is | |
567 | DUMP_L2_SET_TAG_DATA(0, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
568 | ! dram data here since all L2 esr need it | |
569 | ldx [%g6 + STRAND_CE_RPT + STRAND_EVBSC_L2_AFSR(0)], %g4 ! l2esr | |
570 | setx L2_ESR_CE_NO_EAR_BITS, %g1, %g2 | |
571 | btst %g4, %g2 | |
572 | bz,pn %xcc, 1f | |
573 | nop | |
574 | CLEAR_DRAM_CONTENTS(0, STRAND_CE_RPT, %g6, %g5) | |
575 | ba 2f | |
576 | nop | |
577 | 1: | |
578 | DUMP_DRAM_CONTENTS(0, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
579 | 2: | |
580 | /* %g6->cpu %g4=l2esr */ | |
581 | CLEAR_L2_ESR(0, %g4, %g1, %g2) | |
582 | /* 6->strand 4=l2esr */ | |
583 | PROCESS_CE_IN_L2_ESR(0, %g6, %g5, %g4, %g1, %g2, %g3) | |
584 | /* 6->strand 5->erpt 4=flags: action */ | |
585 | ba,a .ce_l2_all | |
586 | ||
587 | .ce_check_l2_b1: | |
588 | CE_CHECK_L2_ESR(1, %g6, %g4, %g1, %g2) | |
589 | bz %xcc, .ce_check_l2_b2 ! check next bank | |
590 | nop | |
591 | SET_STRAND_L2BANK(1, %g6, %g7) ! save bank# | |
592 | ! dump all of the l2 info. must pass the registers as is | |
593 | DUMP_L2_SET_TAG_DATA(1, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
594 | ! dram data here since all L2 esr need it | |
595 | ldx [%g6 + STRAND_CE_RPT + STRAND_EVBSC_L2_AFSR(1)], %g4 ! l2esr | |
596 | setx L2_ESR_CE_NO_EAR_BITS, %g1, %g2 | |
597 | btst %g4, %g2 | |
598 | bz,pn %xcc, 1f | |
599 | nop | |
600 | CLEAR_DRAM_CONTENTS(0, STRAND_CE_RPT, %g6, %g5) | |
601 | ba 2f | |
602 | nop | |
603 | 1: | |
604 | DUMP_DRAM_CONTENTS(1, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
605 | 2: | |
606 | /* %g6->cpu %g4=l2esr */ | |
607 | CLEAR_L2_ESR(1, %g4, %g1, %g2) | |
608 | PROCESS_CE_IN_L2_ESR(1, %g6, %g5, %g4, %g1, %g2, %g3) | |
609 | ba,a .ce_l2_all | |
610 | ||
611 | .ce_check_l2_b2: | |
612 | CE_CHECK_L2_ESR(2, %g6, %g4, %g1, %g2) | |
613 | bz %xcc, .ce_check_l2_b3 ! check next bank | |
614 | nop | |
615 | SET_STRAND_L2BANK(2, %g6, %g7) ! save bank# | |
616 | ! dump all of the l2 info. must pass the registers as is | |
617 | DUMP_L2_SET_TAG_DATA(2, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
618 | ! dram data here since all L2 esr need it | |
619 | ldx [%g6 + STRAND_CE_RPT + STRAND_EVBSC_L2_AFSR(2)], %g4 ! l2esr | |
620 | setx L2_ESR_CE_NO_EAR_BITS, %g1, %g2 | |
621 | btst %g4, %g2 | |
622 | bz,pn %xcc, 1f | |
623 | nop | |
624 | CLEAR_DRAM_CONTENTS(0, STRAND_CE_RPT, %g6, %g5) | |
625 | ba 2f | |
626 | nop | |
627 | 1: | |
628 | DUMP_DRAM_CONTENTS(2, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
629 | 2: | |
630 | /* %g6->cpu %g4=l2esr */ | |
631 | CLEAR_L2_ESR(2, %g4, %g1, %g2) | |
632 | PROCESS_CE_IN_L2_ESR(2, %g6, %g5, %g4, %g1, %g2, %g3) | |
633 | ba,a .ce_l2_all | |
634 | ||
635 | .ce_check_l2_b3: | |
636 | CE_CHECK_L2_ESR(3, %g6, %g4, %g1, %g2) | |
637 | bz %xcc, .ce_no_error | |
638 | nop | |
639 | SET_STRAND_L2BANK(3, %g6, %g7) ! save bank# | |
640 | ! dump all of the l2 info. must pass the registers as is | |
641 | DUMP_L2_SET_TAG_DATA(3, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
642 | ! dram data here since all L2 esr need it | |
643 | ldx [%g6 + STRAND_CE_RPT + STRAND_EVBSC_L2_AFSR(3)], %g4 ! l2esr | |
644 | setx L2_ESR_CE_NO_EAR_BITS, %g1, %g2 | |
645 | btst %g4, %g2 | |
646 | bz,pn %xcc, 1f | |
647 | nop | |
648 | CLEAR_DRAM_CONTENTS(0, STRAND_CE_RPT, %g6, %g5) | |
649 | ba 2f | |
650 | nop | |
651 | 1: | |
652 | DUMP_DRAM_CONTENTS(3, STRAND_CE_RPT, %g6, %g5, %g1, %g2) | |
653 | 2: | |
654 | /* %g6->cpu %g4=l2esr */ | |
655 | CLEAR_L2_ESR(3, %g4, %g1, %g2) | |
656 | PROCESS_CE_IN_L2_ESR(3, %g6, %g5, %g4, %g1, %g2, %g3) | |
657 | ba,a .ce_l2_all | |
658 | ||
659 | .ce_l2_all: | |
660 | brlz %g4, .ce_no_error ! no error found - exit now | |
661 | nop | |
662 | SET_STRAND_RPTFLAGS(%g6, %g4) | |
663 | ||
664 | /* | |
665 | * Storm Prevention: | |
666 | * | |
667 | * This code prevents more than one error every six seconds from | |
668 | * the groups: L2$, DRAM Banks. Since the enables are system wide | |
669 | * we use the error enable bits to indicate the blackout period. | |
670 | * The callback flag is used to indicate if the handler is enabled | |
671 | * on this cpu. | |
672 | */ | |
673 | /* | |
674 | * There is a very small window where multiple interrupts can be | |
675 | * delivered to more than one cpu. | |
676 | * Only one will get through this set successfully. | |
677 | */ | |
678 | .ce_l2dram_storm: | |
679 | GET_STRAND_L2BANK(%g6, %g4) | |
680 | BCLR_L2_BANK_EEN(%g4, CEEN, %g1, %g2) ! g4 = bank# | |
681 | bz %xcc, .ce_l2dram_storm_done ! already disabled | |
682 | nop | |
683 | mov ERR_FLAG_L2DRAM, %g1 ! L2DRAM flag | |
684 | sll %g1, %g4, %g1 ! << bank# | |
685 | lduw [%g6 + STRAND_ERR_FLAG], %g2 ! installed flags | |
686 | btst %g1, %g2 ! handler installed? | |
687 | bnz,pn %xcc, .ce_l2dram_storm_done ! yes | |
688 | ||
689 | bset %g1, %g2 ! no: set it | |
690 | STRAND2CONFIG_STRUCT(%g6, %g1) ! ->configp | |
691 | ldx [%g1 + CONFIG_CE_BLACKOUT], %g1 | |
692 | brz,a,pn %g1, .ce_l2dram_storm_done ! zero: blackout disabled | |
693 | nop | |
694 | stw %g2, [%g6 + STRAND_ERR_FLAG] ! handler installed | |
695 | ! g1 = delta tick | |
696 | HVCALL(err_set_l2_bits) ! g2 = handler address | |
697 | mov CEEN, %g3 ! g3 = arg 0 : bit(s) to set | |
698 | ! g4 = arg 1 : B5-0: bank # | |
699 | HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */ | |
700 | .ce_l2dram_storm_done: | |
701 | ba,a ce_err_ret | |
702 | ||
703 | ENTRY_NP(ce_err_ret) | |
704 | ||
705 | STRAND_STRUCT(%g6) | |
706 | GET_STRAND_RPTFLAGS(%g6, %g4) ! g4: flags: action | |
707 | ||
708 | btst ERR_SEND_DIAG, %g4 ! send diag report? | |
709 | bz %xcc, .ce_unlock_exit ! no | |
710 | nop | |
711 | ! send CE diag report | |
712 | add %g6, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
713 | add %g6, STRAND_CE_RPT + STRAND_UNSENT_PKT, %g2 ! erpt.unsent flag | |
714 | mov EVBSC_SIZE, %g3 ! size | |
715 | HVCALL(send_diag_erpt) ! g4-6 clobbered | |
716 | STRAND_STRUCT(%g6) | |
717 | SET_STRAND_RPTFLAGS(%g6, %g0) ! clear report flags | |
718 | ba,a .ce_unlock_exit ! handler epilogue | |
719 | ||
720 | ! XXX CEs should never watchdog_reset a guest??? | |
721 | ! XXX It should also not inadvertently let a guest run at TL > MAXPTL | |
722 | ! send the error report to the diagnostic service provider | |
723 | ! before watchdog_guest | |
724 | ||
725 | ! ba,a watchdog_guest | |
726 | /*NOTREACHED*/ | |
727 | ||
728 | /* | |
729 | * Sparc and L2DRAM checked with no error to report: | |
730 | */ | |
731 | .ce_no_error: | |
732 | ! Some other thread beat us to it, or we don't own it, or | |
733 | ! the blackout(s) have left us nothing to report. | |
734 | PRINT("NOTE: No Reportable Error\r\n") | |
735 | ||
736 | /* | |
737 | * CE epilogue | |
738 | * The CE error handlers return here after handling the error. | |
739 | */ | |
740 | .ce_unlock_exit: | |
741 | /* MUST leave Sparc CEEN enabled to get L2DRAM interrupts! */ | |
742 | /* Reenable CEEN */ | |
743 | ldxa [%g0]ASI_SPARC_ERR_EN, %g1 ! get current | |
744 | bset CEEN, %g1 ! enable CEEN | |
745 | stxa %g1, [%g0] ASI_SPARC_ERR_EN ! .. | |
746 | ||
747 | /* | |
748 | * With CE storm prevention, the CEEN will be reenabled by the | |
749 | * hstick_match handler when errors stop. | |
750 | */ | |
751 | SPINLOCK_EXIT_ERRORLOCK(%g1) ! release lock | |
752 | ||
753 | ba,a .ce_exit ! exit now | |
754 | ||
755 | .ce_exit: | |
756 | ldx [%g6 + STRAND_ERR_RET], %g7 ! get return address | |
757 | brnz,a %g7, .ce_return ! valid: clear it & return | |
758 | stx %g0, [%g6+ STRAND_ERR_RET] ! .. | |
759 | SET_SIZE(ce_poll_entry) | |
760 | ! NULL: return from interrupt | |
761 | retry ! return from CE interrupt | |
762 | ||
763 | .ce_return: | |
764 | HVRET | |
765 | SET_SIZE(ce_err_ret) | |
766 | SET_SIZE(ce_err) | |
767 | ||
768 | ||
769 | /* | |
770 | * Disrupting uncorrectable error handler. | |
771 | * All of these errors are resumable errors to the guest. I.e. they | |
772 | * are not nonresumable errors. | |
773 | * | |
774 | * At entry, PSTATE.IE = 0, so no furthur disrupting error traps. | |
775 | * | |
776 | * The CE error report buffer is used for reporting. | |
777 | */ | |
778 | ENTRY_NP(dis_ue_err) | |
779 | ||
780 | /* | |
781 | * Check for DBU in DRAM ESR | |
782 | */ | |
783 | CHECK_DRAM_ERROR(DRAM_ESR_DBU, %g1, %g2, %g3, %g4) | |
784 | bnz,pn %xcc, .fatal_reset_dbu ! yes: bail now | |
785 | nop | |
786 | ||
787 | /* get strand, CE buffer in %g6-5, they are safe across calls */ | |
788 | STRAND_ERPT_STRUCT(STRAND_CE_RPT, %g6, %g5) ! g6->strand, g5->strand.ce_rpt | |
789 | ||
790 | /* | |
791 | * We do not idle all strands if the scrubber got a UE | |
792 | */ | |
793 | CHECK_L2_ERROR(L2_ESR_LDSU, %g1, %g2, %g3) | |
794 | bnz,pn %xcc, .dis_ue_no_idle | |
795 | mov ERR_FLAG_STRANDS_NOT_IDLED, %g1 | |
796 | CHECK_DRAM_ERROR(DRAM_ESR_DSU, %g1, %g2, %g3, %g4) | |
797 | bnz,pn %xcc, .dis_ue_no_idle | |
798 | mov ERR_FLAG_STRANDS_NOT_IDLED, %g1 | |
799 | ||
800 | SPINLOCK_IDLE_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
801 | ! At this point, this is the only strand executing | |
802 | mov %g0, %g1 | |
803 | ||
804 | .dis_ue_no_idle: | |
805 | ||
806 | lduw [%g6 + STRAND_ERR_FLAG], %g2 ! installed flags | |
807 | bclr ERR_FLAG_STRANDS_NOT_IDLED, %g2 ! reset STRANDS_IDLED | |
808 | or %g2, %g1, %g2 | |
809 | stw %g2, [%g6 + STRAND_ERR_FLAG] ! .. | |
810 | ||
811 | PRINT("DATA ERR\r\n") | |
812 | CONSOLE_PRINT_ESRS(%g1, %g2, %g3, %g4) | |
813 | ||
814 | /* | |
815 | * Niagara PRM Programming Note: To minimize the possibility of | |
816 | * missing notification of an error, software should any multiple | |
817 | * error indication as soon as possible. | |
818 | */ | |
819 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! SPARC afsr | |
820 | .dis_ue_rd_sa: | |
821 | ldxa [%g0]ASI_SPARC_ERR_ADDR, %g3 ! SPARC afar | |
822 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g1 ! re-read afsr | |
823 | cmp %g1, %g4 ! same? | |
824 | bnz,a %xcc, .dis_ue_rd_sa ! no: read both again | |
825 | mov %g1, %g4 ! save last status | |
826 | stxa %g4, [%g0]ASI_SPARC_ERR_STATUS ! clear SPARC afsr | |
827 | ||
828 | ! save ce_rpt.sparc_afsr | |
829 | stx %g4, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR] | |
830 | ! save ce_rpt.sparc_afar | |
831 | stx %g3, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR] | |
832 | stx %g0, [%g5 + STRAND_VBSC_ERPT + EVBSC_JBI_ERR_LOG] | |
833 | ||
834 | /* | |
835 | * Generate a basic error report | |
836 | * | |
837 | * Sparc status & address are already loaded | |
838 | */ | |
839 | LOAD_BASIC_ERPT(%g6, %g5, %g1, %g2) | |
840 | ||
841 | mov %g6, %g1 ! strand | |
842 | mov %g5, %g2 ! strand.ue_erpt | |
843 | ||
844 | #ifdef DEBUG | |
845 | .pushlocals | |
846 | setx 0xdeadbeefdeadbeef,%g3, %g4 | |
847 | set STRAND_VBSC_ERPT + EVBSC_DIAG_BUF + DIAG_BUF_SIZE-8, %g3 | |
848 | 1: stx %g4, [%g5 + %g3] | |
849 | cmp %g3, STRAND_VBSC_ERPT + EVBSC_DIAG_BUF | |
850 | bgu,pt %xcc, 1b | |
851 | dec 8, %g3 | |
852 | .poplocals | |
853 | #endif | |
854 | ! check for MAU error | |
855 | /* Dump the L2 and DRAM registers also */ | |
856 | ! %g1 has strand pointer, %g2 has &ce_rpt - pointer to error report | |
857 | DUMP_L2_DRAM_ERROR_LOGS(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
858 | ||
859 | ! go through each L2 bank and check for valid UE bits | |
860 | .dis_ue_check_l2_b0: | |
861 | DIS_UE_CHECK_L2_ESR(0, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
862 | bz %xcc, .dis_ue_check_l2_b1 ! check next bank | |
863 | nop | |
864 | /* save the state of the line */ | |
865 | SAVE_L2_LINE_STATE(0, STRAND_CE_RPT, %g1, %g2) | |
866 | !! %g1= strand | |
867 | !! %g2 = erpt | |
868 | DUMP_L2_SET_TAG_DATA(0, STRAND_CE_RPT, %g1, %g2, %g1, %g2) | |
869 | !! %g1 = cpu | |
870 | !! %g2 = cpu.erpt | |
871 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(0)], %g4 ! l2esr | |
872 | CLEAR_L2_ESR(0, %g4, %g5, %g6) ! clear L2 ESR | |
873 | PROCESS_DIS_UE_IN_L2_ESR(0, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
874 | .dis_ue_err_ret, .ue_resume_exit) | |
875 | ||
876 | .dis_ue_check_l2_b1: | |
877 | DIS_UE_CHECK_L2_ESR(1, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
878 | bz %xcc, .dis_ue_check_l2_b2 ! check next bank | |
879 | nop | |
880 | /* save the state of the line */ | |
881 | SAVE_L2_LINE_STATE(1, STRAND_CE_RPT, %g1, %g2) | |
882 | !! %g1= strand | |
883 | !! %g2 = erpt | |
884 | DUMP_L2_SET_TAG_DATA(1, STRAND_CE_RPT, %g1, %g2, %g1, %g2) | |
885 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(1)], %g4 ! l2esr | |
886 | CLEAR_L2_ESR(1, %g4, %g5, %g6) ! clear L2 ESR | |
887 | PROCESS_DIS_UE_IN_L2_ESR(1, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
888 | .dis_ue_err_ret, .ue_resume_exit) | |
889 | ||
890 | .dis_ue_check_l2_b2: | |
891 | DIS_UE_CHECK_L2_ESR(2, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
892 | bz %xcc, .dis_ue_check_l2_b3 ! check next bank | |
893 | nop | |
894 | /* save the state of the line */ | |
895 | SAVE_L2_LINE_STATE(2, STRAND_CE_RPT, %g1, %g2) | |
896 | !! %g1= strand | |
897 | !! %g2 = erpt | |
898 | DUMP_L2_SET_TAG_DATA(2, STRAND_CE_RPT, %g1, %g2, %g1, %g2) | |
899 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(2)], %g4 ! l2esr | |
900 | CLEAR_L2_ESR(2, %g4, %g5, %g6) ! clear L2 ESR | |
901 | PROCESS_DIS_UE_IN_L2_ESR(2, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
902 | .dis_ue_err_ret, .ue_resume_exit) | |
903 | ||
904 | .dis_ue_check_l2_b3: | |
905 | DIS_UE_CHECK_L2_ESR(3, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
906 | bz %xcc, .dis_ue_no_error ! XXX spurious? | |
907 | nop | |
908 | /* save the state of the line */ | |
909 | SAVE_L2_LINE_STATE(3, STRAND_CE_RPT, %g1, %g2) | |
910 | !! %g1= strand | |
911 | !! %g2 = erpt | |
912 | DUMP_L2_SET_TAG_DATA(3, STRAND_CE_RPT, %g1, %g2, %g1, %g2) | |
913 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(3)], %g4 ! l2esr | |
914 | CLEAR_L2_ESR(3, %g4, %g5, %g6) ! clear L2 ESR | |
915 | PROCESS_DIS_UE_IN_L2_ESR(3, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
916 | .dis_ue_err_ret, .ue_resume_exit) | |
917 | ! | |
918 | ! All banks checked, now return | |
919 | ! | |
920 | ba,a .dis_ue_err_ret ! UE handler epilogue | |
921 | /*NOTREACHED*/ | |
922 | ||
923 | .dis_ue_no_error: | |
924 | PRINT("NO DIS UE ERROR\r\n") | |
925 | ! some other thread beat us to it. | |
926 | ! no bits in L2, simply return (XXX send a service error report?) | |
927 | ! send CE diag report | |
928 | STRAND_STRUCT(%g6) | |
929 | add %g6, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
930 | add %g6, STRAND_CE_RPT + STRAND_UNSENT_PKT, %g2 ! erpt.unsent flag | |
931 | mov EVBSC_SIZE, %g3 ! size | |
932 | HVCALL(send_diag_erpt) | |
933 | ||
934 | ba,a .dis_ue_err_ret ! CE handler epilogue | |
935 | /*NOTREACHED*/ | |
936 | SET_SIZE(dis_ue_err) | |
937 | ||
938 | /* | |
939 | * General handling of UEs | |
940 | * if HTSTATE[TL].GL == MAXPGL | |
941 | * reset chip and partitions | |
942 | * else if HTSTATE.PRIV == 1 | |
943 | * reset chip and partitions | |
944 | * else if TL > MAXPTL then watchdog_reset | |
945 | * else call common handler | |
946 | */ | |
947 | ||
948 | /* | |
949 | * Uncorrectable error traps can be taken any time NCEEN | |
950 | * in the SPARC error status register is set. | |
951 | * UEs can occur when executing in the hypervisor, supervisor, | |
952 | * or user code. | |
953 | * | |
954 | * XXX UEs when executing in hypervisor resets the system XXX | |
955 | * TL overflow causes guest to be reset | |
956 | */ | |
957 | ENTRY_NP(ue_poll_entry) /* entry point for the error daemon */ | |
958 | ! %g6->strand | |
959 | stx %g7, [%g6 + STRAND_ERR_RET] ! save return address | |
960 | ||
961 | ba,a ue_err_notrap | |
962 | .empty | |
963 | ||
964 | ENTRY_NP(ue_err) | |
965 | ||
966 | /* | |
967 | * Check for global register saturation and save the current | |
968 | * global register set if necessary. | |
969 | */ | |
970 | SAVE_UE_GLOBALS() | |
971 | ||
972 | ue_err_notrap: | |
973 | /* | |
974 | * Check for DBU in DRAM ESR | |
975 | */ | |
976 | CHECK_DRAM_ERROR(DRAM_ESR_DBU, %g1, %g2, %g3, %g4) | |
977 | bnz,pn %xcc, .fatal_reset_dbu ! yes: bail now | |
978 | nop | |
979 | ||
980 | /* get strand, UE buffer in %g6-5, they are safe across calls */ | |
981 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g6, %g5) ! g6->strand, g5->strand.ue_rpt | |
982 | ||
983 | /* | |
984 | * check to see if UE occurred in hypervisor | |
985 | * We check early in order to avoid a deadlock situation. | |
986 | * in the previous trap, we were handling either a dis UE or a CE | |
987 | */ | |
988 | rdhpr %htstate, %g1 | |
989 | btst HTSTATE_HPRIV, %g1 | |
990 | bnz %xcc, .ue_get_status_addr ! UE in hypervisor | |
991 | nop | |
992 | ||
993 | SPINLOCK_IDLE_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
994 | ! At this point, this is the only strand executing | |
995 | ||
996 | #ifdef DEBUG | |
997 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g1 ! SPARC afsr | |
998 | set SPARC_ESR_NCU, %g4 ! Ifetch/Load from IO space bit | |
999 | btst %g4, %g1 ! NCU set? | |
1000 | bnz %xcc, .skip_print_esrs ! skip printing ESRs | |
1001 | nop | |
1002 | ||
1003 | PRINT("UE_ERR\r\n") | |
1004 | CONSOLE_PRINT_ESRS(%g1, %g2, %g3, %g4) | |
1005 | .skip_print_esrs: | |
1006 | #endif /* DEBUG */ | |
1007 | ||
1008 | ||
1009 | .ue_get_status_addr: | |
1010 | /* | |
1011 | * Niagara PRM Programming Note: To minimize the possibility of | |
1012 | * missing notification of an error, software should clear the | |
1013 | * error indication as soon as possible. | |
1014 | */ | |
1015 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! SPARC afsr | |
1016 | .ue_rd_sa: | |
1017 | ldxa [%g0]ASI_SPARC_ERR_ADDR, %g3 ! SPARC afar | |
1018 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g1 ! re-read afsr | |
1019 | cmp %g1, %g4 ! same? | |
1020 | bnz,a %xcc, .ue_rd_sa ! no: read both again | |
1021 | mov %g1, %g4 ! save last status | |
1022 | ||
1023 | stxa %g4, [%g0]ASI_SPARC_ERR_STATUS ! clear everything seen | |
1024 | ||
1025 | ! save ue_rpt.sparc_afsr | |
1026 | stx %g4, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR] | |
1027 | ! save ue_rpt.sparc_afar | |
1028 | stx %g3, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR] | |
1029 | stx %g0, [%g5 + STRAND_VBSC_ERPT + EVBSC_JBI_ERR_LOG] | |
1030 | ||
1031 | /* | |
1032 | * Check to see if there is any error to process | |
1033 | */ | |
1034 | UE_CHECK(SPARC_UE_MEU_BITS, L2_ESR_UE_BITS, %g4, %g1, %g2, %g3) | |
1035 | bz,a %xcc, .ue_resume_exit ! none: exit | |
1036 | nop | |
1037 | ||
1038 | /* | |
1039 | * Generate a basic error report | |
1040 | * | |
1041 | * Sparc status & address are already loaded | |
1042 | */ | |
1043 | LOAD_BASIC_ERPT(%g6, %g5, %g1, %g2) | |
1044 | ||
1045 | mov %g6, %g1 ! strand | |
1046 | mov %g5, %g2 ! strand.ue_erpt | |
1047 | ||
1048 | #ifdef DEBUG | |
1049 | .pushlocals | |
1050 | setx 0xdeadbeefdeadbeef,%g3, %g4 | |
1051 | set STRAND_VBSC_ERPT + EVBSC_DIAG_BUF + DIAG_BUF_SIZE-8, %g3 | |
1052 | 1: stx %g4, [%g5 + %g3] | |
1053 | cmp %g3, STRAND_VBSC_ERPT + EVBSC_DIAG_BUF | |
1054 | bgu,pt %xcc, 1b | |
1055 | dec 8, %g3 | |
1056 | .poplocals | |
1057 | #endif | |
1058 | ! set error descriptor to UE resumable | |
1059 | set EDESC_UE_RESUMABLE, %g3 | |
1060 | ! edesc in guest erpt | |
1061 | st %g3, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_EDESC] | |
1062 | ||
1063 | ! check SPARC ESR for thread-specific errors | |
1064 | ! %g3 = saved sparc_afsr | |
1065 | ldx [%g2 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR], %g3 | |
1066 | set SPARC_UE_MEU_BITS, %g4 | |
1067 | btst %g4, %g3 ! any UE or MEU bit set? | |
1068 | bz %xcc, .ue_dump_l2 ! no UEs, check L2 | |
1069 | nop | |
1070 | ||
1071 | ! a UE/MEU bit is set in the SPARC ESR. If it is LDAU, then | |
1072 | ! it is L2$/DRAM related. | |
1073 | set SPARC_ESR_LDAU, %g4 ! LDAU bit | |
1074 | btst %g4, %g3 ! LDAU set? | |
1075 | bnz %xcc, .ue_ldau_err | |
1076 | nop | |
1077 | ||
1078 | set SPARC_ESR_NCU, %g4 ! NCU bit | |
1079 | btst %g4, %g3 ! NCU set? | |
1080 | bnz %xcc, .ue_ncu_err | |
1081 | nop | |
1082 | ||
1083 | set SPARC_ESR_IRU, %g4 ! IRU bit | |
1084 | btst %g4, %g3 ! IRU set? | |
1085 | bnz %xcc, .ue_iru_err | |
1086 | nop | |
1087 | ||
1088 | set SPARC_ESR_FRU, %g4 ! FRU bit | |
1089 | btst %g4, %g3 ! FRU set? | |
1090 | bnz %xcc, .ue_fru_err | |
1091 | nop | |
1092 | ||
1093 | /* | |
1094 | * check to see if UE occurred in hypervisor | |
1095 | * We check early in order to avoid a deadlock situation. | |
1096 | * in the previous trap, we were handling either a dis UE or a CE | |
1097 | */ | |
1098 | rdhpr %htstate, %g1 | |
1099 | btst HTSTATE_HPRIV, %g1 | |
1100 | bnz %xcc, .hpriv_ue ! UE in hypervisor | |
1101 | nop | |
1102 | ||
1103 | set SPARC_ESR_MAU, %g4 ! MAU bit | |
1104 | btst %g4, %g3 ! MAU set? | |
1105 | bnz %xcc, .ue_mau_err | |
1106 | nop | |
1107 | ||
1108 | set SPARC_ESR_IMDU, %g4 ! IMDU bit | |
1109 | btst %g4, %g3 ! IMDU set? | |
1110 | bnz %xcc, .ue_imdu_err | |
1111 | nop | |
1112 | ||
1113 | set SPARC_ESR_IMTU, %g4 ! IMTU bit | |
1114 | btst %g4, %g3 ! IMTU set? | |
1115 | bnz %xcc, .ue_imtu_err | |
1116 | nop | |
1117 | ||
1118 | set SPARC_ESR_DMTU, %g4 ! DMTU bit | |
1119 | btst %g4, %g3 ! DMTU set? | |
1120 | bnz %xcc, .ue_dmtu_err | |
1121 | nop | |
1122 | ||
1123 | set SPARC_ESR_DMDU, %g4 ! DMDU bit | |
1124 | btst %g4, %g3 ! DMDU set? | |
1125 | bnz %xcc, .ue_dmdu_err | |
1126 | nop | |
1127 | ||
1128 | set SPARC_ESR_DMSU, %g4 ! DMSU bit | |
1129 | btst %g4, %g3 ! DMSU set? | |
1130 | bnz %xcc, .ue_dmsu_err | |
1131 | nop | |
1132 | ||
1133 | set SPARC_ESR_MEU, %g4 ! MEU bit | |
1134 | btst %g4, %g3 ! MEU set? | |
1135 | bnz %xcc, .ue_just_meu_err | |
1136 | nop | |
1137 | /*NOTREACHED*/ | |
1138 | ! Should not get here as all UE bits have been tested | |
1139 | PRINT("NOTREACHED\r\n") | |
1140 | ba,a .ue_send_resume_exit | |
1141 | ||
1142 | /* | |
1143 | * FRU: Float Register File uncorrectable ECC error | |
1144 | */ | |
1145 | ! If the error is unrecoverable, mark the cpu in error. Else | |
1146 | ! fill out the ue error report in cpu structure. send service | |
1147 | ! entity diagnosis report, then call precise_ue_err_ret. In | |
1148 | ! precise_ue_err_ret, it will queue the error report to guest. | |
1149 | .ue_fru_err: | |
1150 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g2, %g2) ! ->cpu.ue_rpt | |
1151 | ldx [%g2 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 | |
1152 | !! %g1 = sparc afar | |
1153 | HVCALL(clear_fregerr) ! %g1 = input, g2 = output | |
1154 | !! %g2 contains a 0 if we got FRU after FRC for a persistent error | |
1155 | brnz %g2, .ue_not_from_frc ! it is a new FRU | |
1156 | nop | |
1157 | ! Took an FRU trap from the FRC handler reread. Return to FRC handler | |
1158 | PRINT("FRU FROM FRC DIAG\r\n"); | |
1159 | ||
1160 | STRAND_STRUCT(%g6) | |
1161 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
1162 | ||
1163 | RESTORE_UE_GLOBALS() | |
1164 | ||
1165 | done ! complete reread of reg | |
1166 | ||
1167 | .ue_not_from_frc: | |
1168 | /* | |
1169 | * check to see if UE occurred in hypervisor | |
1170 | */ | |
1171 | rdhpr %htstate, %g3 | |
1172 | btst HTSTATE_HPRIV, %g3 | |
1173 | bnz %xcc, .hpriv_ue ! UE in hypervisor | |
1174 | nop | |
1175 | ||
1176 | HVCALL(fru_check) ! g2 = status | |
1177 | ! %g2 contains whether the error is transient, persistent or a failed RF | |
1178 | cmp %g2, RF_TRANSIENT ! transient? | |
1179 | bne .ue_fru_cpu ! no: unrecoverable | |
1180 | nop | |
1181 | ||
1182 | ! FRU is recoverable, send a nonresumable error to the guest | |
1183 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_FRF, | |
1184 | EDESC_PRECISE_NONRESUMABLE, %g1, %g2, %g3) | |
1185 | CLEAR_SPARC_ESR(STRAND_UE_RPT, SPARC_ESR_FRU, %g1, %g2, %g3, %g4) | |
1186 | PRINT("FRU DIAG\r\n") | |
1187 | ba,a .ue_eer_send_ue_rpt | |
1188 | ||
1189 | /* FRU is unrecoverable, mark CPU in error */ | |
1190 | .ue_fru_cpu: | |
1191 | PRINT("CPU in ERROR -FRU\r\n") | |
1192 | ! Set the CPU_ERROR status flag | |
1193 | SET_CPU_IN_ERROR(%g1, %g2) | |
1194 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_CPU, | |
1195 | EDESC_UE_RESUMABLE, %g1, %g2, %g3) | |
1196 | ba,a .ue_send_resume_exit | |
1197 | ||
1198 | /* | |
1199 | * IMDU: ITLB Data Parity Error (precise) | |
1200 | * Detected on instruction translation as well as with loads | |
1201 | * to ASI_ITLB_DATA_ACCESS_REG. | |
1202 | */ | |
1203 | .ue_imdu_err: | |
1204 | PRINT("IMDU DIAG\r\n") | |
1205 | STRAND_STRUCT(%g1) | |
1206 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1207 | ! dump the ITLB entries into cpu.ue_rpt.diag_buf | |
1208 | DUMP_ITLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1209 | mov I_INVALIDATE, %g1 | |
1210 | stxa %g0, [%g1] ASI_TLB_INVALIDATE | |
1211 | #if 0 /* { FIXME: no longer required */ | |
1212 | mov MAP_ITLB, %g1 | |
1213 | HVCALL(remap_perm_addr) | |
1214 | #endif /* } */ | |
1215 | ! log the TLB entries on the console | |
1216 | CONSOLE_PRINT_TLB_DATA("ITLB Tag Data\r\n", %g1, %g2, %g3, %g4, \ | |
1217 | %g5, %g6, %g7) | |
1218 | ! For bringup, dump out the TLB entries after demap page | |
1219 | #ifdef NIAGARA_BRINGUP | |
1220 | PRINT("IMDU demap\r\n") | |
1221 | STRAND_STRUCT(%g1) | |
1222 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1223 | add %g2, 0x400, %g2 ! use the second 1KB area | |
1224 | ! dump the ITLB entries into strand diag buffer area | |
1225 | DUMP_ITLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1226 | ! log the TLB entries on the console for bringup | |
1227 | CONSOLE_PRINT_TLB_DATA_2("ITLB Tag Data\r\n", %g1, %g2, %g3, \ | |
1228 | %g4, %g5, %g6, %g7) | |
1229 | #endif | |
1230 | ba,a .ue_send_resume_exit ! resumable error | |
1231 | /*NOTREACHED*/ | |
1232 | ||
1233 | /* | |
1234 | * IMTU: ITLB Tag Parity Error | |
1235 | * Parity error when accessed via a load from ASI_ITLB_TAG_READ | |
1236 | * Action: Reset the platform. | |
1237 | */ | |
1238 | .ue_imtu_err: | |
1239 | PRINT("IMTU DIAG\r\n") | |
1240 | ! Can't dump tlb since there is no safe mechanism | |
1241 | ba,a .ue_send_rpt_and_abort ! reset | |
1242 | /*NOTREACHED*/ | |
1243 | ||
1244 | /* | |
1245 | * DMTU: DTLB Tag Parity Error | |
1246 | * Parity error when accessed via a load from ASI_DTLB_TAG_READ | |
1247 | * Action: reset the platform. | |
1248 | */ | |
1249 | .ue_dmtu_err: | |
1250 | PRINT("DMTU DIAG\r\n") | |
1251 | ! Can't dump tlb since there is no safe mechanism | |
1252 | ba,a .ue_send_rpt_and_abort ! reset | |
1253 | /*NOTREACHED*/ | |
1254 | ||
1255 | /* | |
1256 | * DMDU: DTLB Data Parity Error on Load and Atomics | |
1257 | * Parity error on atomic or load translation as well | |
1258 | * as with loads to ASI_DTLB_DATA_ACCESS_REG. | |
1259 | */ | |
1260 | .ue_dmdu_err: | |
1261 | PRINT("DMDU DIAG\r\n") | |
1262 | STRAND_STRUCT(%g1) | |
1263 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1264 | ! dump the DTLB entries into the strand diag buffer | |
1265 | DUMP_DTLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1266 | ! log the TLB data on the console | |
1267 | CONSOLE_PRINT_TLB_DATA("DTLB Tag Data\r\n", %g1, %g2, %g3, %g4, \ | |
1268 | %g5, %g6, %g7) | |
1269 | mov D_INVALIDATE, %g1 | |
1270 | stxa %g0, [%g1] ASI_TLB_INVALIDATE | |
1271 | #if 0 /* { FIXME: no longer required */ | |
1272 | mov MAP_DTLB, %g1 | |
1273 | HVCALL(remap_perm_addr) | |
1274 | #endif /* } */ | |
1275 | ! For bringup, dump out the TLB entries after demap page | |
1276 | #ifdef NIAGARA_BRINGUP | |
1277 | PRINT("after demap\r\n") | |
1278 | STRAND_STRUCT(%g1) | |
1279 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1280 | add %g2, 1024, %g2 ! use next 1KB area | |
1281 | ! dump the dtlb entries into the strand diag buffer | |
1282 | DUMP_DTLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1283 | ! log the tlb entries on the console for bringup | |
1284 | CONSOLE_PRINT_TLB_DATA_2("DTLB Tag Data\r\n", %g1, %g2, %g3, \ | |
1285 | %g4, %g5, %g6, %g7) | |
1286 | #endif | |
1287 | ba,a .ue_send_resume_exit ! resumable UE | |
1288 | /*NOTREACHED*/ | |
1289 | ||
1290 | /* | |
1291 | * IRU: IRF Uncorrectable ECC Error | |
1292 | */ | |
1293 | .ue_iru_err: | |
1294 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g2, %g2) | |
1295 | ldx [%g2 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 | |
1296 | !! %g1 = sparc afar | |
1297 | HVCALL(clear_iregerr) ! %g1 = input, %g2 = output | |
1298 | !! %g2 = 0 if we got IRU after IRC for a persistent error bit | |
1299 | brnz %g2, .ue_not_from_irc ! it is a new IRU | |
1300 | nop | |
1301 | ! Took an IRU trap from the IRC handler reread. Return to IRC handler | |
1302 | PRINT("IRU FROM IRC DIAG\r\n") | |
1303 | ||
1304 | STRAND_STRUCT(%g6) | |
1305 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
1306 | ||
1307 | RESTORE_UE_GLOBALS() | |
1308 | ||
1309 | done ! complete reread of reg | |
1310 | ||
1311 | .ue_not_from_irc: | |
1312 | /* | |
1313 | * check to see if UE occurred in hypervisor | |
1314 | */ | |
1315 | rdhpr %htstate, %g3 | |
1316 | btst HTSTATE_HPRIV, %g3 | |
1317 | bnz %xcc, .hpriv_ue ! UE in hypervisor | |
1318 | nop | |
1319 | ||
1320 | HVCALL(iru_check) ! g2 = status | |
1321 | cmp %g2, RF_TRANSIENT ! transient? | |
1322 | bne .ue_iru_cpu ! no: unrecoverable | |
1323 | nop | |
1324 | ||
1325 | ! IRU is recoverable, send a nonresumable error to the guest | |
1326 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_IRF, | |
1327 | EDESC_PRECISE_NONRESUMABLE, %g1, %g2, %g3) | |
1328 | CLEAR_SPARC_ESR(STRAND_UE_RPT, SPARC_ESR_IRU, %g1, %g2, %g3, %g4) | |
1329 | PRINT("IRU DIAG\r\n") | |
1330 | .ue_eer_send_ue_rpt: | |
1331 | ! send the sparc_err_ebl reg to the diag eng | |
1332 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g1, %g2) | |
1333 | ldxa [%g0]ASI_SPARC_ERR_EN, %g3 | |
1334 | stx %g3, [%g2 + STRAND_VBSC_ERPT + EVBSC_DIAG_BUF + DIAG_BUF_REG_INFO] | |
1335 | ba,a .sendnr_ue_resume_exit | |
1336 | /*NOTREACHED*/ | |
1337 | ||
1338 | ! IRU is unrecoverable, mark CPU in error | |
1339 | .ue_iru_cpu: | |
1340 | PRINT("CPU in ERROR - IRU\r\n") | |
1341 | ! Set the CPU_ERROR status flag | |
1342 | SET_CPU_IN_ERROR(%g1, %g2) | |
1343 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_CPU, | |
1344 | EDESC_UE_RESUMABLE, %g1, %g2, %g3) | |
1345 | ba,a .ue_resume_exit | |
1346 | ||
1347 | /* | |
1348 | * DMSU: DTLB Data Parity Error on Store | |
1349 | * Parity error on store translation. | |
1350 | */ | |
1351 | .ue_dmsu_err: | |
1352 | PRINT("DMSU DIAG\r\n") | |
1353 | mov D_INVALIDATE, %g1 | |
1354 | stxa %g0, [%g1] ASI_TLB_INVALIDATE | |
1355 | STRAND_STRUCT(%g1) | |
1356 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1357 | ! dump the DTLB entries into the strand diag buffer | |
1358 | DUMP_DTLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1359 | ! log the TLB data on the console | |
1360 | CONSOLE_PRINT_TLB_DATA("DTLB Tag Data\r\n", %g1, %g2, %g3, %g4, \ | |
1361 | %g5, %g6, %g7) | |
1362 | #if 0 /* { FIXME: no longer required */ | |
1363 | mov MAP_DTLB, %g1 | |
1364 | HVCALL(remap_perm_addr) | |
1365 | #endif /* } */ | |
1366 | ! For bringup we dump the TLB after the demap operation | |
1367 | #ifdef NIAGARA_BRINGUP | |
1368 | PRINT("DMSU demap\r\n") | |
1369 | STRAND_STRUCT(%g1) | |
1370 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1371 | add %g2, 1024, %g2 ! use the next 1KB area | |
1372 | ! dump the dtlb entries | |
1373 | DUMP_DTLB(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1374 | ! log the dtlb to the console | |
1375 | CONSOLE_PRINT_TLB_DATA_2("DTLB Tag Data\r\n", %g1, %g2, %g3, \ | |
1376 | %g4, %g5, %g6, %g7) | |
1377 | #endif | |
1378 | ba,a .ue_send_resume_exit ! resumable error | |
1379 | /*NOTREACHED*/ | |
1380 | ||
1381 | /* | |
1382 | * MEU: Multiple Uncorrectable Error bit | |
1383 | * Sometimes only the MEU bit will be set. It is treated as | |
1384 | * a resumable error. | |
1385 | */ | |
1386 | .ue_just_meu_err: | |
1387 | PRINT("JUST MEU\r\n") | |
1388 | ba,a .ue_send_resume_exit ! resumable UE | |
1389 | /*NOTREACHED*/ | |
1390 | ||
1391 | .ue_send_rpt_and_abort: | |
1392 | ! send UE diag report | |
1393 | STRAND_STRUCT(%g6) | |
1394 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1395 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1396 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1397 | mov EVBSC_SIZE, %g3 ! size | |
1398 | HVCALL(send_diag_erpt) | |
1399 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
1400 | ! abort HV | |
1401 | ba,pt %xcc, hvabort | |
1402 | rd %pc, %g1 | |
1403 | /*NOTREACHED*/ | |
1404 | ||
1405 | /* | |
1406 | * NCU: IO Load/Instruction Fetch Error | |
1407 | */ | |
1408 | .ue_ncu_err: | |
1409 | ||
1410 | ! check for io_prot | |
1411 | STRAND_STRUCT(%g1) | |
1412 | set STRAND_IO_PROT, %g2 | |
1413 | ldx [%g1 + %g2], %g2 ! strand.io_prot | |
1414 | brz %g2, 1f ! if zero, no error protection | |
1415 | nop | |
1416 | ! under i/o error protection | |
1417 | ! set the i/o error flag in the cpu structure and complete the | |
1418 | ! instruction | |
1419 | set STRAND_IO_ERROR, %g2 | |
1420 | mov 1, %g3 | |
1421 | stx %g3, [%g1 + %g2] ! strand.io_error = 1 | |
1422 | ||
1423 | ! clear JBI_ERR_LOG, JBI_ERR_OVF | |
1424 | setx JBI_ERR_LOG, %g3, %g4 | |
1425 | ldx [%g4], %g5 | |
1426 | stx %g5, [%g4] ! clear JBI_ERROR_LOG | |
1427 | setx JBI_ERR_OVF, %g3, %g4 | |
1428 | ldx [%g4], %g5 | |
1429 | stx %g5, [%g4] ! clear JBI_ERROR_OVF | |
1430 | ||
1431 | SPINLOCK_RESUME_ALL_STRAND(%g1, %g3, %g4, %g5, %g6) | |
1432 | ||
1433 | RESTORE_UE_GLOBALS() | |
1434 | ||
1435 | done ! complete the instruction | |
1436 | ! process error | |
1437 | 1: | |
1438 | PRINT("NCU DIAG\r\n") | |
1439 | ||
1440 | rdhpr %htstate, %g1 | |
1441 | btst HTSTATE_HPRIV, %g1 | |
1442 | bnz %xcc, .hpriv_ue | |
1443 | nop | |
1444 | ||
1445 | ! collect all diagnostic data | |
1446 | STRAND_STRUCT(%g1) | |
1447 | add %g1, STRAND_UE_RPT, %g2 ! %g2 = strand.ue_rpt | |
1448 | DUMP_JBI_SSI(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1449 | ||
1450 | ! clear JBI_ERR_LOG, JBI_ERR_OVF, SSI_LOG | |
1451 | setx JBI_ERR_LOG, %g3, %g4 | |
1452 | ldx [%g4], %g5 | |
1453 | brz %g5, .ue_check_ssi | |
1454 | stx %g5, [%g4] ! clear JBI_ERROR_LOG | |
1455 | setx JBI_ERR_OVF, %g3, %g4 | |
1456 | ldx [%g4], %g5 | |
1457 | stx %g5, [%g4] ! clear JBI_ERROR_OVF | |
1458 | ba,a .ue_ncu_diag | |
1459 | ! check SSI | |
1460 | .ue_check_ssi: | |
1461 | setx SSI_LOG, %g3, %g4 | |
1462 | ldx [%g4], %g5 | |
1463 | brz %g5, .ue_no_ncu_info | |
1464 | stx %g5, [%g4] | |
1465 | ba,a .ue_ncu_diag | |
1466 | ||
1467 | .ue_no_ncu_info: | |
1468 | PRINT("NO ERROR LOGGED IN JBI SSI LOG\r\n") | |
1469 | ba,a .ue_ncu_diag | |
1470 | ||
1471 | .ue_ncu_diag: | |
1472 | CONSOLE_PRINT_JBI_SSI("JBI SSI Log\r\n", %g1, %g2, %g3, %g4, \ | |
1473 | %g5, %g6, %g7) | |
1474 | ! send UE diag report | |
1475 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g6, %g1) | |
1476 | inc STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1477 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1478 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1479 | mov EVBSC_SIZE, %g3 ! size | |
1480 | HVCALL(send_diag_erpt) | |
1481 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_PIO, \ | |
1482 | EDESC_PRECISE_NONRESUMABLE, %g4, %g5, %g6) | |
1483 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g1, %g2) | |
1484 | ldx [%g2 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g3 ! VA | |
1485 | stx %g3, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR] | |
1486 | ba,a precise_ue_err_ret ! UE error epilogue | |
1487 | /*NOTREACHED*/ | |
1488 | ||
1489 | .ue_mau_err: | |
1490 | PRINT("MAU DIAG\r\n") | |
1491 | ba,a .sendnr_ue_resume_exit ! non-resumable UE epilogue | |
1492 | ||
1493 | /* | |
1494 | * Precise UEs that are nonresumable errors get here. | |
1495 | * Here the diagnostic erpt is sent before executing | |
1496 | * the handler epilogue. | |
1497 | */ | |
1498 | .sendnr_ue_resume_exit: ! non-resumable UE epilogue | |
1499 | ! send UE diag report | |
1500 | STRAND_STRUCT(%g6) | |
1501 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1502 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1503 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1504 | mov EVBSC_SIZE, %g3 ! size | |
1505 | HVCALL(send_diag_erpt) | |
1506 | ba,a precise_ue_err_ret ! UE error epilogue | |
1507 | ||
1508 | ||
1509 | ! %g1 has the strand pointer, %g2 has the UE error report buffer | |
1510 | .ue_ldau_err: | |
1511 | .ue_dump_l2: | |
1512 | DUMP_L2_DRAM_ERROR_LOGS(%g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1513 | ||
1514 | /* | |
1515 | * check to see if UE occurred in hypervisor | |
1516 | * We check early in order to avoid a deadlock situation. | |
1517 | * in the previous trap, we were handling either a dis UE or a CE | |
1518 | */ | |
1519 | rdhpr %htstate, %g1 | |
1520 | btst HTSTATE_HPRIV, %g1 | |
1521 | bnz %xcc, .hpriv_ue ! UE in hypervisor | |
1522 | nop | |
1523 | ||
1524 | ! check for privileged TL overflow | |
1525 | rdpr %tl, %g1 ! get trap level | |
1526 | cmp %g1, MAXPTL ! is it at max? | |
1527 | bgu,pn %xcc, .tl_overflow ! TL > MAXPTL | |
1528 | nop | |
1529 | ||
1530 | ! check for SPARC_ESR.LDAU | |
1531 | ! go through each L2 bank and check for valid UE bits | |
1532 | .ue_check_l2_b0: | |
1533 | UE_CHECK_L2_ESR(0, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
1534 | bz %xcc, .ue_check_l2_b1 ! check next bank | |
1535 | nop | |
1536 | SAVE_L2_LINE_STATE(0, STRAND_UE_RPT, %g1, %g2) | |
1537 | DUMP_L2_SET_TAG_DATA(0, STRAND_UE_RPT, %g1, %g2, %g1, %g2) | |
1538 | !! %g1->strand | |
1539 | !! %g2->erpt | |
1540 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(0)], %g4 ! l2esr | |
1541 | CLEAR_L2_ESR(0, %g4, %g5, %g6) ! clear L2 ESR | |
1542 | PROCESS_UE_IN_L2_ESR(0, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
1543 | .sendnr_ue_resume_exit, .ue_senddiag_resume_exit, \ | |
1544 | .ue_resume_exit) | |
1545 | ||
1546 | .ue_check_l2_b1: | |
1547 | UE_CHECK_L2_ESR(1, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
1548 | bz %xcc, .ue_check_l2_b2 ! check next bank | |
1549 | nop | |
1550 | SAVE_L2_LINE_STATE(1, STRAND_UE_RPT, %g1, %g2) | |
1551 | DUMP_L2_SET_TAG_DATA(1, STRAND_UE_RPT, %g1, %g2, %g1, %g2) | |
1552 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(1)], %g4 ! l2esr | |
1553 | CLEAR_L2_ESR(1, %g4, %g5, %g6) ! clear L2 ESR | |
1554 | PROCESS_UE_IN_L2_ESR(1, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
1555 | .sendnr_ue_resume_exit, .ue_senddiag_resume_exit, \ | |
1556 | .ue_resume_exit) | |
1557 | ||
1558 | .ue_check_l2_b2: | |
1559 | UE_CHECK_L2_ESR(2, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
1560 | bz %xcc, .ue_check_l2_b3 ! check next bank | |
1561 | nop | |
1562 | SAVE_L2_LINE_STATE(2, STRAND_UE_RPT, %g1, %g2) | |
1563 | DUMP_L2_SET_TAG_DATA(2, STRAND_UE_RPT, %g1, %g2, %g1, %g2) | |
1564 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(2)], %g4 ! l2esr | |
1565 | CLEAR_L2_ESR(2, %g4, %g5, %g6) ! clear L2 ESR | |
1566 | PROCESS_UE_IN_L2_ESR(2, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
1567 | .sendnr_ue_resume_exit, .ue_senddiag_resume_exit, \ | |
1568 | .ue_resume_exit) | |
1569 | ||
1570 | .ue_check_l2_b3: | |
1571 | UE_CHECK_L2_ESR(3, %g1, %g2, %g3, %g4) ! %g1 = L2ESR | |
1572 | bz %xcc, .ue_no_error ! XXX spurious? | |
1573 | nop | |
1574 | SAVE_L2_LINE_STATE(3, STRAND_UE_RPT, %g1, %g2) | |
1575 | DUMP_L2_SET_TAG_DATA(3, STRAND_UE_RPT, %g1, %g2, %g1, %g2) | |
1576 | ldx [%g2 + STRAND_EVBSC_L2_AFSR(3)], %g4 ! l2esr | |
1577 | CLEAR_L2_ESR(3, %g4, %g5, %g6) ! clear L2 ESR | |
1578 | PROCESS_UE_IN_L2_ESR(3, %g1, %g2, %g3, %g4, %g5, %g6, %g7, \ | |
1579 | .sendnr_ue_resume_exit, .ue_senddiag_resume_exit, \ | |
1580 | .ue_resume_exit) | |
1581 | ! | |
1582 | ! All banks checked, now return | |
1583 | ! | |
1584 | PRINT("NOTREACHED!\r\n") | |
1585 | ba,a .ue_resume_exit | |
1586 | ||
1587 | .ue_no_error: | |
1588 | PRINT("NO_UE_ERROR\r\n") | |
1589 | ! some other thread beat us to it. | |
1590 | ! no bits in L2, simply return (XXX send a service error report?) | |
1591 | ||
1592 | .ue_send_resume_exit: | |
1593 | /* | |
1594 | * Precise UEs that are resumable errors get here. | |
1595 | * Here the diagnostic erpt is sent before executing | |
1596 | * the instruction retry. | |
1597 | */ | |
1598 | ! send UE diag report | |
1599 | STRAND_STRUCT(%g6) | |
1600 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1601 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1602 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1603 | mov EVBSC_SIZE, %g3 ! size | |
1604 | HVCALL(send_diag_erpt) | |
1605 | ||
1606 | ba,a .ue_resume_exit ! resumable UE epilogue | |
1607 | ||
1608 | ||
1609 | .tl_overflow: | |
1610 | PRINT("TL OVERFLOW\r\n") | |
1611 | ! send UE diag report | |
1612 | STRAND_STRUCT(%g6) | |
1613 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1614 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1615 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1616 | mov EVBSC_SIZE, %g3 ! size | |
1617 | HVCALL(send_diag_erpt) | |
1618 | ||
1619 | RESTORE_UE_GLOBALS() | |
1620 | ||
1621 | ba,a watchdog_guest | |
1622 | ||
1623 | .hpriv_ue: | |
1624 | ! send UE diag report | |
1625 | STRAND_STRUCT(%g6) | |
1626 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1627 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1628 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1629 | mov EVBSC_SIZE, %g3 ! size | |
1630 | ||
1631 | HVCALL(send_diag_erpt) | |
1632 | ||
1633 | HV_PRINT_SPINLOCK_ENTER(%g1, %g2, %g3) | |
1634 | HV_PRINT_NOTRAP("UE in hypervisor - reset the system\r\n") | |
1635 | rdpr %tl, %g2 | |
1636 | ||
1637 | HV_PRINT_NOTRAP("TPC: 0x") | |
1638 | rdpr %tpc, %g1 | |
1639 | HV_PRINTX_NOTRAP(%g1) | |
1640 | HV_PRINT_NOTRAP("\r\n") | |
1641 | ||
1642 | HV_PRINT_NOTRAP("TT: 0x") | |
1643 | rdpr %tt, %g1 | |
1644 | HV_PRINTX_NOTRAP(%g1) | |
1645 | HV_PRINT_NOTRAP("\r\n") | |
1646 | ||
1647 | HV_PRINT_NOTRAP("TSTATE: 0x") | |
1648 | rdpr %tstate, %g1 | |
1649 | HV_PRINTX_NOTRAP(%g1) | |
1650 | HV_PRINT_NOTRAP("\r\n") | |
1651 | ||
1652 | HV_PRINT_SPINLOCK_EXIT(%g1) | |
1653 | ||
1654 | STRAND_STRUCT(%g6) | |
1655 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
1656 | LEGION_EXIT(3) | |
1657 | ! abort HV | |
1658 | ba,pt %xcc, hvabort | |
1659 | rd %pc, %g1 | |
1660 | ||
1661 | .err_resume_bad_guest_err_q: | |
1662 | SET_CPU_IN_ERROR(%g1, %g2) | |
1663 | SET_ERPT_EDESC_EATTR(STRAND_UE_RPT, EATTR_CPU, | |
1664 | EDESC_UE_RESUMABLE, %g1, %g2, %g3) | |
1665 | ba,a .ue_send_resume_exit ! resumable UE | |
1666 | ||
1667 | .fatal_reset_dbu: /* this is where we take the system down! */ | |
1668 | ! don't care how we got here, stop everything now | |
1669 | PRINT("Reset the System: sir 0 %o0=1 fatal error\r\n") | |
1670 | 1: | |
1671 | PRINT("TT 0x") | |
1672 | rdpr %tt, %g1 | |
1673 | PRINTX(%g1) | |
1674 | PRINT(" TL 0x") | |
1675 | rdpr %tl, %g2 | |
1676 | PRINTX(%g2) | |
1677 | PRINT(" TPC 0x") | |
1678 | rdpr %tpc, %g1 | |
1679 | PRINTX(%g1) | |
1680 | PRINT(" TNPC 0x") | |
1681 | rdpr %tnpc, %g1 | |
1682 | PRINTX(%g1) | |
1683 | PRINT(" TSTATE 0x") | |
1684 | rdpr %tstate, %g1 | |
1685 | PRINTX(%g1) | |
1686 | PRINT("\r\n") | |
1687 | sub %g2, 1, %g2 | |
1688 | brnz %g2, 1b | |
1689 | wrpr %g2, %tl | |
1690 | ||
1691 | mov SIR_TYPE_FATAL_DBU, %o0 | |
1692 | sir 0 | |
1693 | ||
1694 | /* | |
1695 | * Disrupting UE error handler epilogue | |
1696 | * The disrupting UE error handlers return here after handling | |
1697 | * the error | |
1698 | * NCEEN was not disabled, so disrupting UE handler did not | |
1699 | * mask any UEs. But we could have hit some CEs or other | |
1700 | * disrupting UEs whose trap will be taken when we return. | |
1701 | * Here we queue up the resumable error report to the guest. | |
1702 | * | |
1703 | * Disrupting UEs use the CE error buffer | |
1704 | */ | |
1705 | .dis_ue_err_ret: | |
1706 | PRINT("DIS UE_ERR_RET\r\n") | |
1707 | ||
1708 | /* send diag report to vbsc */ | |
1709 | STRAND_STRUCT(%g6) | |
1710 | add %g6, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g1 | |
1711 | add %g6, STRAND_CE_RPT + STRAND_UNSENT_PKT, %g2 ! erpt.unsent flag | |
1712 | mov EVBSC_SIZE, %g3 | |
1713 | HVCALL(send_diag_erpt) | |
1714 | ||
1715 | .dis_ue_err_rerouting: | |
1716 | ||
1717 | /* | |
1718 | * Check if this error needs to be re-routed | |
1719 | * Find which L2 ESR is set and check whether the | |
1720 | * error requires re-routing. If the ESR is non-zero | |
1721 | * but not re-routing, continue as normal. | |
1722 | */ | |
1723 | setx L2_ESR_REROUTED_BITS, %g5, %g4 | |
1724 | STRAND_STRUCT(%g6) | |
1725 | add %g6, STRAND_CE_RPT, %g6 | |
1726 | ||
1727 | ldx [%g6 + STRAND_EVBSC_L2_AFSR(0)], %g5 | |
1728 | btst %g5, %g4 | |
1729 | bnz,pt %xcc, .dis_ue_err_ret_rerouting | |
1730 | mov 0, %g1 ! bank number | |
1731 | brnz,pt %g5, .dis_ue_err_ret_no_rerouting | |
1732 | nop | |
1733 | ldx [%g6 + STRAND_EVBSC_L2_AFSR(1)], %g5 | |
1734 | btst %g5, %g4 | |
1735 | bnz,pt %xcc, .dis_ue_err_ret_rerouting | |
1736 | mov 1, %g1 ! bank number | |
1737 | brnz,pt %g5, .dis_ue_err_ret_no_rerouting | |
1738 | nop | |
1739 | ldx [%g6 + STRAND_EVBSC_L2_AFSR(2)], %g5 | |
1740 | btst %g5, %g4 | |
1741 | bnz,pt %xcc, .dis_ue_err_ret_rerouting | |
1742 | mov 2, %g1 ! bank number | |
1743 | brnz,pt %g5, .dis_ue_err_ret_no_rerouting | |
1744 | nop | |
1745 | ldx [%g6 + STRAND_EVBSC_L2_AFSR(3)], %g5 | |
1746 | btst %g5, %g4 | |
1747 | bnz,pn %xcc, .dis_ue_err_ret_rerouting | |
1748 | mov 3, %g1 ! bank number | |
1749 | nop | |
1750 | ba .dis_ue_err_ret_no_rerouting | |
1751 | nop | |
1752 | ||
1753 | /* | |
1754 | * re-route an error report | |
1755 | * 1. Get the PA of the error from the diag report | |
1756 | * 2. determine whch guest this PA belongs to | |
1757 | */ | |
1758 | .dis_ue_err_ret_rerouting: | |
1759 | ! %g1 bank number | |
1760 | ! %g5 L2 ESR | |
1761 | ! %g6 strand->ce_rprt | |
1762 | ||
1763 | /* | |
1764 | * Need to get the PA from either the DRAM or L2 EAR | |
1765 | */ | |
1766 | setx (L2_ESR_DAU | L2_ESR_DSU), %g3, %g2 | |
1767 | btst %g5, %g2 | |
1768 | be,pt %xcc, .dis_ue_err_ret_rerouting_l2 | |
1769 | nop | |
1770 | ||
1771 | ! DRAM error | |
1772 | ! %g1 bank number | |
1773 | mulx %g1, EVBSC_DRAM_AFAR_INCR, %g1 | |
1774 | add %g1, EVBSC_DRAM_AFAR, %g1 | |
1775 | ldx [%g6 + %g1], %g4 ! PA | |
1776 | ba .dis_ue_err_ret_rerouting_find_guest | |
1777 | nop | |
1778 | ||
1779 | .dis_ue_err_ret_rerouting_l2: | |
1780 | ! %g1 bank number | |
1781 | mulx %g1, EVBSC_L2_AFAR_INCR, %g1 | |
1782 | add %g1, EVBSC_L2_AFAR, %g1 | |
1783 | ldx [%g6 + %g1], %g4 ! PA | |
1784 | ||
1785 | .dis_ue_err_ret_rerouting_find_guest: | |
1786 | /* | |
1787 | * Find the guest which owns this PA. | |
1788 | * For each guest loop through the ra2pa_segment array and check the | |
1789 | * PA against the base/limit | |
1790 | * %g4 PA | |
1791 | */ | |
1792 | ROOT_STRUCT(%g2) | |
1793 | ldx [%g2 + CONFIG_GUESTS], %g2 ! &guests[0] | |
1794 | set NGUESTS - 1, %g3 ! %g3 guest loop counter | |
1795 | 1: | |
1796 | ! PA2RA_CONV(guestp, paddr, raddr, scr1, scr2) | |
1797 | PA2RA_CONV(%g2, %g4, %g6, %g1, %g5) | |
1798 | ! we got a valid RA (%g6), so this is the guest for this PA | |
1799 | brz,pt %g5, 4f | |
1800 | nop | |
1801 | 2: | |
1802 | set GUEST_SIZE, %g5 | |
1803 | add %g2, %g5, %g2 ! guest++ | |
1804 | brnz,pt %g3, 1b | |
1805 | dec %g3 ! nguests-- | |
1806 | ||
1807 | ! no guest found for this PA | |
1808 | ba .dis_ue_err_ret_no_rerouting | |
1809 | nop | |
1810 | 4: | |
1811 | ! %g2 &guest | |
1812 | ! %g4 PA | |
1813 | ||
1814 | ! is it for the guest we are running on ? | |
1815 | GUEST_STRUCT(%g1) | |
1816 | cmp %g1, %g2 | |
1817 | be .dis_ue_err_ret_no_rerouting | |
1818 | nop | |
1819 | ||
1820 | ! go and finish re-routing this error | |
1821 | ba cpu_reroute_error | |
1822 | nop | |
1823 | ||
1824 | /* | |
1825 | * send resumable error report on this CPU | |
1826 | */ | |
1827 | .dis_ue_err_ret_no_rerouting: | |
1828 | ||
1829 | ASMCALL_RQ_ERPT(STRAND_CE_RPT, %g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1830 | ||
1831 | ba,a .dis_ue_resume_exit | |
1832 | ||
1833 | ||
1834 | #if 1 /* XXXX DEAD CODE */ | |
1835 | /* | |
1836 | * Precise UE but ressumable error handler epilogue | |
1837 | * The precise UE error handlers return here after handling the error | |
1838 | * A resumable error will be queued to the affected guest. | |
1839 | */ | |
1840 | ENTRY_NP(precise_ue_res_ret) | |
1841 | PRINT("RES UE_ERR_RET\r\n") | |
1842 | ! Call the function to queue the resumable report | |
1843 | ASMCALL_RQ_ERPT(STRAND_UE_RPT, %g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
1844 | ba,a .ue_resume_exit | |
1845 | #endif | |
1846 | ||
1847 | .ue_senddiag_resume_exit: | |
1848 | ! send UE diag report | |
1849 | STRAND_STRUCT(%g6) | |
1850 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
1851 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
1852 | add %g6, %g2, %g2 ! erpt.unsent flag | |
1853 | mov EVBSC_SIZE, %g3 ! size | |
1854 | HVCALL(send_diag_erpt) | |
1855 | ||
1856 | .dis_ue_resume_exit: | |
1857 | .ue_resume_exit: | |
1858 | ! See if CPU is in ERROR and handle the case | |
1859 | VCPU_STRUCT(%g1) | |
1860 | IS_CPU_IN_ERROR(%g1, %g2) | |
1861 | bne %xcc, .ue_continue | |
1862 | nop | |
1863 | ||
1864 | ! Mark the corresponding strand in error | |
1865 | HVCALL(strand_in_error) | |
1866 | ||
1867 | .ue_continue: | |
1868 | STRAND_STRUCT(%g6) | |
1869 | ||
1870 | /* | |
1871 | * Check whether the UE error handler idled the | |
1872 | * strands | |
1873 | */ | |
1874 | lduw [%g6 + STRAND_ERR_FLAG], %g2 | |
1875 | btst ERR_FLAG_STRANDS_NOT_IDLED, %g2 | |
1876 | bnz %xcc, .ue_continue_not_idled ! strands were not idled | |
1877 | bclr ERR_FLAG_STRANDS_NOT_IDLED, %g2 ! reset STRANDS_IDLED | |
1878 | ||
1879 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
1880 | ba .ue_continue_idled ! flag is not set, | |
1881 | nop ! so skip clearing it | |
1882 | ||
1883 | .ue_continue_not_idled: | |
1884 | ||
1885 | stw %g2, [%g6 + STRAND_ERR_FLAG] ! .. | |
1886 | ||
1887 | .ue_continue_idled: | |
1888 | ||
1889 | ldx [%g6 + STRAND_ERR_RET], %g7 ! get return address | |
1890 | brnz,a %g7, .ue_return ! valid: clear it & return | |
1891 | stx %g0, [%g6+ STRAND_ERR_RET] ! .. | |
1892 | ! NULL: return from interrupt | |
1893 | RESTORE_UE_GLOBALS() | |
1894 | retry ! return from UE interrupt | |
1895 | ||
1896 | .ue_return: | |
1897 | HVRET | |
1898 | SET_SIZE(ue_poll_entry) | |
1899 | SET_SIZE(ue_err) | |
1900 | ||
1901 | ||
1902 | /* | |
1903 | * Precise UE error handler epilogue | |
1904 | * The precise UE error handlers return here after handling the error | |
1905 | * A nonresumable error will be queued to the affected guest. | |
1906 | */ | |
1907 | ENTRY_NP(precise_ue_err_ret) | |
1908 | PRINT("precise_ue_err_ret\r\n") | |
1909 | ||
1910 | ! queue nonresumable error report | |
1911 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g1, %g2) | |
1912 | ||
1913 | /* | |
1914 | * Translate error address | |
1915 | * | |
1916 | * When EATTR_PIO, the error PA is in the RA field of the erpt. | |
1917 | * For others, check the four L2 AFARs to find a non-zero | |
1918 | * address. | |
1919 | */ | |
1920 | lduw [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 | |
1921 | btst EATTR_PIO, %g4 | |
1922 | bz,pt %xcc, .precise_ue_err_ret_mem | |
1923 | nop | |
1924 | ||
1925 | .precise_ue_err_ret_io: | |
1926 | /* No affected memory region */ | |
1927 | stw %g0, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
1928 | ||
1929 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 | |
1930 | VCPU_STRUCT(%g1) | |
1931 | CPU_ERR_IO_PA_TO_RA(%g1, %g4, %g4, %g3, %g5, %g6, .precise_ue_err_ret_io) | |
1932 | ba,pt %xcc, 2f | |
1933 | nop | |
1934 | ||
1935 | .precise_ue_err_ret_mem: | |
1936 | mov ERPT_MEM_SIZE, %g4 | |
1937 | stw %g4, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
1938 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(0)], %g4 | |
1939 | brnz %g4, 1f | |
1940 | nop | |
1941 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(1)], %g4 | |
1942 | brnz %g4, 1f | |
1943 | nop | |
1944 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(2)], %g4 | |
1945 | brnz %g4, 1f | |
1946 | nop | |
1947 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(3)], %g4 | |
1948 | brnz %g4, 1f | |
1949 | nop | |
1950 | ba,pt %xcc, 2f | |
1951 | mov CPU_ERR_INVALID_RA, %g4 | |
1952 | ||
1953 | 1: | |
1954 | VCPU_STRUCT(%g1) /* FIXME: or strand? */ | |
1955 | CPU_ERR_PA_TO_RA(%g1, %g4, %g4, %g5, %g6) | |
1956 | ||
1957 | 2: | |
1958 | stx %g4, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR] | |
1959 | ||
1960 | !! %g1 = cpup | |
1961 | !! %g2 = erpt | |
1962 | HVCALL(queue_nonresumable_erpt) | |
1963 | ||
1964 | STRAND_STRUCT(%g1) | |
1965 | SPINLOCK_RESUME_ALL_STRAND(%g1, %g3, %g4, %g5, %g6) | |
1966 | ||
1967 | ba,pt %xcc, nonresumable_error_trap | |
1968 | nop | |
1969 | /*NOTREACHED*/ | |
1970 | SET_SIZE(precise_ue_err_ret) | |
1971 | ||
1972 | #if STRAND_SUN4V_ERPT != 0 | |
1973 | #error "STRAND_SUN4V_ERPT must be 0" | |
1974 | #endif | |
1975 | ||
1976 | /* | |
1977 | * Queue a resumable error report on this CPU | |
1978 | * %g1 contains pointer to the STRAND structure | |
1979 | * %g2 contains pointer to the error report | |
1980 | * (STRAND_SUN4V_ERPT *must* be 0x0 for this to be called generically) | |
1981 | * | |
1982 | * XXX If there is no free entry in the resumable error queue | |
1983 | * print a message and return. XXX | |
1984 | */ | |
1985 | ENTRY_NP(queue_resumable_erpt) | |
1986 | VCPU_STRUCT(%g1) | |
1987 | ldx [%g1 + CPU_ERRQR_BASE_RA], %g3 ! get q base RA | |
1988 | brnz %g3, 1f ! if base RA is zero, skip | |
1989 | nop | |
1990 | mov %g7, %g6 | |
1991 | PRINT("RQ NOT ALLOC\r\n") | |
1992 | mov %g6, %g7 | |
1993 | ! The resumable error queue is not allocated/initialized | |
1994 | ! simply return. No guest is there to receive it. | |
1995 | jmp %g7 + 4 | |
1996 | nop | |
1997 | 1: | |
1998 | /* | |
1999 | * Translate error address | |
2000 | * | |
2001 | * When EATTR_PIO, the error PA is in the RA field of the erpt. | |
2002 | * For others, check the four L2 AFARs to find a non-zero | |
2003 | * address. | |
2004 | */ | |
2005 | lduw [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 | |
2006 | btst EATTR_PIO, %g4 | |
2007 | bz,pt %xcc, .dis_ue_err_ret_mem | |
2008 | nop | |
2009 | ||
2010 | .dis_ue_err_ret_io: | |
2011 | /* No affected memory region */ | |
2012 | stw %g0, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
2013 | ||
2014 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 | |
2015 | CPU_ERR_IO_PA_TO_RA(%g1, %g4, %g4, %g3, %g5, %g6, .dis_ue_err_ret_io) | |
2016 | ba,pt %xcc, 2f | |
2017 | nop | |
2018 | ||
2019 | .dis_ue_err_ret_mem: | |
2020 | mov ERPT_MEM_SIZE, %g4 | |
2021 | stw %g4, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
2022 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(0)], %g4 | |
2023 | brnz %g4, 1f | |
2024 | nop | |
2025 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(1)], %g4 | |
2026 | brnz %g4, 1f | |
2027 | nop | |
2028 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(2)], %g4 | |
2029 | brnz %g4, 1f | |
2030 | nop | |
2031 | ldx [%g2 + STRAND_EVBSC_L2_AFAR(3)], %g4 | |
2032 | brnz %g4, 1f | |
2033 | nop | |
2034 | ba,pt %xcc, 2f | |
2035 | mov CPU_ERR_INVALID_RA, %g4 | |
2036 | ||
2037 | 1: | |
2038 | VCPU_STRUCT(%g1) /* FIXME: or strand? */ | |
2039 | CPU_ERR_PA_TO_RA(%g1, %g4, %g4, %g5, %g6) | |
2040 | ||
2041 | 2: | |
2042 | stx %g4, [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR] | |
2043 | /* | |
2044 | * If this is a MEM error report, ensure that it has a valid | |
2045 | * RA for this guest | |
2046 | */ | |
2047 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 ! attr | |
2048 | btst EATTR_MEM, %g4 | |
2049 | bz,pt %xcc, 1f | |
2050 | nop | |
2051 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 ! ra | |
2052 | cmp %g4, CPU_ERR_INVALID_RA | |
2053 | bne,pt %xcc, 1f | |
2054 | nop | |
2055 | ||
2056 | ! not for this guest, return | |
2057 | ||
2058 | jmp %g7 + 4 | |
2059 | nop | |
2060 | 1: | |
2061 | mov ERROR_RESUMABLE_QUEUE_TAIL, %g3 | |
2062 | ldxa [%g3]ASI_QUEUE, %g5 ! %g5 = rq_tail | |
2063 | add %g5, 0x40, %g6 ! %g6 = rq_next = rq_tail++ | |
2064 | ldx [%g1 + CPU_ERRQR_MASK], %g4 | |
2065 | and %g6, %g4, %g6 ! %g6 = rq_next mod | |
2066 | mov ERROR_RESUMABLE_QUEUE_HEAD, %g3 | |
2067 | ldxa [%g3] ASI_QUEUE, %g4 ! %g4 = rq_head | |
2068 | cmp %g6, %g4 ! head = ++tail? | |
2069 | be %xcc, .rq_full | |
2070 | mov ERROR_RESUMABLE_QUEUE_TAIL, %g3 | |
2071 | stxa %g6, [%g3] ASI_QUEUE ! new tail = rq_next | |
2072 | ! write up the queue record | |
2073 | ldx [%g1 + CPU_ERRQR_BASE], %g4 | |
2074 | add %g5, %g4, %g3 ! %g3 = base + tail | |
2075 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL], %g4 ! ehdl | |
2076 | stx %g4, [%g3 + 0x0] | |
2077 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK], %g4 ! stick | |
2078 | stx %g4, [%g3 + 0x8] | |
2079 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_EDESC], %g4 ! edesc | |
2080 | st %g4, [%g3 + 0x10] | |
2081 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 ! attr | |
2082 | st %g4, [%g3 + 0x14] | |
2083 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 ! ra | |
2084 | stx %g4, [%g3 + 0x18] | |
2085 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ], %g4 ! sz | |
2086 | st %g4, [%g3 + 0x20] | |
2087 | lduh [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_CPUID], %g4 ! cpuid | |
2088 | stuh %g4, [%g3 + 0x24] | |
2089 | lduh [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_SECS], %g4 | |
2090 | stuh %g4, [%g3 + 0x26] ! pad/secs | |
2091 | stx %g0, [%g3 + 0x28] ! word5 | |
2092 | stx %g0, [%g3 + 0x30] ! word6 | |
2093 | stx %g0, [%g3 + 0x38] ! word7 | |
2094 | ||
2095 | jmp %g7 + 4 | |
2096 | nop | |
2097 | ||
2098 | .rq_full: | |
2099 | ! The resumable error queue is full. | |
2100 | ! simply return | |
2101 | mov %g7, %g6 | |
2102 | PRINT("RQ FULL\r\n") | |
2103 | mov %g6, %g7 | |
2104 | ||
2105 | jmp %g7 + 4 | |
2106 | nop | |
2107 | SET_SIZE(queue_resumable_erpt) | |
2108 | ||
2109 | /* | |
2110 | * Queue a nonresumable error report on this CPU | |
2111 | * %g2 contains pointer to the error report | |
2112 | * %g1, %g3 - %g6 clobbered | |
2113 | * %g7 return address | |
2114 | * | |
2115 | * Check to see what is the guest state: | |
2116 | * switch(guestp->state) { | |
2117 | * case GUEST_STATE_SUSPENDED: | |
2118 | * case GUEST_STATE_NORMAL: | |
2119 | * ! calculate new head | |
2120 | * oldtail = [ERROR_NONRESUMABLE_QUEUE_TAIL]ASI_QUEUE | |
2121 | * qnr_mask =vpup->errqnr_mask; | |
2122 | * newtail = (oldtail + qsize) & mask; | |
2123 | * head = [ERROR_NONRESUMABLE_QUEUE_HEAD]ASI_QUEUE | |
2124 | * if (vcpup->cpu_errqnr_base_ra == 0 || (head == newhead)) { | |
2125 | * sir_guest() | |
2126 | * } else { | |
2127 | * deliver_pkt(pkt); | |
2128 | * } | |
2129 | * break; | |
2130 | * case GUEST_STATE_EXITING: | |
2131 | * case GUEST_STATE_STOPPED: | |
2132 | * case GUEST_STATE_UNCONFIGURED: | |
2133 | * drop_pkt(); | |
2134 | * | |
2135 | * break; | |
2136 | * } | |
2137 | * | |
2138 | * This routine just moves the erpt to the queue, it does not | |
2139 | * modify the data. | |
2140 | */ | |
2141 | ENTRY_NP(queue_nonresumable_erpt) | |
2142 | ||
2143 | VCPU_STRUCT(%g1) | |
2144 | ! Get the guest structure this vcpu belongs | |
2145 | VCPU2GUEST_STRUCT(%g1, %g5) | |
2146 | ||
2147 | ! Determine the guest state | |
2148 | lduw [%g5 + GUEST_STATE], %g4 | |
2149 | set GUEST_STATE_SUSPENDED, %g3 | |
2150 | cmp %g4, %g3 | |
2151 | be,pn %xcc, .check_vcpu_queues | |
2152 | set GUEST_STATE_NORMAL, %g3 | |
2153 | cmp %g4, %g3 | |
2154 | be,pn %xcc, .check_vcpu_queues | |
2155 | set GUEST_STATE_EXITING, %g3 | |
2156 | cmp %g4, %g3 | |
2157 | be,pn %xcc, .drop_nrq_pkt | |
2158 | set GUEST_STATE_STOPPED, %g3 | |
2159 | cmp %g4, %g3 | |
2160 | be,pn %xcc, .drop_nrq_pkt | |
2161 | set GUEST_STATE_UNCONFIGURED, %g3 | |
2162 | cmp %g4, %g3 | |
2163 | be,pn %xcc, .drop_nrq_pkt | |
2164 | nop | |
2165 | ||
2166 | .check_vcpu_queues: | |
2167 | ! %g1 vcpup | |
2168 | ldx [%g1 + CPU_ERRQNR_BASE_RA], %g3 ! get q base RA | |
2169 | brz,pn %g3, .queue_nonresumable_bad_queue | |
2170 | nop | |
2171 | mov ERROR_NONRESUMABLE_QUEUE_TAIL, %g3 | |
2172 | ldxa [%g3]ASI_QUEUE, %g5 ! %g5 = rq_tail | |
2173 | add %g5, 0x40, %g6 ! %g6 = rq_next = rq_tail++ | |
2174 | ldx [%g1 + CPU_ERRQNR_MASK], %g4 | |
2175 | and %g6, %g4, %g6 ! %g6 = rq_next mod | |
2176 | mov ERROR_NONRESUMABLE_QUEUE_HEAD, %g3 | |
2177 | ldxa [%g3] ASI_QUEUE, %g4 ! %g4 = rq_head | |
2178 | cmp %g6, %g4 ! head = ++tail? | |
2179 | be,pn %xcc, .queue_nonresumable_full_queue | |
2180 | mov ERROR_NONRESUMABLE_QUEUE_TAIL, %g3 | |
2181 | ||
2182 | /* | |
2183 | * Deliver NR error pkt to guest | |
2184 | */ | |
2185 | stxa %g6, [%g3]ASI_QUEUE ! new tail = rq_next | |
2186 | ! write the queue record | |
2187 | ldx [%g1 + CPU_ERRQNR_BASE], %g4 | |
2188 | add %g5, %g4, %g3 ! %g3 = base + tail | |
2189 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL], %g4 ! ehdl | |
2190 | stx %g4, [%g3 + 0x0] | |
2191 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK], %g4 ! stick | |
2192 | stx %g4, [%g3 + 0x8] | |
2193 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_EDESC], %g4 ! edesc | |
2194 | st %g4, [%g3 + 0x10] | |
2195 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 ! attr | |
2196 | st %g4, [%g3 + 0x14] | |
2197 | ldx [%g2 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 ! ra | |
2198 | stx %g4, [%g3 + 0x18] | |
2199 | ld [%g2 + STRAND_SUN4V_ERPT + ESUN4V_SZ], %g4 ! sz | |
2200 | st %g4, [%g3 + 0x20] | |
2201 | lduh [%g2 + STRAND_SUN4V_ERPT + ESUN4V_G_CPUID], %g4 ! cpuid | |
2202 | stuh %g4, [%g3 + 0x24] | |
2203 | stuh %g0, [%g3 + 0x26] ! pad | |
2204 | stx %g0, [%g3 + 0x28] ! word5 | |
2205 | stx %g0, [%g3 + 0x30] ! word6 | |
2206 | stx %g0, [%g3 + 0x38] ! word7 | |
2207 | ||
2208 | mov %g7, %g6 | |
2209 | PRINT("queue_nonresumable_erpt: entry enqueued\r\n") | |
2210 | mov %g6, %g7 | |
2211 | ||
2212 | HVRET | |
2213 | ||
2214 | .drop_nrq_pkt: | |
2215 | /* | |
2216 | * The guest is not in the proper state to receive pkts | |
2217 | * Drop packet by just returning | |
2218 | */ | |
2219 | #ifdef DEBUG | |
2220 | mov %g7, %g6 | |
2221 | PRINT("no guest to deliver NR error pkt. Dropping it\r\n") | |
2222 | mov %g6, %g7 | |
2223 | #endif | |
2224 | HVRET | |
2225 | ||
2226 | .queue_nonresumable_full_queue: | |
2227 | /* | |
2228 | * The nonresumable error queue is full. | |
2229 | * Reset the guest | |
2230 | */ | |
2231 | #ifdef DEBUG | |
2232 | mov %g7, %g6 | |
2233 | PRINT("queue_nonresumable_erpt: nrq full - exiting guest\r\n") | |
2234 | mov %g6, %g7 | |
2235 | #endif | |
2236 | ba,a .queue_nonresumable_reset | |
2237 | ||
2238 | .queue_nonresumable_bad_queue: | |
2239 | /* | |
2240 | * The nonresumable error queue is not allocated/initialized | |
2241 | * Reset the guest | |
2242 | */ | |
2243 | #ifdef DEBUG | |
2244 | mov %g7, %g6 | |
2245 | PRINT("NRQ NOT ALLOC - exiting guest\r\n") | |
2246 | mov %g6, %g7 | |
2247 | #endif | |
2248 | /* fall through */ | |
2249 | ||
2250 | .queue_nonresumable_reset: | |
2251 | #ifdef NIAGARA_BRINGUP | |
2252 | rdpr %tl, %g2 | |
2253 | deccc %g2 | |
2254 | bz %xcc, 1f | |
2255 | nop | |
2256 | wrpr %g2, %tl | |
2257 | PRINT("TPC \r\n") | |
2258 | rdpr %tpc, %g1 | |
2259 | PRINTX(%g1) | |
2260 | PRINT("\r\n") | |
2261 | PRINT("TT \r\n") | |
2262 | rdpr %tt, %g1 | |
2263 | PRINTX(%g1) | |
2264 | PRINT("\r\n") | |
2265 | PRINT("TSTATE \r\n") | |
2266 | rdpr %tstate, %g1 | |
2267 | PRINTX(%g1) | |
2268 | PRINT("\r\n") | |
2269 | 1: | |
2270 | #endif | |
2271 | ba,a .err_resume_bad_guest_err_q | |
2272 | SET_SIZE(queue_nonresumable_erpt) | |
2273 | ||
2274 | ||
2275 | /* | |
2276 | * JBUS error | |
2277 | */ | |
2278 | ENTRY(ue_jbus_err) | |
2279 | ||
2280 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g6, %g5) ! g6->strand, g5->strand.ue_rpt | |
2281 | ||
2282 | SPINLOCK_IDLE_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
2283 | ! At this point, this is the only strand executing | |
2284 | ||
2285 | /* | |
2286 | * Generate a basic error report | |
2287 | */ | |
2288 | LOAD_BASIC_ERPT(%g6, %g5, %g1, %g2) | |
2289 | ||
2290 | /* | |
2291 | * Clear unused diag buf fields | |
2292 | */ | |
2293 | stx %g0, [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFSR] | |
2294 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_L2_AFSR(0)] | |
2295 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_L2_AFSR(1)] | |
2296 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_L2_AFSR(2)] | |
2297 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_L2_AFSR(3)] | |
2298 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_DRAM_AFSR(0)] | |
2299 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_DRAM_AFSR(1)] | |
2300 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_DRAM_AFSR(2)] | |
2301 | stx %g0, [%g5 + STRAND_VBSC_ERPT + STRAND_EVBSC_DRAM_AFSR(3)] | |
2302 | ||
2303 | /* | |
2304 | * Store JBUS error data in error report | |
2305 | */ | |
2306 | DUMP_JBI_SSI(%g6, %g5, %g3, %g4, %g1, %g2, %g7) | |
2307 | ||
2308 | /* | |
2309 | * Clear the JBI errors logged in the erpt | |
2310 | */ | |
2311 | STRAND_ERPT_STRUCT(STRAND_UE_RPT, %g6, %g5) ! g6->strand, g5->strand.ue_rpt | |
2312 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_JBI_ERR_LOG], %g1 | |
2313 | setx JBI_ERR_LOG, %g3, %g2 | |
2314 | stx %g1, [%g2] | |
2315 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_DIAG_BUF + JS_JBI_ERR_OVF], %g4 | |
2316 | setx JBI_ERR_OVF, %g3, %g2 | |
2317 | stx %g4, [%g2] | |
2318 | or %g1, %g4, %g1 ! combine primary and overflow for fatal check | |
2319 | CPU_PUSH(%g1, %g2, %g3, %g4) /* save JBI_ERR_LOG|JVI_ERR_OVF */ | |
2320 | ||
2321 | /* | |
2322 | * send UE diag report | |
2323 | */ | |
2324 | add %g6, STRAND_UE_RPT + STRAND_VBSC_ERPT, %g1 ! erpt.vbsc | |
2325 | set STRAND_UE_RPT + STRAND_UNSENT_PKT, %g2 | |
2326 | add %g6, %g2, %g2 ! erpt.unsent flag | |
2327 | mov EVBSC_SIZE, %g3 ! size | |
2328 | HVCALL(send_diag_erpt) | |
2329 | ||
2330 | STRAND_STRUCT(%g6) | |
2331 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
2332 | ||
2333 | /* | |
2334 | * Clear interrupt | |
2335 | */ | |
2336 | setx IOBBASE, %g3, %g2 | |
2337 | stx %g0, [%g2 + INT_CTL + INT_CTL_DEV_OFF(IOBDEV_SSIERR)] | |
2338 | ||
2339 | /* | |
2340 | * Get saved JBI error log register and check for fatal errors | |
2341 | */ | |
2342 | CPU_POP(%g1, %g2, %g3, %g4) | |
2343 | btst JBI_ABORT_ERRS, %g1 | |
2344 | bnz,pn %xcc, .ue_jbus_err_fatal | |
2345 | nop | |
2346 | ||
2347 | /* | |
2348 | * Not a fatal JBI error, we sent the info to vbsc so just | |
2349 | * return to whatever this strand was doing. | |
2350 | */ | |
2351 | retry | |
2352 | ||
2353 | .ue_jbus_err_fatal: | |
2354 | LEGION_EXIT(3) | |
2355 | ! abort HV | |
2356 | ba,pt %xcc, hvabort | |
2357 | rd %pc, %g1 | |
2358 | SET_SIZE(ue_jbus_err) | |
2359 | ||
2360 | ||
2361 | /* | |
2362 | * irc_check(uint64_t sparc_ear) [Non-LEAF] | |
2363 | * | |
2364 | * Checks whether the IRC error is transient or persistent. | |
2365 | * Before we re-read the register in error, we set the irc_ear | |
2366 | * in the CPU struct to the SPARC EAR value, which has the reg# | |
2367 | * and the syndrome. A zero syndrome is not possible for error, | |
2368 | * therefore irc_ear == 0 means IRC trap didn't set it. | |
2369 | * (Note %g0 like other registers can generate errors.) | |
2370 | * If the IRU trap is taken because of a persistent uncorrectable error, | |
2371 | * the IRU trap handler will check the irc_ear field with the SPARC_EAR | |
2372 | * logged. If they are the same, then IRU trap handler clears the | |
2373 | * irc_ear field and returns. | |
2374 | * | |
2375 | * set_ircear(sparc_ear) | |
2376 | * irf_reread(sparc_ear) | |
2377 | * if (CPU.irc_ear == 0) | |
2378 | * return RF_PERSISTENT; | |
2379 | * else { | |
2380 | * CPU.irc_ear = 0; | |
2381 | * return RF_TRANSIENT; | |
2382 | * } | |
2383 | * Arguments: | |
2384 | * %g1 - input - SPARC EAR - clobbered | |
2385 | * %g2 - output (RF_TRANSIENT, RF_PERSISTENT) | |
2386 | * %g3 - scratch | |
2387 | * %g4 - scratch | |
2388 | * %g5 - erpt | |
2389 | * %g6 - strand | |
2390 | * %g7 - return address | |
2391 | */ | |
2392 | ENTRY_NP(irc_check) | |
2393 | ||
2394 | ! init STRAND.irc_ear | |
2395 | stx %g1, [%g6 + STRAND_REGERR] ! CPU.irc_ear = sparc EAR (!=0) | |
2396 | ||
2397 | ! reread register | |
2398 | mov %g7, %g6 ! save return address | |
2399 | HVCALL(irf_reread) ! %g1 has SPARC EAR | |
2400 | mov %g6, %g7 ! restore return address | |
2401 | ||
2402 | STRAND_STRUCT(%g6) ! restore g6->strand | |
2403 | ||
2404 | ! check STRAND.irc_ear | |
2405 | ldx [%g6 + STRAND_REGERR], %g2 ! read STRAND.irc_ear | |
2406 | brz %g2, .irc_ret ! persistent error | |
2407 | mov RF_PERSISTENT, %g2 | |
2408 | ||
2409 | ! transient error. H/W has fixed it now after the reread | |
2410 | ! get back to interrupted program | |
2411 | stx %g0, [%g6 + STRAND_REGERR] ! clear irc_ear | |
2412 | mov RF_TRANSIENT, %g2 ! return transient | |
2413 | .irc_ret: | |
2414 | HVRET | |
2415 | SET_SIZE(irc_check) | |
2416 | ||
2417 | ||
2418 | /* | |
2419 | * int iru_check(uint64_t sparc_ear) [Non-Leaf] | |
2420 | * | |
2421 | * Check whether the IRU error is transient, persistent | |
2422 | * or if the integer register file is flaky. | |
2423 | * | |
2424 | * clear_irf_ue(sparc_ear); | |
2425 | * irf_reread(sparc_ear); | |
2426 | * if (SPARC_ESR.IRU == 0) { | |
2427 | * return RF_TRANSIENT; | |
2428 | * } | |
2429 | * if (SPARC_EAR == sparc_ear) | |
2430 | * return RF_PERSISTENT; | |
2431 | * } else { | |
2432 | * return RF_FAILURE; | |
2433 | * } | |
2434 | * Arguments: | |
2435 | * %g1 - input - SPARC EAR - clobbered | |
2436 | * %g2 - output (RF_TRANSIENT, RF_PERSISTENT, RF_FAILURE) | |
2437 | * %g3 - scratch | |
2438 | * %g4 - scratch | |
2439 | * %g5 - erpt pointer | |
2440 | * %g6 - strand pointer | |
2441 | */ | |
2442 | ENTRY_NP(iru_check) | |
2443 | mov %g7, %g6 ! save return address | |
2444 | ||
2445 | HVCALL(clear_irf_ue) ! %g1 has SPARC EAR | |
2446 | ||
2447 | ! reread register | |
2448 | HVCALL(irf_reread) ! %g1 has SPARC EAR | |
2449 | ||
2450 | mov %g6, %g7 ! restore return address | |
2451 | STRAND_STRUCT(%g6) ! restore strand | |
2452 | ||
2453 | ! check SPARC ESR for IRU error | |
2454 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! get SPARC ESR | |
2455 | set SPARC_ESR_IRU, %g3 ! IRU bit | |
2456 | btst %g3, %g4 ! check for IRU | |
2457 | bz %xcc, .iru_ret ! no: | |
2458 | mov RF_TRANSIENT, %g2 ! return transient | |
2459 | ||
2460 | ! persistent IRU error? | |
2461 | ! check EAR for match | |
2462 | ldxa [%g0]ASI_SPARC_ERR_ADDR, %g2 ! get SPARC EAR | |
2463 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 ! saved EAR | |
2464 | xor %g2, %g1, %g2 ! Are they the same? | |
2465 | andcc %g2, SPARC_EAR_IREG_MASK, %g2 ! (ignore non-register bits) | |
2466 | bnz %xcc, .iru_ret ! no: | |
2467 | mov RF_FAILURE, %g2 ! return reg file failure | |
2468 | stxa %g4, [%g0]ASI_SPARC_ERR_STATUS ! yes: clear SPARC ESR | |
2469 | mov RF_PERSISTENT, %g2 ! return persistent error | |
2470 | .iru_ret: | |
2471 | HVRET ! return to caller | |
2472 | SET_SIZE(iru_check) | |
2473 | ||
2474 | ||
2475 | /* | |
2476 | * void irf_reread(uint64_t sparc_ear) [LEAF function] | |
2477 | * | |
2478 | * Caller: IRC or IRU handler | |
2479 | * | |
2480 | * Re-read integer register in error | |
2481 | * Arguments: | |
2482 | * %g1 - input - SPARC_EAR | |
2483 | * %g2 - %g4 - scratch | |
2484 | * %g5, %g6 - preserved | |
2485 | * %g7 - return address | |
2486 | */ | |
2487 | ENTRY_NP(irf_reread) | |
2488 | and %g1, SPARC_EAR_IREG_MASK, %g2 | |
2489 | srlx %g2, SPARC_EAR_IREG_SHIFT, %g2 ! %g2 has int reg num | |
2490 | ||
2491 | ! %g2 has the int reg# in error. | |
2492 | ! Current window is pointing to the window of the reg in error | |
2493 | ||
2494 | ! get the register number within the set | |
2495 | and %g2, 0x1f, %g2 ! mask off GL/CWP | |
2496 | cmp %g2, 8 ! is reg# < 8? | |
2497 | bl .glob ! yes, then global reg | |
2498 | nop | |
2499 | ||
2500 | ! Now re-read the register in error | |
2501 | ba 1f ! do reread | |
2502 | rd %pc, %g3 ! get reread instr base addr | |
2503 | ! an array of instruction blocks indexed by register number to | |
2504 | ! reread the non-global register reported in error. | |
2505 | or %g0, %o0, %o0 ! reread %o0 | |
2506 | ba,a .reread_done | |
2507 | or %g0, %o1, %o1 ! reread %o1 | |
2508 | ba,a .reread_done | |
2509 | or %g0, %o2, %o2 ! reread %o2 | |
2510 | ba,a .reread_done | |
2511 | or %g0, %o3, %o3 ! reread %o3 | |
2512 | ba,a .reread_done | |
2513 | or %g0, %o4, %o4 ! reread %o4 | |
2514 | ba,a .reread_done | |
2515 | or %g0, %o5, %o5 ! reread %o5 | |
2516 | ba,a .reread_done | |
2517 | or %g0, %o6, %o6 ! reread %o6 | |
2518 | ba,a .reread_done | |
2519 | or %g0, %o7, %o7 ! reread %o7 | |
2520 | ba,a .reread_done | |
2521 | or %g0, %l0, %l0 ! reread %l0 | |
2522 | ba,a .reread_done | |
2523 | or %g0, %l1, %l1 ! reread %l1 | |
2524 | ba,a .reread_done | |
2525 | or %g0, %l2, %l2 ! reread %l2 | |
2526 | ba,a .reread_done | |
2527 | or %g0, %l3, %l3 ! reread %l3 | |
2528 | ba,a .reread_done | |
2529 | or %g0, %l4, %l4 ! reread %l4 | |
2530 | ba,a .reread_done | |
2531 | or %g0, %l5, %l5 ! reread %l5 | |
2532 | ba,a .reread_done | |
2533 | or %g0, %l6, %l6 ! reread %l6 | |
2534 | ba,a .reread_done | |
2535 | or %g0, %l7, %l7 ! reread %l7 | |
2536 | ba,a .reread_done | |
2537 | or %g0, %i0, %i0 ! reread %i0 | |
2538 | ba,a .reread_done | |
2539 | or %g0, %i1, %i1 ! reread %i1 | |
2540 | ba,a .reread_done | |
2541 | or %g0, %i2, %i2 ! reread %i2 | |
2542 | ba,a .reread_done | |
2543 | or %g0, %i3, %i3 ! reread %i3 | |
2544 | ba,a .reread_done | |
2545 | or %g0, %i4, %i4 ! reread %i4 | |
2546 | ba,a .reread_done | |
2547 | or %g0, %i5, %i5 ! reread %i5 | |
2548 | ba,a .reread_done | |
2549 | or %g0, %i6, %i6 ! reread %i6 | |
2550 | ba,a .reread_done | |
2551 | or %g0, %i7, %i7 ! reread %i7 | |
2552 | ba,a .reread_done | |
2553 | 1: | |
2554 | sub %g2, 8, %g2 ! skip globals | |
2555 | sllx %g2, 3, %g2 ! offset = reg# * 8 | |
2556 | add %g3, %g2, %g3 ! %g3 = instruction block addr | |
2557 | ||
2558 | ldxa [%g0]ASI_SPARC_ERR_EN, %g2 ! save current in %g2 | |
2559 | andn %g2, CEEN, %g4 ! disable CEEN | |
2560 | stxa %g4, [%g0] ASI_SPARC_ERR_EN ! .. | |
2561 | ||
2562 | jmp %g3 + SZ_INSTR ! jmp to reread register | |
2563 | nop | |
2564 | ||
2565 | ! restore gl from value in %o0, and restore %o0 | |
2566 | .gl_reread_done: | |
2567 | wrpr %o0, %gl ! restore %gl | |
2568 | mov %g4, %o0 ! restore %o0 | |
2569 | ||
2570 | ! Here, we check the iregerr field after the reread. If it | |
2571 | ! is zero, then we know it is a persistent uncorrectable error. | |
2572 | ! If it is nonzero, then we know it is a transient error. | |
2573 | .reread_done: | |
2574 | stxa %g2, [%g0] ASI_SPARC_ERR_EN ! restore CEEN | |
2575 | HVRET ! return to caller | |
2576 | ||
2577 | ! %g2 has the register number | |
2578 | .glob: | |
2579 | ! now re-read the global register in error | |
2580 | ba 1f | |
2581 | rd %pc, %g3 ! reread instruction base addr | |
2582 | ! an array of instructions blocks indexed by global register number | |
2583 | ! to reread the global register reported in error. | |
2584 | ! %gl points to the error global set | |
2585 | or %g0, %g0, %g0 ! reread %g0 (yay!) | |
2586 | ba,a .gl_reread_done | |
2587 | or %g0, %g1, %g1 ! reread %g1 | |
2588 | ba,a .gl_reread_done | |
2589 | or %g0, %g2, %g2 ! reread %g2 | |
2590 | ba,a .gl_reread_done | |
2591 | or %g0, %g3, %g3 ! reread %g3 | |
2592 | ba,a .gl_reread_done | |
2593 | or %g0, %g4, %g4 ! reread %g4 | |
2594 | ba,a .gl_reread_done | |
2595 | or %g0, %g5, %g5 ! reread %g5 | |
2596 | ba,a .gl_reread_done | |
2597 | or %g0, %g6, %g6 ! reread %g6 | |
2598 | ba,a .gl_reread_done | |
2599 | or %g0, %g7, %g7 ! reread %g7 | |
2600 | ba,a .gl_reread_done | |
2601 | 1: | |
2602 | sllx %g2, 3, %g2 ! offset (2 instrs) | |
2603 | add %g3, %g2, %g3 ! %g3 = instruction entry | |
2604 | ||
2605 | ldxa [%g0]ASI_SPARC_ERR_EN, %g2 ! save current in %g2 | |
2606 | andn %g2, CEEN, %g4 ! disable CEEN | |
2607 | stxa %g4, [%g0] ASI_SPARC_ERR_EN ! .. | |
2608 | ||
2609 | mov %o0, %g4 ! save %o0 in %g4 | |
2610 | rdpr %gl, %o0 ! save %gl in %o0 | |
2611 | ||
2612 | ! set gl to error global | |
2613 | and %g1, SPARC_EAR_GL_MASK, %g1 ! get global set from EAR | |
2614 | srlx %g1, SPARC_EAR_GL_SHIFT, %g1 ! %g1 has %gl value | |
2615 | ||
2616 | jmp %g3 + SZ_INSTR ! jump to reread global | |
2617 | wrpr %g1, %gl ! set gl to error gl | |
2618 | SET_SIZE(irf_reread) | |
2619 | ||
2620 | ||
2621 | /* clear_iregerr(sparc_ear) [LEAF Function] | |
2622 | * | |
2623 | * Clear CPU.iregerr if the IRU register in error == CPU.iregerr | |
2624 | * Return 0 if CPU.iregerr matches, and 1 if no match | |
2625 | * Arguments: | |
2626 | * %g1 - SPARC EAR | |
2627 | * %g2 - output - 0 if CPU.iregerr matches, 1 if no match | |
2628 | * %g3, %g4 - scratch | |
2629 | * %g5 - erpt pointer | |
2630 | * %g6 - strand pointer | |
2631 | * %g7 - return address | |
2632 | */ | |
2633 | ENTRY_NP(clear_iregerr) | |
2634 | ldx [%g6 + STRAND_REGERR], %g3 ! %g3 = STRAND.iregerr | |
2635 | ||
2636 | ! compare the register number from EAR | |
2637 | xor %g3, %g1, %g3 ! Are they the same? | |
2638 | andcc %g3, SPARC_EAR_IREG_MASK, %g3 ! (ignore non-register bits) | |
2639 | bz %xcc, .ireg_match ! yes, then clear | |
2640 | nop | |
2641 | mov 1, %g2 ! return 1 for no match | |
2642 | HVRET | |
2643 | ||
2644 | ! %g4 has CPU.iregerr address | |
2645 | ! IRU was taken from IRC trap handler reread attempt | |
2646 | .ireg_match: | |
2647 | stx %g0, [%g6 + STRAND_REGERR] ! clear STRAND.iregerr | |
2648 | mov %g0, %g2 ! return 0 for ireg match | |
2649 | HVRET | |
2650 | SET_SIZE(clear_iregerr) | |
2651 | ||
2652 | ||
2653 | /* | |
2654 | * void clear_irf_ue(uint64_t sparc_ear) | |
2655 | * | |
2656 | * Clear the UE in the integer register file | |
2657 | * Arguments: | |
2658 | * %g1 - input - SPARC EAR | |
2659 | * %g2-%g4 -scratch | |
2660 | * %g5, %g6 - preserved | |
2661 | * %g7 - return address | |
2662 | */ | |
2663 | ENTRY_NP(clear_irf_ue) | |
2664 | and %g1, SPARC_EAR_IREG_MASK, %g2 | |
2665 | srlx %g2, SPARC_EAR_IREG_SHIFT, %g2 ! %g2 has int reg num | |
2666 | ! get the register number within the set | |
2667 | and %g2, 0x1f, %g2 ! mask off GL/CWP | |
2668 | cmp %g2, 8 ! is reg# < 8? | |
2669 | bl .glob_ue ! yes, then global reg | |
2670 | nop | |
2671 | ||
2672 | ! Now clear the register in error | |
2673 | ba 1f ! clear register | |
2674 | rd %pc, %g3 ! get clear instr base addr | |
2675 | ! an array of instruction blocks indexed by register number to | |
2676 | ! clear the non-global register reported in error. | |
2677 | mov %g0, %o0 ! clear %o0 | |
2678 | ba,a .clear_done | |
2679 | mov %g0, %o1 ! clear %o1 | |
2680 | ba,a .clear_done | |
2681 | mov %g0, %o2 ! clear %o2 | |
2682 | ba,a .clear_done | |
2683 | mov %g0, %o3 ! clear %o3 | |
2684 | ba,a .clear_done | |
2685 | mov %g0, %o4 ! clear %o4 | |
2686 | ba,a .clear_done | |
2687 | mov %g0, %o5 ! clear %o5 | |
2688 | ba,a .clear_done | |
2689 | mov %g0, %o6 ! clear %o6 | |
2690 | ba,a .clear_done | |
2691 | mov %g0, %o7 ! clear %o7 | |
2692 | ba,a .clear_done | |
2693 | mov %g0, %l0 ! clear %l0 | |
2694 | ba,a .clear_done | |
2695 | mov %g0, %l1 ! clear %l1 | |
2696 | ba,a .clear_done | |
2697 | mov %g0, %l2 ! clear %l2 | |
2698 | ba,a .clear_done | |
2699 | mov %g0, %l3 ! clear %l3 | |
2700 | ba,a .clear_done | |
2701 | mov %g0, %l4 ! clear %l4 | |
2702 | ba,a .clear_done | |
2703 | mov %g0, %l5 ! clear %l5 | |
2704 | ba,a .clear_done | |
2705 | mov %g0, %l6 ! clear %l6 | |
2706 | ba,a .clear_done | |
2707 | mov %g0, %l7 ! clear %l7 | |
2708 | ba,a .clear_done | |
2709 | mov %g0, %i0 ! clear %i0 | |
2710 | ba,a .clear_done | |
2711 | mov %g0, %i1 ! clear %i1 | |
2712 | ba,a .clear_done | |
2713 | mov %g0, %i2 ! clear %i2 | |
2714 | ba,a .clear_done | |
2715 | mov %g0, %i3 ! clear %i3 | |
2716 | ba,a .clear_done | |
2717 | mov %g0, %i4 ! clear %i4 | |
2718 | ba,a .clear_done | |
2719 | mov %g0, %i5 ! clear %i5 | |
2720 | ba,a .clear_done | |
2721 | mov %g0, %i6 ! clear %i6 | |
2722 | ba,a .clear_done | |
2723 | mov %g0, %i7 ! clear %i7 | |
2724 | ba,a .clear_done | |
2725 | 1: | |
2726 | sub %g2, 8, %g2 ! skip globals | |
2727 | sllx %g2, 3, %g2 ! offset = reg# * 8 | |
2728 | add %g3, %g2, %g3 ! %g3 = instruction block addr | |
2729 | jmp %g3 + SZ_INSTR ! jmp to clear register | |
2730 | nop | |
2731 | ||
2732 | ! restore gl from value in %o0, and restore %o0 | |
2733 | .gl_clear_done: | |
2734 | wrpr %o0, %gl ! restore %gl | |
2735 | mov %g4, %o0 ! restore %o0 | |
2736 | ||
2737 | ! Here, we check the iregerr field after the reread. If it | |
2738 | ! is zero, then we know it is a persistent uncorrectable error. | |
2739 | ! If it is nonzero, then we know it is a transient error. | |
2740 | .clear_done: | |
2741 | HVRET ! return to caller | |
2742 | ||
2743 | ! %g2 has the gl + register number | |
2744 | .glob_ue: | |
2745 | ! now re-read the global register in error | |
2746 | ba 1f | |
2747 | rd %pc, %g3 ! get clear instr base addr | |
2748 | ! an array of instructions blocks indexed by global register number | |
2749 | ! to clear the global register reported in error. | |
2750 | ! %gl points to the error global set | |
2751 | mov %g0, %g0 ! clear %g0 (yay!) | |
2752 | ba,a .gl_clear_done | |
2753 | mov %g0, %g1 ! clear %g1 | |
2754 | ba,a .gl_clear_done | |
2755 | mov %g0, %g2 ! clear %g2 | |
2756 | ba,a .gl_clear_done | |
2757 | mov %g0, %g3 ! clear %g3 | |
2758 | ba,a .gl_clear_done | |
2759 | mov %g0, %g4 ! clear %g4 | |
2760 | ba,a .gl_clear_done | |
2761 | mov %g0, %g5 ! clear %g5 | |
2762 | ba,a .gl_clear_done | |
2763 | mov %g0, %g6 ! clear %g6 | |
2764 | ba,a .gl_clear_done | |
2765 | mov %g0, %g7 ! clear %g7 | |
2766 | ba,a .gl_clear_done | |
2767 | 1: | |
2768 | sllx %g2, 3, %g2 ! offset (2 instrs) | |
2769 | add %g3, %g2, %g3 ! %g3 = instruction entry | |
2770 | mov %o0, %g4 ! save %o0 in %g4 | |
2771 | rdpr %gl, %o0 ! save %gl in %o0 | |
2772 | ||
2773 | ! set gl to error global | |
2774 | and %g1, SPARC_EAR_GL_MASK, %g2 ! get global set from EAR | |
2775 | srlx %g2, SPARC_EAR_GL_SHIFT, %g2 ! %g2 has %gl value | |
2776 | ||
2777 | jmp %g3 + SZ_INSTR ! jump to clear global | |
2778 | wrpr %g2, %gl ! set gl to error gl | |
2779 | SET_SIZE(clear_irf_ue) | |
2780 | ||
2781 | ||
2782 | /* | |
2783 | * frc_check(uint64_t sparc_ear) [Non-Leaf] | |
2784 | * | |
2785 | * Check whether the FRC error is transient or persistent. | |
2786 | * Before we re-read the register in error, we set the frc_ear | |
2787 | * in the CPU struct to the SPARC EAR value, which has the reg# | |
2788 | * and the syndrome. A zero syndrome is not possible for error, | |
2789 | * therefore frc_ear == 0 means FRC trap didn't set it. | |
2790 | * (Note %g0 like other registers can generate errors.) | |
2791 | * If the FRU trap is taken because of a persistent uncorrectable error, | |
2792 | * the FRU trap handler will check the frc_ear field with the SPARC_EAR | |
2793 | * logged. If they are the same, then FRU trap handler clears the | |
2794 | * frc_ear field and returns. | |
2795 | * | |
2796 | * set_frcear(sparc_ear) | |
2797 | * frf_reread(sparc_ear) | |
2798 | * if (cpu.frc_ear == 0) | |
2799 | * return RF_PERSISTENT; | |
2800 | * else { | |
2801 | * cpu.frc_ear = 0; | |
2802 | * return RF_TRANSIENT; | |
2803 | * } | |
2804 | * Arguments: | |
2805 | * %g1 - input - SPARC EAR - clobbered | |
2806 | * %g2 - output (RF_TRANSIENT, RF_PERSISTENT) | |
2807 | * %g3 - scratch | |
2808 | * %g4 - scratch | |
2809 | * %g5 - erpt pointer | |
2810 | * %g6 - strand pointer | |
2811 | */ | |
2812 | ENTRY_NP(frc_check) | |
2813 | ||
2814 | ! init strand.frc_ear | |
2815 | stx %g1, [%g6 + STRAND_REGERR] ! strand.frc_ear = sparc EAR (!=0) | |
2816 | ||
2817 | /* | |
2818 | * It is possible that FPRS.FEF was disabled when we took the | |
2819 | * disrupting trap caused by the FP CE. We must ensure that FPRS.FEF | |
2820 | * is enabled before calling frf_reread(). | |
2821 | * | |
2822 | * Note that the Sparc V9 spec mandates that PSTATE.PEF be enabled | |
2823 | * when we take a trap if there is an FPU present. As this error | |
2824 | * condition can only occur with an FPU we do not need to verify | |
2825 | * PSTATE.PEF here. | |
2826 | */ | |
2827 | rd %fprs, %g5 | |
2828 | btst FPRS_FEF, %g5 ! FPRS.FEF set ? | |
2829 | bz,a,pn %xcc, 1f ! no: set it | |
2830 | wr %g5, FPRS_FEF, %fprs ! yes: annulled | |
2831 | 1: | |
2832 | ! reread register | |
2833 | mov %g7, %g6 ! save return address | |
2834 | HVCALL(frf_reread) ! %g1 has SPARC EAR, | |
2835 | ! %g5/%g6 preserved | |
2836 | wr %g5, %g0, %fprs ! restore FPRS | |
2837 | mov %g6, %g7 ! restore return address | |
2838 | ||
2839 | STRAND_ERPT_STRUCT(STRAND_CE_RPT, %g6, %g5) ! g6->strand, g5->strand.ce_rpt | |
2840 | ||
2841 | ! check strand.frc_ear | |
2842 | ldx [%g6 + STRAND_REGERR], %g2 ! read strand.frc_ear | |
2843 | brz %g2, .frc_ret ! persistent error | |
2844 | mov RF_PERSISTENT, %g2 | |
2845 | ||
2846 | ! transient error. H/W has fixed it now after the reread | |
2847 | ! get back to interrupted program | |
2848 | stx %g0, [%g6 + STRAND_REGERR] ! clear frc_ear | |
2849 | mov RF_TRANSIENT, %g2 ! return transient | |
2850 | .frc_ret: | |
2851 | HVRET | |
2852 | SET_SIZE(frc_check) | |
2853 | ||
2854 | ||
2855 | /* | |
2856 | * fru_check(uint64_t sparc_ear) [Non-Leaf] | |
2857 | * | |
2858 | * Check whether the FRU error is transient or persistent | |
2859 | * or if the floating point register file is failing. | |
2860 | * clear_frf_ue(sparc_ear); | |
2861 | * frf_reread(sparc_ear); | |
2862 | * if (SPARC_ESR.FRU == 0) { | |
2863 | * return RF_TRANSIENT; | |
2864 | * } | |
2865 | * if (SPARC_EAR == sparc_ear) | |
2866 | * return RF_PERSISTENT; | |
2867 | * } else { | |
2868 | * return RF_FAILURE; | |
2869 | * } | |
2870 | * Arguments: | |
2871 | * %g1 - input - SPARC EAR - clobbered | |
2872 | * %g2 - output (RF_TRANSIENT, RF_PERSISTENT, RF_FAILURE) | |
2873 | * %g3 - scratch | |
2874 | * %g4 - scratch | |
2875 | * %g5 - erpt pointer | |
2876 | * %g6 - strand pointer | |
2877 | */ | |
2878 | ENTRY_NP(fru_check) | |
2879 | mov %g7, %g6 ! save return address | |
2880 | ||
2881 | HVCALL(clear_frf_ue) ! %g1 has SPARC EAR | |
2882 | ||
2883 | ! reread register | |
2884 | HVCALL(frf_reread) ! %g1 has SPARC EAR | |
2885 | ||
2886 | mov %g6, %g7 ! restore return address | |
2887 | STRAND_STRUCT(%g6) ! restore strand | |
2888 | ||
2889 | ! check SPARC ESR for FRU error | |
2890 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! get SPARC ESR | |
2891 | set SPARC_ESR_FRU, %g3 ! FRU bit | |
2892 | btst %g3, %g4 ! check for FRU | |
2893 | bz %xcc, .fru_ret ! no: | |
2894 | mov RF_TRANSIENT, %g2 ! return transient | |
2895 | ||
2896 | ! persistent FRU error? | |
2897 | ! check EAR for match | |
2898 | ldxa [%g0]ASI_SPARC_ERR_ADDR, %g2 ! get SPARC EAR | |
2899 | ldx [%g5 + STRAND_VBSC_ERPT + EVBSC_SPARC_AFAR], %g1 ! saved EAR | |
2900 | xor %g2, %g1, %g2 ! Are they the same? | |
2901 | andcc %g2, SPARC_EAR_FPREG_MASK, %g2 ! (ignore non-register bits) | |
2902 | bnz %xcc, .fru_ret ! no: | |
2903 | mov RF_FAILURE, %g2 ! return reg file failure | |
2904 | stxa %g4, [%g0]ASI_SPARC_ERR_STATUS ! yes: clear SPARC ESR | |
2905 | mov RF_PERSISTENT, %g2 ! return persistent | |
2906 | .fru_ret: | |
2907 | HVRET ! return to caller | |
2908 | SET_SIZE(fru_check) | |
2909 | ||
2910 | ||
2911 | /* | |
2912 | * IRF Uncorrectible ECC Error | |
2913 | * | |
2914 | * if (clear_iregerr(sparc_ear) == MATCH from IRC) { | |
2915 | * DONE; | |
2916 | * } else { | |
2917 | * if (iru_check(sparc_ear) == RF_PERSISTENT) { | |
2918 | * CPU.status = mark CPU in ERROR; | |
2919 | * if ((CPUnext = avail(partID)) != NULL) { | |
2920 | * x_call(CPUnext, I_AM_IN_ERROR); | |
2921 | * stop_self(); | |
2922 | * } else { | |
2923 | * q_service_error_report(spi); | |
2924 | * stop_self(); - watchdog reset later? | |
2925 | * } | |
2926 | * } else { | |
2927 | * q_sun4v_error_report(nrq); | |
2928 | * q_service_error_report(spi); | |
2929 | * jmp nonresumable_error trap handler | |
2930 | * } | |
2931 | * } | |
2932 | */ | |
2933 | ||
2934 | ||
2935 | /* | |
2936 | * frf_reread(uint64_t sparc_ear) [LEAF function] | |
2937 | * | |
2938 | * Reread the FRF register in error. | |
2939 | * Arguments: | |
2940 | * %g1 - input - SPARC EAR | |
2941 | * %g2 - %g4 - scratch | |
2942 | * %g5, %g6 - preserved | |
2943 | * %g7 - return address | |
2944 | */ | |
2945 | ENTRY_NP(frf_reread) | |
2946 | and %g1, SPARC_EAR_FPREG_MASK, %g2 | |
2947 | srlx %g2, SPARC_EAR_FPREG_SHIFT, %g2 ! %g2 has 6-bit fpreg number | |
2948 | ||
2949 | ! Now reread the register in error | |
2950 | ba 1f | |
2951 | rd %pc, %g3 ! %g3 = base address | |
2952 | ||
2953 | ! an array of instruction blocks indexed by register number to | |
2954 | ! reread the floating-point register reported in error | |
2955 | ! The first 32 entries use single-precision register | |
2956 | ! The next 32 entries reread the double-precision register | |
2957 | fmovs %f0, %f0 ! reread %f0 | |
2958 | ba,a .fp_reread_done | |
2959 | fmovs %f1, %f1 ! reread %f1 | |
2960 | ba,a .fp_reread_done | |
2961 | fmovs %f2, %f2 ! reread %f2 | |
2962 | ba,a .fp_reread_done | |
2963 | fmovs %f3, %f3 ! reread %f3 | |
2964 | ba,a .fp_reread_done | |
2965 | fmovs %f4, %f4 ! reread %f4 | |
2966 | ba,a .fp_reread_done | |
2967 | fmovs %f5, %f5 ! reread %f5 | |
2968 | ba,a .fp_reread_done | |
2969 | fmovs %f6, %f6 ! reread %f6 | |
2970 | ba,a .fp_reread_done | |
2971 | fmovs %f7, %f7 ! reread %f7 | |
2972 | ba,a .fp_reread_done | |
2973 | fmovs %f8, %f8 ! reread %f8 | |
2974 | ba,a .fp_reread_done | |
2975 | fmovs %f9, %f9 ! reread %f9 | |
2976 | ba,a .fp_reread_done | |
2977 | fmovs %f10, %f10 ! reread %f10 | |
2978 | ba,a .fp_reread_done | |
2979 | fmovs %f11, %f11 ! reread %f11 | |
2980 | ba,a .fp_reread_done | |
2981 | fmovs %f12, %f12 ! reread %f12 | |
2982 | ba,a .fp_reread_done | |
2983 | fmovs %f13, %f13 ! reread %f13 | |
2984 | ba,a .fp_reread_done | |
2985 | fmovs %f14, %f14 ! reread %f14 | |
2986 | ba,a .fp_reread_done | |
2987 | fmovs %f15, %f15 ! reread %f15 | |
2988 | ba,a .fp_reread_done | |
2989 | fmovs %f16, %f16 ! reread %f16 | |
2990 | ba,a .fp_reread_done | |
2991 | fmovs %f17, %f17 ! reread %f17 | |
2992 | ba,a .fp_reread_done | |
2993 | fmovs %f18, %f18 ! reread %f18 | |
2994 | ba,a .fp_reread_done | |
2995 | fmovs %f19, %f19 ! reread %f19 | |
2996 | ba,a .fp_reread_done | |
2997 | fmovs %f20, %f20 ! reread %f20 | |
2998 | ba,a .fp_reread_done | |
2999 | fmovs %f21, %f21 ! reread %f21 | |
3000 | ba,a .fp_reread_done | |
3001 | fmovs %f22, %f22 ! reread %f22 | |
3002 | ba,a .fp_reread_done | |
3003 | fmovs %f23, %f23 ! reread %f23 | |
3004 | ba,a .fp_reread_done | |
3005 | fmovs %f24, %f24 ! reread %f24 | |
3006 | ba,a .fp_reread_done | |
3007 | fmovs %f25, %f25 ! reread %f25 | |
3008 | ba,a .fp_reread_done | |
3009 | fmovs %f26, %f26 ! reread %f26 | |
3010 | ba,a .fp_reread_done | |
3011 | fmovs %f27, %f27 ! reread %f27 | |
3012 | ba,a .fp_reread_done | |
3013 | fmovs %f28, %f28 ! reread %f28 | |
3014 | ba,a .fp_reread_done | |
3015 | fmovs %f29, %f29 ! reread %f29 | |
3016 | ba,a .fp_reread_done | |
3017 | fmovs %f30, %f30 ! reread %f30 | |
3018 | ba,a .fp_reread_done | |
3019 | fmovs %f30, %f31 ! reread %f31 | |
3020 | ba,a .fp_reread_done | |
3021 | ! double precision register pairs, reread both of them on errors | |
3022 | fmovd %f32, %f32 ! reread %f32 | |
3023 | ba,a .fp_reread_done | |
3024 | fmovd %f32, %f32 ! reread %f32 | |
3025 | ba,a .fp_reread_done | |
3026 | fmovd %f34, %f34 ! reread %f34 | |
3027 | ba,a .fp_reread_done | |
3028 | fmovd %f34, %f34 ! reread %f34 | |
3029 | ba,a .fp_reread_done | |
3030 | fmovd %f36, %f36 ! reread %f36 | |
3031 | ba,a .fp_reread_done | |
3032 | fmovd %f36, %f36 ! reread %f36 | |
3033 | ba,a .fp_reread_done | |
3034 | fmovd %f38, %f38 ! reread %f38 | |
3035 | ba,a .fp_reread_done | |
3036 | fmovd %f38, %f38 ! reread %f38 | |
3037 | ba,a .fp_reread_done | |
3038 | fmovd %f40, %f40 ! reread %f40 | |
3039 | ba,a .fp_reread_done | |
3040 | fmovd %f40, %f40 ! reread %f40 | |
3041 | ba,a .fp_reread_done | |
3042 | fmovd %f42, %f42 ! reread %f42 | |
3043 | ba,a .fp_reread_done | |
3044 | fmovd %f42, %f42 ! reread %f42 | |
3045 | ba,a .fp_reread_done | |
3046 | fmovd %f44, %f44 ! reread %f44 | |
3047 | ba,a .fp_reread_done | |
3048 | fmovd %f44, %f44 ! reread %f44 | |
3049 | ba,a .fp_reread_done | |
3050 | fmovd %f46, %f46 ! reread %f46 | |
3051 | ba,a .fp_reread_done | |
3052 | fmovd %f46, %f46 ! reread %f46 | |
3053 | ba,a .fp_reread_done | |
3054 | fmovd %f48, %f48 ! reread %f48 | |
3055 | ba,a .fp_reread_done | |
3056 | fmovd %f48, %f48 ! reread %f48 | |
3057 | ba,a .fp_reread_done | |
3058 | fmovd %f50, %f50 ! reread %f50 | |
3059 | ba,a .fp_reread_done | |
3060 | fmovd %f50, %f50 ! reread %f50 | |
3061 | ba,a .fp_reread_done | |
3062 | fmovd %f52, %f52 ! reread %f52 | |
3063 | ba,a .fp_reread_done | |
3064 | fmovd %f52, %f52 ! reread %f52 | |
3065 | ba,a .fp_reread_done | |
3066 | fmovd %f54, %f54 ! reread %f54 | |
3067 | ba,a .fp_reread_done | |
3068 | fmovd %f54, %f54 ! reread %f54 | |
3069 | ba,a .fp_reread_done | |
3070 | fmovd %f56, %f56 ! reread %f56 | |
3071 | ba,a .fp_reread_done | |
3072 | fmovd %f56, %f56 ! reread %f56 | |
3073 | ba,a .fp_reread_done | |
3074 | fmovd %f58, %f58 ! reread %f58 | |
3075 | ba,a .fp_reread_done | |
3076 | fmovd %f58, %f58 ! reread %f58 | |
3077 | ba,a .fp_reread_done | |
3078 | fmovd %f60, %f60 ! reread %f60 | |
3079 | ba,a .fp_reread_done | |
3080 | fmovd %f60, %f60 ! reread %f60 | |
3081 | ba,a .fp_reread_done | |
3082 | fmovd %f62, %f62 ! reread %f62 | |
3083 | ba,a .fp_reread_done | |
3084 | fmovd %f62, %f62 ! reread %f62 | |
3085 | ba,a .fp_reread_done | |
3086 | 1: | |
3087 | ! %g2 has freg number, %g3 has base address-4 | |
3088 | sllx %g2, 3, %g2 ! offset = freg# * 8 | |
3089 | add %g3, %g2, %g3 ! %g3 = instruction block addr | |
3090 | ||
3091 | ldxa [%g0]ASI_SPARC_ERR_EN, %g2 ! save current in %g2 | |
3092 | andn %g2, CEEN, %g4 ! disable CEEN | |
3093 | stxa %g4, [%g0] ASI_SPARC_ERR_EN ! .. | |
3094 | ||
3095 | jmp %g3 + SZ_INSTR ! jmp to reread register | |
3096 | nop | |
3097 | ||
3098 | .fp_reread_done: | |
3099 | stxa %g2, [%g0] ASI_SPARC_ERR_EN ! restore CEEN | |
3100 | HVRET ! return to caller | |
3101 | SET_SIZE(frf_reread) | |
3102 | ||
3103 | ||
3104 | /* | |
3105 | * clear_fregerr(sparc_ear) [LEAF Function] | |
3106 | * | |
3107 | * Clear cpu.fregerr if the FRU register in error == cpu.fregerr | |
3108 | * Return 0 if cpu.fregerr matches, and 1 if no match | |
3109 | * Arguments: | |
3110 | * %g1 - SPARC EAR | |
3111 | * %g2 - output - 0 if cpu.fregerr matches, 1 if no match | |
3112 | * %g3, %g4 - scratch | |
3113 | * %g5 - erpt pointer | |
3114 | * %g6 - strand pointer | |
3115 | * %g7 - return address | |
3116 | */ | |
3117 | ENTRY_NP(clear_fregerr) | |
3118 | ldx [%g6 + STRAND_REGERR], %g3 ! %g3 = strand.fregerr | |
3119 | ||
3120 | ! get register number from EAR | |
3121 | xor %g3, %g1, %g3 ! Are they the same? | |
3122 | andcc %g3, SPARC_EAR_FPREG_MASK, %g3 ! (ignore non-register bits) | |
3123 | bz %xcc, .freg_match ! yes, then clear | |
3124 | nop | |
3125 | mov 1, %g2 ! return 1 for no match | |
3126 | HVRET | |
3127 | ||
3128 | ! %g4 has cpu.fregerr address | |
3129 | ! FRU was taken from FRC trap handler reread attempt | |
3130 | .freg_match: | |
3131 | stx %g0, [%g6 + STRAND_REGERR] ! clear strand.fregerr | |
3132 | mov %g0, %g2 ! return 0 for freg match | |
3133 | HVRET | |
3134 | SET_SIZE(clear_fregerr) | |
3135 | ||
3136 | ||
3137 | /* | |
3138 | * clear_frf_ue(uint64_t sparc_ear) [LEAF function] | |
3139 | * | |
3140 | * Clear the UE in the floating-point register file | |
3141 | * Arguments: | |
3142 | * %g1 - SPARC EAR | |
3143 | * %g2 - %g4 - scratch | |
3144 | * %g5, %g6 - preserverd | |
3145 | * %g7 - return address | |
3146 | */ | |
3147 | ENTRY_NP(clear_frf_ue) | |
3148 | and %g1, SPARC_EAR_FPREG_MASK, %g2 | |
3149 | srlx %g2, SPARC_EAR_FPREG_SHIFT, %g2 ! %g2 has 6-bit fpreg number | |
3150 | ||
3151 | ! Now clear the register in error | |
3152 | ba 1f | |
3153 | rd %pc, %g3 ! %g3 = base address | |
3154 | ||
3155 | ! an array of instruction blocks indexed by register number to | |
3156 | ! clear the floating-point register reported in error | |
3157 | ! The first 32 entries use single-precision register | |
3158 | ! The next 32 entries clear the double-precision register | |
3159 | fzeros %f0 ! clear %f0 | |
3160 | ba,a .fp_clear_done | |
3161 | fzeros %f1 ! clear %f1 | |
3162 | ba,a .fp_clear_done | |
3163 | fzeros %f2 ! clear %f2 | |
3164 | ba,a .fp_clear_done | |
3165 | fzeros %f3 ! clear %f3 | |
3166 | ba,a .fp_clear_done | |
3167 | fzeros %f4 ! clear %f4 | |
3168 | ba,a .fp_clear_done | |
3169 | fzeros %f5 ! clear %f5 | |
3170 | ba,a .fp_clear_done | |
3171 | fzeros %f6 ! clear %f6 | |
3172 | ba,a .fp_clear_done | |
3173 | fzeros %f7 ! clear %f7 | |
3174 | ba,a .fp_clear_done | |
3175 | fzeros %f8 ! clear %f8 | |
3176 | ba,a .fp_clear_done | |
3177 | fzeros %f9 ! clear %f9 | |
3178 | ba,a .fp_clear_done | |
3179 | fzeros %f10 ! clear %f10 | |
3180 | ba,a .fp_clear_done | |
3181 | fzeros %f11 ! clear %f11 | |
3182 | ba,a .fp_clear_done | |
3183 | fzeros %f12 ! clear %f12 | |
3184 | ba,a .fp_clear_done | |
3185 | fzeros %f13 ! clear %f13 | |
3186 | ba,a .fp_clear_done | |
3187 | fzeros %f14 ! clear %f14 | |
3188 | ba,a .fp_clear_done | |
3189 | fzeros %f15 ! clear %f15 | |
3190 | ba,a .fp_clear_done | |
3191 | fzeros %f16 ! clear %f16 | |
3192 | ba,a .fp_clear_done | |
3193 | fzeros %f17 ! clear %f17 | |
3194 | ba,a .fp_clear_done | |
3195 | fzeros %f18 ! clear %f18 | |
3196 | ba,a .fp_clear_done | |
3197 | fzeros %f19 ! clear %f19 | |
3198 | ba,a .fp_clear_done | |
3199 | fzeros %f20 ! clear %f20 | |
3200 | ba,a .fp_clear_done | |
3201 | fzeros %f21 ! clear %f21 | |
3202 | ba,a .fp_clear_done | |
3203 | fzeros %f22 ! clear %f22 | |
3204 | ba,a .fp_clear_done | |
3205 | fzeros %f23 ! clear %f23 | |
3206 | ba,a .fp_clear_done | |
3207 | fzeros %f24 ! clear %f24 | |
3208 | ba,a .fp_clear_done | |
3209 | fzeros %f25 ! clear %f25 | |
3210 | ba,a .fp_clear_done | |
3211 | fzeros %f26 ! clear %f26 | |
3212 | ba,a .fp_clear_done | |
3213 | fzeros %f27 ! clear %f27 | |
3214 | ba,a .fp_clear_done | |
3215 | fzeros %f28 ! clear %f28 | |
3216 | ba,a .fp_clear_done | |
3217 | fzeros %f29 ! clear %f29 | |
3218 | ba,a .fp_clear_done | |
3219 | fzeros %f30 ! clear %f30 | |
3220 | ba,a .fp_clear_done | |
3221 | fzeros %f31 ! clear %f31 | |
3222 | ba,a .fp_clear_done | |
3223 | ! double precision register pairs, clear both of them on errors | |
3224 | fzero %f32 ! clear %f32 | |
3225 | ba,a .fp_clear_done | |
3226 | fzero %f32 ! clear %f32 | |
3227 | ba,a .fp_clear_done | |
3228 | fzero %f34 ! clear %f34 | |
3229 | ba,a .fp_clear_done | |
3230 | fzero %f34 ! clear %f34 | |
3231 | ba,a .fp_clear_done | |
3232 | fzero %f36 ! clear %f36 | |
3233 | ba,a .fp_clear_done | |
3234 | fzero %f36 ! clear %f36 | |
3235 | ba,a .fp_clear_done | |
3236 | fzero %f38 ! clear %f38 | |
3237 | ba,a .fp_clear_done | |
3238 | fzero %f38 ! clear %f38 | |
3239 | ba,a .fp_clear_done | |
3240 | fzero %f40 ! clear %f40 | |
3241 | ba,a .fp_clear_done | |
3242 | fzero %f40 ! clear %f40 | |
3243 | ba,a .fp_clear_done | |
3244 | fzero %f42 ! clear %f42 | |
3245 | ba,a .fp_clear_done | |
3246 | fzero %f42 ! clear %f42 | |
3247 | ba,a .fp_clear_done | |
3248 | fzero %f44 ! clear %f44 | |
3249 | ba,a .fp_clear_done | |
3250 | fzero %f44 ! clear %f44 | |
3251 | ba,a .fp_clear_done | |
3252 | fzero %f46 ! clear %f46 | |
3253 | ba,a .fp_clear_done | |
3254 | fzero %f46 ! clear %f46 | |
3255 | ba,a .fp_clear_done | |
3256 | fzero %f48 ! clear %f48 | |
3257 | ba,a .fp_clear_done | |
3258 | fzero %f48 ! clear %f48 | |
3259 | ba,a .fp_clear_done | |
3260 | fzero %f50 ! clear %f50 | |
3261 | ba,a .fp_clear_done | |
3262 | fzero %f50 ! clear %f50 | |
3263 | ba,a .fp_clear_done | |
3264 | fzero %f52 ! clear %f52 | |
3265 | ba,a .fp_clear_done | |
3266 | fzero %f52 ! clear %f52 | |
3267 | ba,a .fp_clear_done | |
3268 | fzero %f54 ! clear %f54 | |
3269 | ba,a .fp_clear_done | |
3270 | fzero %f54 ! clear %f54 | |
3271 | ba,a .fp_clear_done | |
3272 | fzero %f56 ! clear %f56 | |
3273 | ba,a .fp_clear_done | |
3274 | fzero %f56 ! clear %f56 | |
3275 | ba,a .fp_clear_done | |
3276 | fzero %f58 ! clear %f58 | |
3277 | ba,a .fp_clear_done | |
3278 | fzero %f58 ! clear %f58 | |
3279 | ba,a .fp_clear_done | |
3280 | fzero %f60 ! clear %f60 | |
3281 | ba,a .fp_clear_done | |
3282 | fzero %f60 ! clear %f60 | |
3283 | ba,a .fp_clear_done | |
3284 | fzero %f62 ! clear %f62 | |
3285 | ba,a .fp_clear_done | |
3286 | fzero %f62 ! clear %f62 | |
3287 | ba,a .fp_clear_done | |
3288 | 1: | |
3289 | ! %g2 has freg number, %g3 has base address-4 | |
3290 | sllx %g2, 3, %g2 ! offset = freg# * 8 | |
3291 | add %g3, %g2, %g3 ! %g3 = instruction block addr | |
3292 | jmp %g3 + SZ_INSTR ! jmp to clear register | |
3293 | nop | |
3294 | ||
3295 | .fp_clear_done: | |
3296 | HVRET ! return to caller | |
3297 | SET_SIZE(clear_frf_ue) | |
3298 | ||
3299 | ||
3300 | /* | |
3301 | * FRC Uncorrectible ECC Error | |
3302 | * | |
3303 | * FRU Error Handler: Check for persistent error | |
3304 | * if (fru_check(sparc_ear) == RF_TRANSIENT) { | |
3305 | * q_sun4v_error_report(nrq); | |
3306 | * q_service_error_report(spi); | |
3307 | * jmp nonresumable_error trap handler; | |
3308 | * } else { | |
3309 | * CPU.status = mark CPU in ERROR; | |
3310 | * if ((CPUnext = avail(partID)) != NULL) { | |
3311 | * x_call(CPUnext, I_AM_IN_ERROR); | |
3312 | * stop_self(); | |
3313 | * } else { | |
3314 | * q_service_error_report(spi); | |
3315 | * stop_self(); causes watchdog reset later? | |
3316 | * } | |
3317 | * } | |
3318 | */ | |
3319 | ||
3320 | ||
3321 | /* | |
3322 | * Handler to set bit(s) in the SPARC Error Enable Register | |
3323 | * | |
3324 | * Called to get handler callback address (avoid relocation problems) | |
3325 | * | |
3326 | * Entry Data: | |
3327 | * none | |
3328 | * | |
3329 | * Return Data: | |
3330 | * %g2: handler address | |
3331 | * | |
3332 | * Registers modified: | |
3333 | * %g2 | |
3334 | */ | |
3335 | ENTRY_NP(err_set_sparc_bits) | |
3336 | RETURN_HANDLER_ADDRESS(%g2) ! in %g2 | |
3337 | ||
3338 | /* | |
3339 | * Callback from interrupt: | |
3340 | * | |
3341 | * This will re-enable the Sparc interrupts. | |
3342 | * Process in this order: | |
3343 | * - clear any Sparc CE's | |
3344 | * - clear blackout | |
3345 | * - enable Sparc EEN | |
3346 | * | |
3347 | * Entry Data: | |
3348 | * %g1: bit(s) to set | |
3349 | * %g2: <scratch> | |
3350 | * | |
3351 | * Return Data: | |
3352 | * none | |
3353 | * | |
3354 | * Registers modified: | |
3355 | * %g1-6 | |
3356 | */ | |
3357 | .err_set_sparc_bits: /* This is the actual function entry */ | |
3358 | mov %g1, %g5 ! bits | |
3359 | !! %g5 = bits to set | |
3360 | ||
3361 | set SPARC_CE_BITS, %g1 | |
3362 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g3 ! SPARC afsr | |
3363 | btst %g1, %g3 ! is a CE pending? | |
3364 | bz .err_set_sparc_1 ! no: | |
3365 | set SPARC_ESR_PRIV, %g2 | |
3366 | or %g1, %g2, %g1 ! yes: include PRIV | |
3367 | and %g3, %g1, %g3 ! just the CE bits | |
3368 | stxa %g3, [%g0]ASI_SPARC_ERR_STATUS ! clear SPARC CE afsr bits | |
3369 | .err_set_sparc_1: | |
3370 | STRAND_STRUCT(%g3) | |
3371 | lduw [%g3 + STRAND_ERR_FLAG], %g1 ! installed flags | |
3372 | bclr ERR_FLAG_SPARC, %g1 ! reset SPARC ESR | |
3373 | stw %g1, [%g3 + STRAND_ERR_FLAG] ! .. | |
3374 | ||
3375 | ldxa [%g0]ASI_SPARC_ERR_EN, %g3 ! get current | |
3376 | or %g3, %g5, %g3 ! set bit(s) | |
3377 | stxa %g3, [%g0]ASI_SPARC_ERR_EN ! store back | |
3378 | ||
3379 | ||
3380 | HVRET | |
3381 | SET_SIZE(err_set_sparc_bits) | |
3382 | ||
3383 | ||
3384 | /* | |
3385 | * Handler to set bit(s) in the L2 Error Enable Register | |
3386 | * | |
3387 | * This will re-enable the L2/DRAM interrupts. | |
3388 | * Process in this order: | |
3389 | * - clear any DRAM CE's | |
3390 | * - clear any L2 CE's | |
3391 | * - clear blackout | |
3392 | * - enable L2DRAM EEN | |
3393 | * | |
3394 | * Called to get handler callback address (avoid relocation problems) | |
3395 | * | |
3396 | * Entry Data: | |
3397 | * none | |
3398 | * | |
3399 | * Return Data: | |
3400 | * %g2: handler address | |
3401 | * | |
3402 | * Registers modified: | |
3403 | * %g2 | |
3404 | */ | |
3405 | ENTRY_NP(err_set_l2_bits) | |
3406 | RETURN_HANDLER_ADDRESS(%g2) ! in %g2 | |
3407 | ||
3408 | /* | |
3409 | * Callback from interrupt: | |
3410 | * | |
3411 | * Entry Data: | |
3412 | * %g1: bit(s) to set | |
3413 | * %g2: B:5-0 = bank # | |
3414 | * | |
3415 | * Return Data: | |
3416 | * none | |
3417 | * | |
3418 | * Registers modified: | |
3419 | * %g1-6 | |
3420 | */ | |
3421 | .err_set_l2_bits: /* This is the actual function entry */ | |
3422 | mov %g1, %g5 ! bits | |
3423 | and %g2, NO_L2_BANKS - 1, %g6 ! bank # | |
3424 | !! %g5 = bits to set | |
3425 | !! %g6 = bank# | |
3426 | setx DRAM_ESR_CE_BITS | DRAM_ESR_MEC, %g1, %g2 | |
3427 | setx DRAM_ESR_BASE, %g1, %g3 ! DRAM base | |
3428 | sllx %g6, DRAM_BANK_SHIFT, %g4 ! + bank offset | |
3429 | ldx [%g3 + %g4], %g1 ! get ESR[bank] | |
3430 | and %g1, %g2, %g1 ! reset CE bits only | |
3431 | stx %g1, [%g3 + %g4] | |
3432 | ||
3433 | setx L2_ESR_CE_BITS | L2_ESR_VEC, %g1, %g2 | |
3434 | setx L2_ESR_BASE, %g1, %g3 ! L2 base | |
3435 | sll %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
3436 | ldx [%g3 + %g4], %g1 ! get ESR[bank] | |
3437 | and %g1, %g2, %g1 ! reset CE bits only | |
3438 | stx %g1, [%g3 + %g4] | |
3439 | ||
3440 | STRAND_STRUCT(%g3) | |
3441 | mov ERR_FLAG_L2DRAM, %g1 ! L2DRAM flag | |
3442 | sll %g1, %g6, %g1 ! << bank# | |
3443 | lduw [%g3 + STRAND_ERR_FLAG], %g2 ! installed flags | |
3444 | bclr %g1, %g2 ! reset L2DRAM[bank] | |
3445 | stw %g2, [%g3 + STRAND_ERR_FLAG] ! .. | |
3446 | !! %g1 = bits | |
3447 | !! %g6 = bank# | |
3448 | BSET_L2_BANK_EEN(%g6, %g5, %g2, %g3) ! L2 Bank EEN[%g6] |= %g5 | |
3449 | ||
3450 | HVRET | |
3451 | SET_SIZE(err_set_l2_bits) | |
3452 | ||
3453 | ||
3454 | /* | |
3455 | * Poll to detect errors that did not cause an interrupt for one | |
3456 | * reason or another. | |
3457 | * Most common cause: L2/DRAM error from prefetch. | |
3458 | * | |
3459 | * Called to get handler callback address (avoid relocation problems) | |
3460 | * | |
3461 | * Entry Data: | |
3462 | * none | |
3463 | * | |
3464 | * Return Data: | |
3465 | * %g2: handler address | |
3466 | * | |
3467 | * Registers modified: | |
3468 | * %g2 | |
3469 | */ | |
3470 | ENTRY_NP(err_poll_daemon) | |
3471 | RETURN_HANDLER_ADDRESS(%g2) ! in %g2 | |
3472 | ||
3473 | /* | |
3474 | * Callback from interrupt: | |
3475 | * | |
3476 | * Entry Data: | |
3477 | * %g1: 0 | |
3478 | * %g2: 0 | |
3479 | * %g3: Interrupt Tick Time | |
3480 | * | |
3481 | * Return Data: | |
3482 | * none | |
3483 | * | |
3484 | * Registers modified: | |
3485 | * %g1-6 | |
3486 | */ | |
3487 | .err_poll_daemon: | |
3488 | /* | |
3489 | * Get strand, CE buffer in %g6-5, they are safe across calls | |
3490 | */ | |
3491 | ||
3492 | STRAND_STRUCT(%g6) | |
3493 | ||
3494 | stx %g3, [%g6 + STRAND_ERR_POLL_ITT] ! save interrupt tick time | |
3495 | stx %g7, [%g6 + STRAND_ERR_POLL_RET] ! save return address | |
3496 | ||
3497 | /* | |
3498 | * Look for Sparc errors: test only, | |
3499 | * the error handler will do the work | |
3500 | */ | |
3501 | .err_poll_sparc: | |
3502 | ldxa [%g0]ASI_SPARC_ERR_STATUS, %g4 ! SPARC afsr | |
3503 | ! | |
3504 | ! Check for any UE: | |
3505 | ! | |
3506 | UE_CHECK(SPARC_UE_MEU_BITS, L2_ESR_UE_BITS, %g4, %g1, %g2, %g3) | |
3507 | bz %xcc, .err_poll_no_ue ! no | |
3508 | nop | |
3509 | HVCALL(ue_poll_entry) ! yes: go process | |
3510 | ||
3511 | ba,a .err_poll_sparc ! and re-check Sparc status | |
3512 | .err_poll_no_ue: | |
3513 | ||
3514 | ! | |
3515 | ! Check for any CE: | |
3516 | ! | |
3517 | CE_CHECK(%g6, %g4, %g1, %g2, %g3) ! cpup, spesr, | |
3518 | bz %xcc, .err_poll_no_ce ! no | |
3519 | nop | |
3520 | HVCALL(ce_poll_entry) ! yes | |
3521 | ||
3522 | ba,a .err_poll_sparc ! go re-check Sparc status | |
3523 | .err_poll_no_ce: | |
3524 | ||
3525 | /* | |
3526 | * reinstall poll handler | |
3527 | */ | |
3528 | STRAND2CONFIG_STRUCT(%g6, %g1) ! ->config | |
3529 | ldx [%g1 + CONFIG_CE_POLL_TIME], %g1 ! g1 = time interval | |
3530 | brz %g1, 9f ! disabled: branch | |
3531 | nop | |
3532 | ldx [%g6 + STRAND_ERR_POLL_ITT], %g2 ! this interrupt tick time | |
3533 | add %g1, %g2, %g1 ! abs time for next poll | |
3534 | ||
3535 | HVCALL(err_poll_daemon) ! g2 = handler address | |
3536 | clr %g3 ! g3 = arg 0 : n/a | |
3537 | clr %g4 ! g4 = arg 1 : n/a | |
3538 | HVCALL(cyclic_add_abs) /* ( abs_tick, address, arg0, arg1 ) */ | |
3539 | 9: | |
3540 | ||
3541 | STRAND_STRUCT(%g6) | |
3542 | ldx [%g6 + STRAND_ERR_POLL_RET], %g7 ! restore return address | |
3543 | HVRET | |
3544 | SET_SIZE(err_poll_daemon) | |
3545 | ||
3546 | /* | |
3547 | * Function to start error polling daemon: | |
3548 | * | |
3549 | * Entry Data: | |
3550 | * none | |
3551 | * | |
3552 | * Return Data: | |
3553 | * %g1: status | |
3554 | * 0 - success (started) | |
3555 | * 1 - failed (already running) | |
3556 | * 2 - failed to start | |
3557 | * | |
3558 | * Registers modified: | |
3559 | * %g1-6 | |
3560 | */ | |
3561 | ENTRY_NP(err_poll_daemon_start) | |
3562 | STRAND_STRUCT(%g6) | |
3563 | ||
3564 | stx %g7, [%g6 + STRAND_ERR_POLL_RET] ! save return address | |
3565 | ||
3566 | lduw [%g6 + STRAND_ERR_FLAG], %g2 | |
3567 | btst ERR_FLAG_POLLD, %g2 ! handler flags | |
3568 | bnz,a %xcc, 9f ! poll deamon installed? | |
3569 | mov 1, %g1 ! yes: return "running" | |
3570 | bset ERR_FLAG_POLLD, %g2 ! set it | |
3571 | stw %g2, [%g6 + STRAND_ERR_FLAG] ! store | |
3572 | ||
3573 | /* | |
3574 | * Install the callback handler: just start at now + ce_poll_time | |
3575 | */ | |
3576 | STRAND2CONFIG_STRUCT(%g6, %g1) ! ->config | |
3577 | ldx [%g1 + CONFIG_CE_POLL_TIME], %g1 ! g1 = cycle time in ticks | |
3578 | HVCALL(err_poll_daemon) ! g2 = handler address | |
3579 | clr %g3 ! g3 = arg 0 : error bits | |
3580 | clr %g4 ! g3 = arg 1 : | |
3581 | HVCALL(cyclic_add_rel) /* ( del_tick, address, arg0, arg1 ) */ | |
3582 | ||
3583 | STRAND_STRUCT(%g6) | |
3584 | ldx [%g6 + STRAND_ERR_POLL_RET], %g7 ! restore return address | |
3585 | clr %g1 ! status = success | |
3586 | 9: | |
3587 | HVRET ! %g1 = status | |
3588 | SET_SIZE(err_poll_daemon_start) | |
3589 | ||
3590 | ||
3591 | #if EVBSC_L2_AFSR_INCR == 8 | |
3592 | #define EVBSC_L2_AFSR_SHIFT 3 | |
3593 | #else | |
3594 | #error "EVBSC_L2_AFSR_INCR is not 8" | |
3595 | #endif | |
3596 | #if EVBSC_L2_AFAR_INCR == 8 | |
3597 | #define EVBSC_L2_AFAR_SHIFT 3 | |
3598 | #else | |
3599 | #error "EVBSC_L2_AFAR_INCR is not 8" | |
3600 | #endif | |
3601 | /* | |
3602 | * This function determines if an error is transient, sticky or | |
3603 | * permanent. We only check disposition on Memory CE's. Hence, | |
3604 | * we only work the L2 error registers. | |
3605 | * The algorithm to classify the error is as follows: | |
3606 | * 1) Displacement flush the E$ line corresponding to %addr. | |
3607 | * The first ldxa guarantees that the %addr is no longer in | |
3608 | * M, O, or E (goes to I or S (if instruction fetch also | |
3609 | * happens). | |
3610 | * 2) "Write" the data using a ldx %addrm %scr CAS %addr,%scr,%scr. | |
3611 | * The casxa guarantees a transition from I to M or S to M. | |
3612 | * There are two possibilities that the sequence does not act | |
3613 | * as intended: | |
3614 | * - the line is displaced between the ld and the cas: | |
3615 | * we still have the correct value in %scr and the cas will | |
3616 | * reload the line - this is OK since the ld was to get the | |
3617 | * value, no to get the line in the cache. | |
3618 | * - the line is written between the ld and the cas: | |
3619 | * the intent to modify the line has effectively succeeded | |
3620 | * 3) Displacement flush the E$ line corresponding to %addr. | |
3621 | * The second ldxa pushes the M line out of the ecache, | |
3622 | * into the writeback buffers, on the way to memory. | |
3623 | * 4) The "membar #Sync" pushes the cache line out of | |
3624 | * the writeback buffers onto the bus, on the way to | |
3625 | * dram finally. | |
3626 | * %g1 - bank number | |
3627 | * | |
3628 | * XXX - Need to handle race with HW scrubber | |
3629 | */ | |
3630 | ENTRY_NP(err_determine_disposition) | |
3631 | CPU_PUSH(%g7, %g4, %g5, %g6) ! save return address | |
3632 | ||
3633 | ! Read and save the current enable | |
3634 | mov %g1, %g6 ! bank # | |
3635 | GET_L2_BANK_EEN(%g6, %g5, %g4) | |
3636 | CPU_PUSH(%g5, %g3, %g4, %g2) ! save for later | |
3637 | ||
3638 | ! disable CEEN | |
3639 | BCLR_L2_BANK_EEN(%g6, CEEN, %g4, %g3) | |
3640 | ||
3641 | ! get err address into %g1 | |
3642 | STRAND_STRUCT(%g1) | |
3643 | add %g1, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g1 | |
3644 | sllx %g6, EVBSC_L2_AFAR_SHIFT, %g4 | |
3645 | add %g4, EVBSC_L2_AFAR, %g2 | |
3646 | ldx [%g1 + %g2], %g1 | |
3647 | ! Mask AFAR to get only valid bits | |
3648 | and %g1, ~L2_EAR_DRAM_MASK, %g1 | |
3649 | ||
3650 | ! l2_flush_line garbles %g6. Save %g6 which | |
3651 | ! contains the BNUM | |
3652 | CPU_PUSH(%g6, %g3, %g4, %g2) ! save for later | |
3653 | ||
3654 | /* | |
3655 | * displace and cause a write back | |
3656 | * Niagara works differently than previous generations. | |
3657 | * On previous generations, a cas will mark the line dirty, | |
3658 | * regardless of the success of the compare. | |
3659 | * In Niagara, the line only gets mark dirty if the swap occurs. | |
3660 | * Hence, we need to first load the value and store it back via the cas | |
3661 | */ | |
3662 | HVCALL(l2_flush_line) | |
3663 | ldx [%g1], %g6 | |
3664 | casx [%g1], %g6, %g6 | |
3665 | HVCALL(l2_flush_line) | |
3666 | ||
3667 | ! push cache line out of the write back buffers | |
3668 | membar #Sync | |
3669 | ||
3670 | CPU_POP(%g6, %g2, %g3, %g4) | |
3671 | ||
3672 | /* | |
3673 | * Read the errs registers again and compare them with our saved | |
3674 | * version. If they are the same, then error is persistent | |
3675 | */ | |
3676 | ||
3677 | ! read err regs | |
3678 | setx L2_ESR_DRAM_CE_BITS, %g3, %g2 | |
3679 | setx L2_ESR_BASE, %g3, %g5 ! L2 base | |
3680 | sll %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
3681 | ldx [%g5 + %g4], %g5 ! get ESR[bank] | |
3682 | and %g5, %g2, %g5 ! compare only CEs | |
3683 | ||
3684 | ! get our copy | |
3685 | STRAND_STRUCT(%g3) | |
3686 | add %g3, STRAND_CE_RPT + STRAND_VBSC_ERPT, %g3 | |
3687 | sllx %g6, EVBSC_L2_AFSR_SHIFT, %g4 | |
3688 | add %g4, EVBSC_L2_AFSR, %g4 | |
3689 | ldx [%g3 + %g4], %g4 ! orig AFSR | |
3690 | and %g4, %g2, %g4 ! only CEs | |
3691 | ||
3692 | ! clear the disposition to have none | |
3693 | mov CE_XDIAG_NONE, %g2 | |
3694 | stx %g2, [%g3 + EVBSC_DIAG_BUF + DRAM_DISPOSITION] | |
3695 | ||
3696 | ! compare with stored | |
3697 | cmp %g4, %g5 | |
3698 | bne,pt %xcc, 2f | |
3699 | nop | |
3700 | ||
3701 | ! now check afar and see if same. | |
3702 | ! %g1 still contains the stored afar | |
3703 | ||
3704 | setx L2_EAR_BASE, %g4, %g5 ! L2 base | |
3705 | sll %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
3706 | ldx [%g5 + %g4], %g4 | |
3707 | ! mask only valid bits | |
3708 | and %g4, ~L2_EAR_DRAM_MASK, %g4 | |
3709 | ||
3710 | cmp %g1, %g4 | |
3711 | bne,pt %xcc, 2f | |
3712 | mov CE_XDIAG_CE1, %g4 | |
3713 | ||
3714 | ! set ce1 if match | |
3715 | stx %g4, [%g3 + EVBSC_DIAG_BUF + DRAM_DISPOSITION] | |
3716 | ||
3717 | ! clear the error reg | |
3718 | sllx %g6, EVBSC_L2_AFSR_SHIFT, %g4 | |
3719 | add %g4, EVBSC_L2_AFSR, %g4 | |
3720 | ldx [%g3 + %g4], %g4 ! orig AFSR | |
3721 | ||
3722 | setx L2_ESR_BASE, %g2, %g5 ! L2 base | |
3723 | sll %g6, L2_BANK_SHIFT, %g2 ! + bank offset | |
3724 | stx %g4, [%g5 + %g2] ! clear ESR[bank] | |
3725 | ||
3726 | /* | |
3727 | * Read data again. data should now come from memory. We check | |
3728 | * for errors. If the saved version and new errs registers are the | |
3729 | * same then it is a stuck bit | |
3730 | * %g1 still contains our stored afar | |
3731 | */ | |
3732 | 2: | |
3733 | ldx [%g1], %g2 | |
3734 | ||
3735 | ! read regs | |
3736 | setx L2_ESR_DRAM_CE_BITS, %g5, %g2 | |
3737 | setx L2_ESR_BASE, %g4, %g5 ! L2 base | |
3738 | sll %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
3739 | ldx [%g5 + %g4], %g5 ! get ESR[bank] | |
3740 | and %g5, %g2, %g5 ! compare only CEs | |
3741 | ||
3742 | ! stored value | |
3743 | sllx %g6, EVBSC_L2_AFSR_SHIFT, %g4 | |
3744 | add %g4, EVBSC_L2_AFSR, %g4 | |
3745 | ldx [%g3 + %g4], %g4 ! orig AFSR | |
3746 | and %g4, %g2, %g4 ! only CEs | |
3747 | ||
3748 | ! compare with stored | |
3749 | cmp %g4, %g5 | |
3750 | bne,pt %xcc, 1f | |
3751 | nop | |
3752 | ||
3753 | ! now check afar and see if same. | |
3754 | ! %g1 still contains the stored afar | |
3755 | ||
3756 | setx L2_EAR_BASE, %g4, %g5 ! L2 base | |
3757 | sll %g6, L2_BANK_SHIFT, %g4 ! + bank offset | |
3758 | ldx [%g5 + %g4], %g4 | |
3759 | ! mask only valid bits | |
3760 | and %g4, ~L2_EAR_DRAM_MASK, %g4 | |
3761 | ||
3762 | cmp %g1, %g4 | |
3763 | bne,pt %xcc, 1f | |
3764 | mov CE_XDIAG_CE2, %g5 | |
3765 | ||
3766 | ! set ce2 if match | |
3767 | ldx [%g3 + EVBSC_DIAG_BUF + DRAM_DISPOSITION], %g2 | |
3768 | or %g5, %g2, %g2 | |
3769 | stx %g2, [%g3 + EVBSC_DIAG_BUF + DRAM_DISPOSITION] | |
3770 | ||
3771 | 1: | |
3772 | ! restore orig ce | |
3773 | CPU_POP(%g5, %g2, %g3, %g4) | |
3774 | SET_L2_EEN_BASE(%g2) | |
3775 | sllx %g6, L2_BANK_SHIFT, %g3 ! bank offset | |
3776 | add %g2, %g3, %g2 ! bank address | |
3777 | stx %g5, [%g2] ! restore value | |
3778 | ||
3779 | CPU_POP(%g7, %g1, %g2, %g3) | |
3780 | HVRET | |
3781 | ||
3782 | SET_SIZE(err_determine_disposition) | |
3783 | ||
3784 | ||
3785 | /* | |
3786 | * Handle strand in error | |
3787 | * All other strands are idle | |
3788 | * This strand: | |
3789 | * - search for another "good" strand | |
3790 | * - flag as halted (bit mask) | |
3791 | * - Remove cyclic (Error Daemon) | |
3792 | * - handoff interrupt steering | |
3793 | * - Migrate all intrs | |
3794 | * - notify good strand to finish rest of work | |
3795 | * - put myself into idle | |
3796 | * Selected Good strand: | |
3797 | * - send resumable error to guest | |
3798 | * %g6 should not be clobbered | |
3799 | */ | |
3800 | ||
3801 | ENTRY_NP(strand_in_error) | |
3802 | ||
3803 | ! Remove this cpu from the active bitmask and add it to halted | |
3804 | STRAND_STRUCT(%g5) | |
3805 | ldub [%g5 + STRAND_ID], %g5 | |
3806 | mov 1, %g4 | |
3807 | sllx %g4, %g5, %g4 | |
3808 | ||
3809 | !! %g5 - strand id | |
3810 | ROOT_STRUCT(%g2) ! config ptr | |
3811 | ||
3812 | ! clear this strand from the active list | |
3813 | ldx [%g2 + CONFIG_STACTIVE], %g3 | |
3814 | bclr %g4, %g3 | |
3815 | stx %g3, [%g2 + CONFIG_STACTIVE] | |
3816 | ||
3817 | ! set this cpu in the halted list | |
3818 | ldx [%g2 + CONFIG_STHALT], %g3 | |
3819 | bset %g4, %g3 | |
3820 | stx %g3, [%g2 + CONFIG_STHALT] | |
3821 | ||
3822 | ! find another idle strand for re-targetting | |
3823 | ldx [%g2 + CONFIG_STIDLE], %g3 | |
3824 | mov 0, %g6 | |
3825 | .find_strand: | |
3826 | cmp %g5, %g6 | |
3827 | be,pn %xcc, .next_strand | |
3828 | mov 1, %g4 | |
3829 | sllx %g4, %g6, %g4 | |
3830 | andcc %g3, %g4, %g0 | |
3831 | bnz,a %xcc, .found_a_strand | |
3832 | nop | |
3833 | ||
3834 | .next_strand: | |
3835 | inc %g6 | |
3836 | cmp %g6, NSTRANDS | |
3837 | bne,pn %xcc, .find_strand | |
3838 | nop | |
3839 | ||
3840 | /* | |
3841 | * No usable active strands are left in the | |
3842 | * system, force host exit | |
3843 | */ | |
3844 | #ifdef CONFIG_VBSC_SVC | |
3845 | ba,a vbsc_guest_exit | |
3846 | #else | |
3847 | LEGION_EXIT(%o0) | |
3848 | #endif | |
3849 | ||
3850 | .found_a_strand: | |
3851 | /* | |
3852 | * handoff L2 Steering CPU | |
3853 | * If we are the steering cpu, migrate it to our chosen one | |
3854 | */ | |
3855 | ||
3856 | !! %g5 - this strand ID | |
3857 | !! %g6 - target strand ID | |
3858 | setx L2_CONTROL_REG, %g3, %g4 | |
3859 | ldx [%g4], %g2 ! current setting | |
3860 | srlx %g2, L2_ERRORSTEER_SHIFT, %g3 | |
3861 | and %g3, (NSTRANDS - 1), %g3 | |
3862 | cmp %g3, %g5 ! is this steering strand ? | |
3863 | bnz,pt %xcc, 1f | |
3864 | nop | |
3865 | ||
3866 | ! It is the L2 Steering strand. Migrate responsibility to tgt strand | |
3867 | sllx %g3, L2_ERRORSTEER_SHIFT, %g3 | |
3868 | andn %g3, %g2, %g2 ! remove this strand | |
3869 | sllx %g6, L2_ERRORSTEER_SHIFT, %g3 | |
3870 | or %g2, %g3, %g2 | |
3871 | stx %g2, [%g4] | |
3872 | 1: | |
3873 | mov %g5, %g1 | |
3874 | mov %g6, %g2 | |
3875 | ||
3876 | !! %g1 - this strand ID | |
3877 | !! %g2 - target strand ID | |
3878 | #ifdef CONFIG_FPGA | |
3879 | /* | |
3880 | * Migrate SSI intrs | |
3881 | */ | |
3882 | STRAND_PUSH(%g1, %g3, %g4) | |
3883 | STRAND_PUSH(%g2, %g3, %g4) | |
3884 | HVCALL(ssi_intr_redistribution) | |
3885 | STRAND_POP(%g2, %g3) | |
3886 | STRAND_POP(%g1, %g3) | |
3887 | #endif | |
3888 | ||
3889 | #if 0 /* XXX */ | |
3890 | /* | |
3891 | * XXX err_poll_daemon (collapse into heartbeat?) | |
3892 | */ | |
3893 | #endif | |
3894 | ||
3895 | /* | |
3896 | * Disable heartbeat interrupts if they're on this cpu. | |
3897 | * cpu_in_error_finish will invoke heartbeat_enable on the | |
3898 | * remote cpu if the heartbeat was disabled. | |
3899 | */ | |
3900 | STRAND_PUSH(%g1, %g3, %g4) | |
3901 | STRAND_PUSH(%g2, %g3, %g4) | |
3902 | HVCALL(heartbeat_disable) | |
3903 | STRAND_POP(%g2, %g3) | |
3904 | STRAND_POP(%g1, %g3) | |
3905 | ||
3906 | #ifdef CONFIG_FIRE | |
3907 | /* | |
3908 | * if this guest owns a fire bus, redirect | |
3909 | * fire interrupts | |
3910 | */ | |
3911 | GUEST_STRUCT(%g3) | |
3912 | ROOT_STRUCT(%g4) | |
3913 | ldx [%g4 + CONFIG_PCIE_BUSSES], %g4 | |
3914 | ! check leaf A | |
3915 | ldx [%g4 + PCIE_DEVICE_GUESTP], %g5 | |
3916 | cmp %g3, %g5 | |
3917 | be %xcc, 2f | |
3918 | nop | |
3919 | ! check leaf B | |
3920 | ldx [%g4 + PCIE_DEVICE_GUESTP + PCIE_DEVICE_SIZE], %g5 | |
3921 | cmp %g3, %g5 | |
3922 | bne %xcc, 3f | |
3923 | nop | |
3924 | 2: | |
3925 | /* | |
3926 | * Migrate fire intrs | |
3927 | */ | |
3928 | STRAND_PUSH(%g1, %g3, %g4) | |
3929 | STRAND_PUSH(%g2, %g3, %g4) | |
3930 | HVCALL(fire_intr_redistribution) | |
3931 | STRAND_POP(%g2, %g3) | |
3932 | STRAND_POP(%g1, %g3) | |
3933 | /* | |
3934 | * Migrate fire err intrs | |
3935 | */ | |
3936 | STRAND_PUSH(%g1, %g3, %g4) | |
3937 | STRAND_PUSH(%g2, %g3, %g4) | |
3938 | HVCALL(fire_err_intr_redistribution) | |
3939 | STRAND_POP(%g2, %g3) | |
3940 | STRAND_POP(%g1, %g3) | |
3941 | 3: | |
3942 | #endif | |
3943 | /* | |
3944 | * Migrate vdev intrs | |
3945 | */ | |
3946 | STRAND_PUSH(%g1, %g3, %g4) | |
3947 | STRAND_PUSH(%g2, %g3, %g4) | |
3948 | HVCALL(vdev_intr_redistribution) | |
3949 | STRAND_POP(%g2, %g3) | |
3950 | STRAND_POP(%g1, %g3) | |
3951 | ||
3952 | /* | |
3953 | * Now pick another VCPU in this guest to target the erpt | |
3954 | * Ensure that the VCPU is not bound to the strand in error | |
3955 | */ | |
3956 | VCPU_STRUCT(%g1) | |
3957 | GUEST_STRUCT(%g2) | |
3958 | add %g2, GUEST_VCPUS, %g2 | |
3959 | mov 0, %g3 | |
3960 | ||
3961 | !! %g1 - this vcpu struct | |
3962 | !! %g2 - array of vcpus in guest | |
3963 | !! %g3 - vcpu array idx | |
3964 | .find_cpu_loop: | |
3965 | ldx [%g2], %g4 ! vcpu struct | |
3966 | brz,pn %g4, .find_cpu_continue | |
3967 | nop | |
3968 | ||
3969 | ! ignore this vcpu | |
3970 | cmp %g4, %g1 | |
3971 | be,pn %xcc, .find_cpu_continue | |
3972 | nop | |
3973 | ||
3974 | ! check whether this CPU is running guest code ? | |
3975 | ldx [%g4 + CPU_STATUS], %g6 | |
3976 | cmp %g6, CPU_STATE_RUNNING | |
3977 | bne,pt %xcc, .find_cpu_continue | |
3978 | nop | |
3979 | ||
3980 | ! check the error queues.. if not set, not a good candidate | |
3981 | ldx [%g4 + CPU_ERRQR_BASE], %g6 | |
3982 | brz,pt %g6, .find_cpu_continue | |
3983 | nop | |
3984 | ||
3985 | /* | |
3986 | * find the strand this vcpu is ON, make sure it is idle | |
3987 | * NOTE: currently this check is not necessary, more | |
3988 | * likely when we have sub-strand scheduling | |
3989 | */ | |
3990 | !! %g1 - this vcpu struct | |
3991 | !! %g2 - curr vcpu in guest vcpu array | |
3992 | !! %g3 - vcpu array idx | |
3993 | !! %g4 - target vcpus struct | |
3994 | STRAND_STRUCT(%g5) ! this strand | |
3995 | ldx [%g4 + CPU_STRAND], %g6 ! vcpu->strand | |
3996 | cmp %g5, %g6 | |
3997 | be,pn %xcc, .find_cpu_continue | |
3998 | nop | |
3999 | ||
4000 | ! check if the target strand is IDLE | |
4001 | ldub [%g6 + STRAND_ID], %g6 ! vcpu->strand->id | |
4002 | mov 1, %g5 | |
4003 | sllx %g5, %g6, %g6 | |
4004 | VCPU2ROOT_STRUCT(%g1, %g5) | |
4005 | ldx [%g5 + CONFIG_STIDLE], %g5 | |
4006 | btst %g5, %g6 | |
4007 | bnz,pt %xcc, .found_a_cpu | |
4008 | nop | |
4009 | ||
4010 | .find_cpu_continue: | |
4011 | add %g2, GUEST_VCPUS_INCR, %g2 | |
4012 | inc %g3 | |
4013 | cmp %g3, NVCPUS | |
4014 | bne,pn %xcc, .find_cpu_loop | |
4015 | nop | |
4016 | ||
4017 | ! If we got here, we didn't find a good tgt cpu | |
4018 | ! do not send an erpt, exit the guest | |
4019 | ||
4020 | HVCALL(guest_exit) | |
4021 | ||
4022 | ba,a .skip_sending_erpt | |
4023 | ||
4024 | .found_a_cpu: | |
4025 | !! %g4 - target vcpu struct | |
4026 | /* | |
4027 | * This cpu has most of the information to send to the Guest. | |
4028 | * We copy from this cpu err rpt to the tgt's err rpt | |
4029 | */ | |
4030 | STRAND_STRUCT(%g1) ! this strand | |
4031 | STRAND2ERPT_STRUCT(STRAND_UE_RPT, %g1, %g1) | |
4032 | ||
4033 | ! get tgt strand ce erpt | |
4034 | ldx [%g4 + CPU_STRAND], %g2 ! tgt_vcpu->strand | |
4035 | STRAND2ERPT_STRUCT(STRAND_CE_RPT, %g2, %g3) | |
4036 | ||
4037 | ! copy info to tgt cpu ce err buf | |
4038 | ldx [%g1 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL], %g4 ! ehdl | |
4039 | stx %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL] | |
4040 | ldx [%g1 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK], %g4 ! stick | |
4041 | stx %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK] | |
4042 | ld [%g1 + STRAND_SUN4V_ERPT + ESUN4V_EDESC], %g4 ! edesc | |
4043 | st %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_EDESC] | |
4044 | ld [%g1 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g4 ! attr | |
4045 | st %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_ATTR] | |
4046 | ldx [%g1 + STRAND_SUN4V_ERPT + ESUN4V_ADDR], %g4 ! ra | |
4047 | stx %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_ADDR] | |
4048 | ld [%g1 + STRAND_SUN4V_ERPT + ESUN4V_SZ], %g4 ! sz | |
4049 | st %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
4050 | lduh [%g1 + STRAND_SUN4V_ERPT + ESUN4V_G_CPUID], %g4 ! cpuid | |
4051 | stuh %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_G_CPUID] | |
4052 | lduh [%g1 + STRAND_SUN4V_ERPT + ESUN4V_G_SECS], %g4 | |
4053 | stuh %g4, [%g3 + STRAND_SUN4V_ERPT + ESUN4V_G_SECS] | |
4054 | ||
4055 | /* | |
4056 | * Send a xcall to the target strand so it can finish the work | |
4057 | */ | |
4058 | ldub [%g2 + STRAND_ID], %g6 ! tgt strand id | |
4059 | sllx %g6, INT_VEC_DIS_VCID_SHIFT, %g5 | |
4060 | or %g5, VECINTR_CPUINERR, %g5 | |
4061 | stxa %g5, [%g0]ASI_INTR_UDB_W | |
4062 | ||
4063 | .skip_sending_erpt: | |
4064 | STRAND_STRUCT(%g6) | |
4065 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
4066 | ||
4067 | ! remove self from idle list | |
4068 | STRAND_STRUCT(%g1) | |
4069 | ldub [%g1 + STRAND_ID], %g6 /* phys id */ | |
4070 | mov 1, %g1 | |
4071 | sllx %g1, %g6, %g1 | |
4072 | ROOT_STRUCT(%g6) | |
4073 | ldx [%g6 + CONFIG_STIDLE], %g5 | |
4074 | bclr %g1, %g5 | |
4075 | stx %g5, [%g6 + CONFIG_STIDLE] | |
4076 | ||
4077 | ! idle myself | |
4078 | STRAND_STRUCT(%g1) | |
4079 | ldub [%g1 + STRAND_ID], %g6 /* phys id */ | |
4080 | INT_VEC_DSPCH_ONE(INT_VEC_DIS_TYPE_IDLE, %g6, %g3, %g4) | |
4081 | ||
4082 | /* | |
4083 | * Paranoia!! If we get here someone else resumed this strand | |
4084 | * by mistake | |
4085 | * hvabort to catch the mistake | |
4086 | */ | |
4087 | ba hvabort | |
4088 | rd %pc, %g1 | |
4089 | ||
4090 | SET_SIZE(strand_in_error) | |
4091 | ||
4092 | ENTRY(ssi_mondo) | |
4093 | ||
4094 | /* | |
4095 | * Check for JBUS error | |
4096 | */ | |
4097 | setx JBI_ERR_LOG, %g1, %g2 | |
4098 | ldx [%g2], %g2 | |
4099 | brnz,pn %g2, ue_jbus_err | |
4100 | nop | |
4101 | ||
4102 | /* | |
4103 | * Clear the INT_CTL.MASK bit for the SSI | |
4104 | */ | |
4105 | setx IOBBASE, %g3, %g2 | |
4106 | stx %g0, [%g2 + INT_CTL + INT_CTL_DEV_OFF(IOBDEV_SSIERR)] | |
4107 | ||
4108 | retry | |
4109 | ||
4110 | SET_SIZE(ssi_mondo) | |
4111 | ||
4112 | /* | |
4113 | * re-route an error report (cont'd) | |
4114 | * 3. select one of the active CPUs for that guest | |
4115 | * 4. Copy the data from the error erport into that | |
4116 | * CPUs cpu struct | |
4117 | * 5. Send a VECINTR_ERROR_XCALL to that CPU | |
4118 | * 6: RETRY | |
4119 | * | |
4120 | * %g2 target guest | |
4121 | * %g4 PA | |
4122 | */ | |
4123 | ||
4124 | /* FIXME: re-whack this for vcpu/strand split */ | |
4125 | ||
4126 | ENTRY_NP(cpu_reroute_error) | |
4127 | ||
4128 | /* | |
4129 | * find first live cpu in guest->vcpus | |
4130 | * Then deliver the error to that vcpu, and interrupt | |
4131 | * the strand it is running on to make that happen. | |
4132 | */ | |
4133 | add %g2, GUEST_VCPUS, %g2 | |
4134 | mov 0, %g3 | |
4135 | 1: | |
4136 | cmp %g3, NVCPUS | |
4137 | be,pn %xcc, cpu_reroute_error_exit | |
4138 | nop | |
4139 | ||
4140 | mulx %g3, GUEST_VCPUS_INCR, %g5 | |
4141 | ldx [%g2 + %g5], %g1 | |
4142 | brz,a,pn %g1, 1b | |
4143 | inc %g3 | |
4144 | ! check whether this CPU is running guest code ? | |
4145 | ldx [%g1 + CPU_STATUS], %g5 | |
4146 | cmp %g5, CPU_STATE_RUNNING | |
4147 | bne,pt %xcc, 1b | |
4148 | inc %g3 | |
4149 | ||
4150 | ! %g3 target vcpu id | |
4151 | ! %g1 &vcpus[target] | |
4152 | ||
4153 | ldx [%g1 + CPU_STRAND], %g1 | |
4154 | ||
4155 | /* | |
4156 | * It is possible that the CPUs rerouted data is already in use. | |
4157 | * We use the rerouted_addr field as a spinlock. The target CPU | |
4158 | * will set this to 0 after reading the error data allowing us | |
4159 | * to re-use the rerouting fields. | |
4160 | * See cpu_err_rerouted() below. | |
4161 | * | |
4162 | * %g1 &strands[target] | |
4163 | * %g3 target cpuid | |
4164 | * %g4 PA | |
4165 | */ | |
4166 | set STRAND_REROUTED_ADDR, %g2 | |
4167 | add %g1, %g2, %g6 | |
4168 | 1: casx [%g6], %g0, %g4 | |
4169 | brnz,pn %g4, 1b | |
4170 | nop | |
4171 | ||
4172 | ||
4173 | ! get the data out of the current STRAND's ce_rpt buf and store | |
4174 | ! in the target STRAND struct | |
4175 | STRAND_ERPT_STRUCT(STRAND_CE_RPT, %g6, %g5) ! g6->strand, g5->strand.ce_rpt | |
4176 | ldx [%g5 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL], %g6 | |
4177 | set STRAND_REROUTED_EHDL, %g4 | |
4178 | stx %g6, [%g1 + %g4] | |
4179 | lduw [%g5 + STRAND_SUN4V_ERPT + ESUN4V_ATTR], %g6 | |
4180 | set STRAND_REROUTED_ATTR, %g4 | |
4181 | stx %g6, [%g1 + %g4] | |
4182 | ldx [%g5 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK], %g6 | |
4183 | ! STICK is probably not necssary. I doubt if FMA checks | |
4184 | ! both EHDL/STICK when looking for duplicate reports, | |
4185 | ! but it doesn't kill us to do it. | |
4186 | set STRAND_REROUTED_STICK, %g4 | |
4187 | stx %g6, [%g1 + %g4] | |
4188 | ||
4189 | ! send an x-call to the target CPU | |
4190 | ldub [%g1 + STRAND_ID], %g3 | |
4191 | sllx %g3, IVDR_THREAD, %g3 | |
4192 | mov VECINTR_ERROR_XCALL, %g5 | |
4193 | or %g3, %g5, %g3 | |
4194 | stxa %g3, [%g0]ASI_INTR_UDB_W | |
4195 | cpu_reroute_error_exit: | |
4196 | ! error is re-routed, get out of here | |
4197 | STRAND_STRUCT(%g6) | |
4198 | SPINLOCK_RESUME_ALL_STRAND(%g6, %g1, %g2, %g3, %g4) | |
4199 | ||
4200 | ldx [%g6 + STRAND_ERR_RET], %g7 ! get return address | |
4201 | brnz,a %g7, .ue_return ! valid: clear it & return | |
4202 | stx %g0, [%g6 + STRAND_ERR_RET] ! .. | |
4203 | ||
4204 | retry | |
4205 | ||
4206 | SET_SIZE(cpu_reroute_error) | |
4207 | ||
4208 | /* | |
4209 | * An error has been re-routed to this STRAND. | |
4210 | * The EHDL/ADDR/STICK/ATTR have been stored in the STRAND struct | |
4211 | * by the STRAND that originally detected the error. | |
4212 | * | |
4213 | * Note: STICK may not be strictly necessary | |
4214 | */ | |
4215 | ENTRY_NP(cpu_err_rerouted) | |
4216 | ||
4217 | STRAND_ERPT_STRUCT(STRAND_CE_RPT, %g6, %g5) ! g6->strand, g5->strand.ce_rpt | |
4218 | #ifdef DEBUG_ERROR_REROUTING | |
4219 | PRINT("Error Re-routed to CPU strand "); | |
4220 | ldub [%g6 + STRAND_ID], %g4 | |
4221 | PRINTX(%g4) | |
4222 | PRINT("\r\n"); | |
4223 | #endif | |
4224 | ||
4225 | set STRAND_REROUTED_EHDL, %g4 | |
4226 | ldx [%g6 + %g4], %g4 | |
4227 | stx %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_G_EHDL] | |
4228 | ||
4229 | set STRAND_REROUTED_STICK, %g4 | |
4230 | ldx [%g6 + %g4], %g4 | |
4231 | stx %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_G_STICK] | |
4232 | ||
4233 | set STRAND_REROUTED_ATTR, %g4 | |
4234 | ldx [%g6 + %g4], %g4 | |
4235 | stw %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_ATTR] | |
4236 | ||
4237 | ! keep ADDR after EHDL/STICK/ATTR to avoid race | |
4238 | set STRAND_REROUTED_ADDR, %g4 | |
4239 | ldx [%g6 + %g4], %g1 | |
4240 | ! Clear the strand->rerouted-addr field now to let other | |
4241 | ! errors in. | |
4242 | stx %g0, [%g6 + %g4] | |
4243 | ! Translate the PA to a guest RA | |
4244 | VCPU_STRUCT(%g6) | |
4245 | CPU_ERR_PA_TO_RA(%g6, %g1, %g4, %g2, %g3) | |
4246 | stx %g1, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_ADDR] | |
4247 | ||
4248 | ldub [%g6 + CPU_VID], %g4 /* guest cpuid */ | |
4249 | stuh %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_G_CPUID] | |
4250 | ||
4251 | set EDESC_UE_RESUMABLE, %g4 | |
4252 | stw %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_EDESC] | |
4253 | ||
4254 | mov ERPT_MEM_SIZE, %g4 | |
4255 | st %g4, [%g5 + STRAND_SUN4V_ERPT + ESUN4V_SZ] | |
4256 | ||
4257 | /* | |
4258 | * gueue a resumable error report and return | |
4259 | */ | |
4260 | ASMCALL_RQ_ERPT(STRAND_CE_RPT, %g1, %g2, %g3, %g4, %g5, %g6, %g7) | |
4261 | ||
4262 | retry | |
4263 | ||
4264 | SET_SIZE(cpu_err_rerouted) | |
4265 | ||
4266 | ||
4267 | ENTRY_NP(hvabort) | |
4268 | mov %g1, %g6 | |
4269 | HV_PRINT_NOTRAP("ABORT: Failure 0x"); | |
4270 | HV_PRINTX_NOTRAP(%g6) | |
4271 | #ifdef CONFIG_VBSC_SVC | |
4272 | HV_PRINT_NOTRAP(", contacting vbsc\r\n"); | |
4273 | ba,pt %xcc, vbsc_hv_abort | |
4274 | mov %g6, %g1 | |
4275 | ||
4276 | #else | |
4277 | HV_PRINT_NOTRAP(", spinning\r\n"); | |
4278 | LEGION_EXIT(1) | |
4279 | 2: ba,a 2b | |
4280 | nop | |
4281 | #endif | |
4282 | SET_SIZE(hvabort) | |
4283 | ||
4284 | ||
4285 | ! intended never to return | |
4286 | ENTRY(c_hvabort) | |
4287 | mov %o7, %g1 | |
4288 | ba hvabort | |
4289 | nop | |
4290 | SET_SIZE(c_hvabort) |