Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: errors_common.s | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | #pragma ident "@(#)errors_common.s 1.5 07/08/01 SMI" | |
50 | ||
51 | #include <sys/asm_linkage.h> | |
52 | #include <sun4v/asi.h> | |
53 | #include <sun4v/traps.h> | |
54 | #include <sun4v/queue.h> | |
55 | #include <hypervisor.h> | |
56 | #include <guest.h> | |
57 | #include <asi.h> | |
58 | #include <mmu.h> | |
59 | #include <hprivregs.h> | |
60 | #include <cache.h> | |
61 | #include <dram.h> | |
62 | #include <ncu.h> | |
63 | ||
64 | #include <cmp.h> | |
65 | #include <abort.h> | |
66 | ||
67 | #include <offsets.h> | |
68 | #include <util.h> | |
69 | #include <legion.h> | |
70 | #include <errs_common.h> | |
71 | #include <error_defs.h> | |
72 | #include <error_regs.h> | |
73 | #include <error_asm.h> | |
74 | ||
75 | /* | |
76 | * Just a short note to document the error handling | |
77 | * implementation. Each error type has an error_table entry which | |
78 | * mandates how that error is corrected, reported, which guest | |
79 | * Queue is used (if any), etc. The trap handler for the error | |
80 | * uses the trap-specific array of error entries to identify the | |
81 | * individual error which has occurred, passing a table entry to the | |
82 | * generic error handler for processing. The error_handler | |
83 | * looks at the error_flags/error_functions encoded in the error_table | |
84 | * entry to determine how the error is to be dealt with, | |
85 | * yanking the error-specific functionality from the table and executing it. | |
86 | * | |
87 | * The processing is as follows :- | |
88 | * 1) Create sun4v guest report | |
89 | * 2) Gather data for SP Diagnosis Engine | |
90 | * 3) Perform any error correction | |
91 | * 4) Set up storm handler if required | |
92 | * 5) Send diagnosis report to SP | |
93 | * 6) Queue guest error report | |
94 | * 7) Exit | |
95 | */ | |
96 | ||
97 | ||
98 | /* | |
99 | * This is the common entry point for all errors. The various | |
100 | * trap handlers will have determined the error type, located | |
101 | * the error_table_entry for that error and transferred control | |
102 | * to here. | |
103 | * | |
104 | * %g1 &error_table_entry | |
105 | */ | |
106 | ENTRY(error_handler) | |
107 | ||
108 | /* | |
109 | * First we store the address of the error_table-entry | |
110 | * in cpu->cpu_err_table_entry[TL] | |
111 | */ | |
112 | SET_ERR_TABLE_ENTRY(%g1, %g2, %g3) | |
113 | ||
114 | /* | |
115 | * Check if this error requires an immediate warm reset | |
116 | */ | |
117 | ld [%g1 + ERR_FLAGS], %g4 | |
118 | set ERR_FORCE_SIR, %g3 | |
119 | btst %g3, %g4 | |
120 | bz,pt %xcc, error_handler_check_io_prot | |
121 | nop | |
122 | ||
123 | PRINT("Software Reset required after Fatal Error!\r\n") | |
124 | ||
125 | ! reset the system, an SIR with %o0 = 1 is treated as a fatal reset | |
126 | mov 1, %o0 | |
127 | sir 0 | |
128 | ||
129 | error_handler_check_io_prot: | |
130 | /* | |
131 | * Check if this error occurred with strand error protection | |
132 | * enabled. This is only enabled from inside the error handling | |
133 | * code to facilitate checking for stuck-at errors, false error | |
134 | * conditions, etc. | |
135 | */ | |
136 | STRAND_STRUCT(%g4) | |
137 | lduw [%g4 + STRAND_ERR_FLAG], %g4 | |
138 | set STRAND_ERR_FLAG_PROTECTION, %g5 | |
139 | btst %g4, %g5 | |
140 | bnz,pn %xcc, 1f | |
141 | nop | |
142 | ||
143 | /* | |
144 | * If this error occurred during peek/poke operation with | |
145 | * protection set we just complete the instruction. First | |
146 | * check whether this error type supports I/O protection | |
147 | */ | |
148 | ld [%g1 + ERR_FLAGS], %g4 | |
149 | btst ERR_IO_PROT, %g4 | |
150 | bz,pn %xcc, error_handler_sun4v_reporting ! no I/O protection | |
151 | nop | |
152 | ||
153 | STRAND_STRUCT(%g2) | |
154 | set STRAND_IO_PROT, %g3 | |
155 | ldx [%g2 + %g3], %g3 ! strand.io_prot | |
156 | brz %g3, error_handler_sun4v_reporting ! if zero, no error protection | |
157 | nop | |
158 | ||
159 | /* | |
160 | * Error occurred under i/o error protection | |
161 | * Set the i/o error flag in the strand structure and complete the | |
162 | * instruction | |
163 | */ | |
164 | 1: | |
165 | STRAND_STRUCT(%g2) | |
166 | add %g2, STRAND_IO_ERROR, %g2 | |
167 | mov 1, %g3 | |
168 | stx %g3, [%g2] ! strand.io_error = 1 | |
169 | ||
170 | /* | |
171 | * Do any required error correction | |
172 | */ | |
173 | ldx [%g1 + ERR_CORRECT_FCN], %g3 | |
174 | brz,pn %g3, 1f | |
175 | nop | |
176 | ||
177 | HVJMP(%g3, %g7) | |
178 | ||
179 | HVCALL(clear_dram_l2c_esr_regs) | |
180 | ||
181 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
182 | ld [%g1 + ERR_FLAGS], %g4 | |
183 | set ERR_CLEAR_AMB_ERRORS, %g2 | |
184 | btst %g2, %g4 | |
185 | bz,pn %xcc, 1f | |
186 | nop | |
187 | ||
188 | HVCALL(clear_amb_errors) | |
189 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
190 | ld [%g1 + ERR_FLAGS], %g4 | |
191 | 1: | |
192 | /* | |
193 | * Does the trap handler for this error park the strands ? | |
194 | * If yes, resume them here. | |
195 | */ | |
196 | btst ERR_STRANDS_PARKED, %g4 | |
197 | bz,pn %xcc, 1f | |
198 | nop | |
199 | ||
200 | RESUME_ALL_STRANDS(%g2, %g3, %g5, %g6) | |
201 | 1: | |
202 | /* | |
203 | * check whether we stored the globals and re-used | |
204 | * at MAXPTL | |
205 | */ | |
206 | btst ERR_GL_STORED, %g4 | |
207 | bz,pt %xcc, 1f | |
208 | nop | |
209 | ||
210 | RESTORE_GLOBALS(done) | |
211 | ||
212 | 1: | |
213 | /* | |
214 | * All done ..... get out of here | |
215 | */ | |
216 | ||
217 | done ! complete the instruction | |
218 | ||
219 | error_handler_sun4v_reporting: | |
220 | ||
221 | #ifdef DEBUG | |
222 | PRINT_ERROR_TABLE_ENTRY() | |
223 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
224 | #endif | |
225 | ||
226 | ||
227 | /* | |
228 | * Do any required SUN4V guest error reporting | |
229 | */ | |
230 | ldub [%g1 + ERR_SUN4V_RPRT_TYPE], %g3 | |
231 | cmp %g3, SUN4V_NO_REPORT | |
232 | be,pn %xcc, error_handler_diag_reporting | |
233 | nop | |
234 | ||
235 | HVCALL(error_handler_sun4v_report) | |
236 | ||
237 | /* | |
238 | * Must ensure that we get a sun4v report buffer | |
239 | */ | |
240 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g3) | |
241 | brz %g2, error_handler_sun4v_reporting | |
242 | nop | |
243 | ||
244 | ! Note: %g1 preserved across call | |
245 | ||
246 | /* | |
247 | * Call the guest error_specific sun4v report function | |
248 | * This function should be used for ASI report types to load | |
249 | * the ASI/RA fields of the sun4v guest report | |
250 | * PCI-E reports will fill in the DESC attributes | |
251 | */ | |
252 | ldx [%g1 + ERR_GUEST_REPORT_FCN], %g3 | |
253 | brz,pn %g3, error_handler_diag_reporting | |
254 | nop | |
255 | ||
256 | HVJMP(%g3, %g7) | |
257 | ||
258 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
259 | ||
260 | error_handler_diag_reporting: | |
261 | /* | |
262 | * Do any required SP diagnosis error reporting | |
263 | */ | |
264 | ldx [%g1 + ERR_REPORT_FCN], %g3 | |
265 | brz,pn %g3, error_handler_correction | |
266 | nop | |
267 | ||
268 | /* | |
269 | * First set up the generic report | |
270 | */ | |
271 | HVCALL(error_handler_diag_report) | |
272 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
273 | ||
274 | /* | |
275 | * call the error-specific reporting function | |
276 | */ | |
277 | ldx [%g1 + ERR_REPORT_FCN], %g3 | |
278 | HVJMP(%g3, %g7) | |
279 | ||
280 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
281 | ||
282 | error_handler_correction: | |
283 | /* | |
284 | * Do any required error correction | |
285 | */ | |
286 | ldx [%g1 + ERR_CORRECT_FCN], %g3 | |
287 | brz,pn %g3, error_handler_amb_errors | |
288 | nop | |
289 | ||
290 | HVJMP(%g3, %g7) | |
291 | ||
292 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
293 | ||
294 | error_handler_amb_errors: | |
295 | ld [%g1 + ERR_FLAGS], %g4 | |
296 | set ERR_CLEAR_AMB_ERRORS, %g2 | |
297 | btst %g2, %g4 | |
298 | bz,pn %xcc, error_handler_storm | |
299 | nop | |
300 | ||
301 | HVCALL(clear_amb_errors) | |
302 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
303 | ||
304 | error_handler_storm: | |
305 | /* | |
306 | * If we need to set up any defences against error storms | |
307 | * for this error type, do it now | |
308 | */ | |
309 | ldx [%g1 + ERR_STORM_FCN], %g3 | |
310 | brz,pn %g3, error_handler_epilog | |
311 | nop | |
312 | ||
313 | HVJMP(%g3, %g7) | |
314 | ||
315 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
316 | ||
317 | error_handler_epilog: | |
318 | ||
319 | #ifdef DEBUG | |
320 | /* | |
321 | * dump out Service Error Report (SER) | |
322 | * and diag buf data | |
323 | */ | |
324 | #ifdef DEBUG_LEGION | |
325 | HVCALL(print_diag_ser) | |
326 | HVCALL(print_diag_buf) | |
327 | #endif | |
328 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
329 | ||
330 | /* | |
331 | * The error-specific data now ... | |
332 | */ | |
333 | ldx [%g1 + ERR_PRINT_FCN], %g3 | |
334 | brz,pn %g3, 1f | |
335 | nop | |
336 | ||
337 | HVJMP(%g3, %g7) | |
338 | ||
339 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
340 | 1: | |
341 | #endif | |
342 | /* | |
343 | * Does the trap handler for this error park the strands ? | |
344 | * If yes, resume them here. | |
345 | */ | |
346 | ld [%g1 + ERR_FLAGS], %g2 | |
347 | btst ERR_STRANDS_PARKED, %g2 | |
348 | bz,pn %xcc, 1f | |
349 | nop | |
350 | ||
351 | RESUME_ALL_STRANDS(%g2, %g4, %g5, %g6) | |
352 | 1: | |
353 | /* | |
354 | * I have a vision of using an asynchronous cyclic | |
355 | * to send the diagnostic reports to the SP. This cyclic | |
356 | * will trigger and scan through all the err_diag_rprt's | |
357 | * looking for ERR_RPRT_PENDING entries and send them off. | |
358 | * This will cut down the time spent in the error | |
359 | * handler. | |
360 | * | |
361 | * For the moment, this is just a dream, we will just call | |
362 | * the report transmission directly. | |
363 | */ | |
364 | ldx [%g1 + ERR_REPORT_FCN], %g3 | |
365 | brz,pn %g3, error_handler_resumable | |
366 | nop | |
367 | ||
368 | /* | |
369 | * send the report to the SP | |
370 | * send_diag_erpt() will clear the in_use flag | |
371 | * %g1 err_diag_rprt | |
372 | * %g2 err_diag_rprt.err_diag.in_use | |
373 | * %g3 sizeof(err_diag_rprt) | |
374 | */ | |
375 | GET_ERR_DIAG_BUF(%g1, %g5) | |
376 | add %g1, ERR_DIAG_RPRT_IN_USE, %g2 | |
377 | add %g1, ERR_DIAG_RPRT_REPORT_SIZE, %g3 | |
378 | lduw [%g3], %g3 | |
379 | HVCALL(send_diag_erpt) | |
380 | ||
381 | ! Note: %g1 not preserved across call | |
382 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
383 | ||
384 | error_handler_resumable: | |
385 | ||
386 | /* | |
387 | * Check if this CPU has been marked bad, if so mark the | |
388 | * corresponding strand in error | |
389 | */ | |
390 | VCPU_STRUCT(%g2) | |
391 | ldx [%g2 + CPU_STATUS], %g2 | |
392 | cmp %g2, CPU_STATE_ERROR | |
393 | be,pn %xcc, strand_in_error | |
394 | nop | |
395 | ||
396 | /* | |
397 | * If this is a FATAL error we will abort the HV now. | |
398 | * No report goes to the guest in this case | |
399 | */ | |
400 | ld [%g1 + ERR_FLAGS], %g2 | |
401 | btst ERR_FATAL, %g2 | |
402 | bnz,pn %xcc, fatal_error | |
403 | nop | |
404 | ||
405 | /* | |
406 | * For some L2$ errors, if the line was not dirty | |
407 | * we can continue without terminating the guest | |
408 | */ | |
409 | ld [%g1 + ERR_FLAGS], %g2 | |
410 | btst ERR_CHECK_LINE_STATE, %g2 | |
411 | bz,pn %xcc, 1f | |
412 | nop | |
413 | ||
414 | GET_ERR_DIAG_DATA_BUF(%g4, %g5) | |
415 | ldx [%g4 + ERR_DIAG_L2_LINE_STATE], %g5 | |
416 | cmp %g5, L2_LINE_DIRTY | |
417 | bne %xcc, error_handler_erpt_done | |
418 | nop | |
419 | 1: | |
420 | ||
421 | /* | |
422 | * check if this is a non-resumable error | |
423 | */ | |
424 | ld [%g1 + ERR_FLAGS], %g2 | |
425 | btst ERR_NON_RESUMABLE, %g2 | |
426 | bnz,pn %xcc, error_handler_non_resumable | |
427 | nop | |
428 | ||
429 | /* | |
430 | * No report to guest if the error occurred in the hypervisor | |
431 | */ | |
432 | rdpr %tl, %g2 | |
433 | brz,pn %g2, 1f | |
434 | nop | |
435 | ||
436 | rdhpr %htstate, %g2 | |
437 | btst HTSTATE_HPRIV, %g2 | |
438 | bnz %xcc, error_handler_erpt_done | |
439 | nop | |
440 | ||
441 | 1: | |
442 | ||
443 | /* | |
444 | * Send a resumable error report to the guest if | |
445 | * required. | |
446 | */ | |
447 | ldub [%g1 + ERR_SUN4V_RPRT_TYPE], %g2 | |
448 | cmp %g2, SUN4V_NO_REPORT | |
449 | be,pt %xcc, error_handler_erpt_done | |
450 | nop | |
451 | ||
452 | /* | |
453 | * If the ATTR field in the sun4v report has been set to | |
454 | * zero, we do not want to send this report. This will have | |
455 | * been done in the error handling code which populates the | |
456 | * report when it is determined that the error occurred | |
457 | * on some hyperprivileged register. | |
458 | */ | |
459 | GET_ERR_SUN4V_RPRT_BUF(%g3, %g4) | |
460 | ld [%g3 + ERR_SUN4V_RPRT_ATTR], %g4 ! attr | |
461 | brz,pn %g4, error_handler_erpt_done | |
462 | nop | |
463 | ||
464 | /* | |
465 | * Sun4v guest resumable error interrupt | |
466 | */ | |
467 | #ifdef DEBUG | |
468 | PRINT_NOTRAP("queue resumable erpt\r\n"); | |
469 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
470 | #endif | |
471 | HVCALL(errors_queue_resumable_erpt) | |
472 | GET_ERR_TABLE_ENTRY(%g1, %g2) | |
473 | ||
474 | error_handler_erpt_done: | |
475 | ||
476 | ld [%g1 + ERR_FLAGS], %g2 | |
477 | btst ERR_ISSUE_DONE, %g2 | |
478 | bnz,pn %xcc, error_handler_done | |
479 | .empty | |
480 | ||
481 | /* | |
482 | * fall through to RETRY | |
483 | */ | |
484 | ||
485 | error_handler_retry: | |
486 | ||
487 | /* | |
488 | * check whether we stored the globals and re-used | |
489 | * at MAXPTL | |
490 | */ | |
491 | btst ERR_GL_STORED, %g2 | |
492 | bz,pt %xcc, 1f | |
493 | nop | |
494 | ||
495 | RESTORE_GLOBALS(retry) | |
496 | 1: | |
497 | /* | |
498 | * And back we go | |
499 | */ | |
500 | retry | |
501 | ||
502 | error_handler_done: | |
503 | /* | |
504 | * check whether we stored the globals and re-used | |
505 | * at MAXPTL | |
506 | */ | |
507 | btst ERR_GL_STORED, %g2 | |
508 | bz,pt %xcc, 1f | |
509 | nop | |
510 | ||
511 | RESTORE_GLOBALS(done) | |
512 | ||
513 | 1: | |
514 | /* | |
515 | * All done ..... get out of here | |
516 | */ | |
517 | done | |
518 | ||
519 | error_handler_non_resumable: | |
520 | PRINT_NOTRAP("error_handler_non_resumable\r\n"); | |
521 | ||
522 | /* | |
523 | * Abort if the error occurred in the hypervisor itself. | |
524 | */ | |
525 | rdhpr %htstate, %g2 | |
526 | btst HTSTATE_HPRIV, %g2 | |
527 | bnz %xcc, hpriv_error | |
528 | nop | |
529 | ||
530 | /* | |
531 | * queue a report on the non-resumable error queue | |
532 | * and then jump to the guests non-resumable trap | |
533 | * handler function | |
534 | */ | |
535 | ldub [%g1 + ERR_SUN4V_RPRT_TYPE], %g2 | |
536 | cmp %g2, SUN4V_NO_REPORT | |
537 | be,pt %xcc, abort_bad_guest_err_queue | |
538 | nop | |
539 | ||
540 | /* | |
541 | * If the ATTR field in the sun4v report has been set to | |
542 | * zero, we do not want to send this report. This will have | |
543 | * been done in the error handling code which populates the | |
544 | * report when it is determined that the error occurred | |
545 | * on some hyperprivileged register. | |
546 | */ | |
547 | GET_ERR_SUN4V_RPRT_BUF(%g3, %g4) | |
548 | ld [%g3 + ERR_SUN4V_RPRT_ATTR], %g4 ! attr | |
549 | brz,pn %g4, hpriv_error | |
550 | nop | |
551 | ||
552 | ba nonresumable_guest_trap | |
553 | .empty | |
554 | ||
555 | SET_SIZE(error_handler) | |
556 | ||
557 | /* | |
558 | * allocate a SUN4V report and fill it in. | |
559 | * %g1 &error_table_entry (preserved) | |
560 | * %g3 Sun4v report attribute | |
561 | * %g7 return address | |
562 | */ | |
563 | ENTRY(error_handler_sun4v_report) | |
564 | ||
565 | /* | |
566 | * Go through the array of err_sun4v_rprt looking | |
567 | * for in_use = 0. | |
568 | */ | |
569 | setx err_sun4v_rprt, %g2, %g4 | |
570 | RELOC_OFFSET(%g2, %g5) | |
571 | sub %g4, %g5, %g4 | |
572 | add %g4, ERR_SUN4V_RPRT_IN_USE, %g4 | |
573 | mov MAX_ERROR_REPORT_BUFS, %g5 | |
574 | 1: | |
575 | ldstub [%g4], %g6 ! in_use | |
576 | brz,a,pt %g6, 2f | |
577 | sub %g4, ERR_SUN4V_RPRT_IN_USE, %g4 ! back to &err_sunv_rprt | |
578 | ||
579 | /* | |
580 | * This err-sun4v_rprt is in use, go again | |
581 | */ | |
582 | dec %g5 | |
583 | brz,a,pn %g5, error_handler_sun4v_report_exit | |
584 | clr %g4 ! no buf available | |
585 | ba 1b ! try next buffer | |
586 | add %g4, ERR_SUN4V_RPRT_SIZE, %g4 | |
587 | ||
588 | 2: | |
589 | brz,pn %g4, error_handler_sun4v_report_exit | |
590 | .empty | |
591 | ||
592 | /* | |
593 | * First we store the address of the err_sun4v_rprt | |
594 | * in strand->strand_sun4v_rprt_buf[TL] | |
595 | */ | |
596 | STRAND_STRUCT(%g2) | |
597 | rdpr %tl, %g5 | |
598 | dec %g5 | |
599 | mulx %g5, STRAND_SUN4V_RPRT_BUF_INCR, %g5 | |
600 | add %g5, STRAND_SUN4V_RPRT_BUF, %g5 | |
601 | stx %g4, [%g2 + %g5] | |
602 | ||
603 | /* | |
604 | * PCI-E or sun4v error report ? | |
605 | */ | |
606 | cmp %g3, SUN4V_PCIE_RPRT | |
607 | be,pn %xcc, error_handler_sun4v_pcie_report | |
608 | nop | |
609 | ||
610 | /* | |
611 | * Attr field in error table entry is the bit position, need to shift | |
612 | * it to the actual value now. | |
613 | */ | |
614 | mov 1, %g5 | |
615 | sllx %g5, %g3, %g3 | |
616 | ||
617 | /* | |
618 | * Fill in the sun4v guest report | |
619 | */ | |
620 | ! error_table_entry->err_sun4v_edesc | |
621 | ldub [%g1 + ERR_SUN4V_EDESC], %g5 | |
622 | srl %g5, EDESC_TYPE_SHIFT, %g5 | |
623 | and %g5, EDESC_TYPE_MASK, %g5 | |
624 | st %g5, [%g4 + ERR_SUN4V_RPRT_EDESC] | |
625 | ! strand->strand-id | |
626 | ldub [%g2 + STRAND_ID], %g5 | |
627 | stuh %g5, [%g4 + ERR_SUN4V_RPRT_G_CPUID] | |
628 | GENERATE_EHDL(%g6, %g5) | |
629 | stx %g6, [%g4 + ERR_SUN4V_RPRT_G_EHDL] | |
630 | GET_ERR_STICK(%g5) | |
631 | stx %g5, [%g4 + ERR_SUN4V_RPRT_G_STICK] | |
632 | ||
633 | /* | |
634 | * Set the MODE, bits [25:24] of the ATTR field | |
635 | */ | |
636 | rdpr %tstate, %g5 | |
637 | srlx %g5, TSTATE_PSTATE_SHIFT, %g5 | |
638 | and %g5, PSTATE_PRIV, %g5 | |
639 | movrz %g5, ATTR_USER_MODE, %g6 ! PSTATE.PRIV == 0, User mode | |
640 | movrnz %g5, ATTR_PRIV_MODE, %g6 ! PSTATE.PRIV != 0, Privileged mode | |
641 | sllx %g6, ATTR_MODE_SHIFT, %g6 | |
642 | or %g3, %g6, %g3 ! %g3 ATTR, %g6 MODE | |
643 | ||
644 | st %g3, [%g4 + ERR_SUN4V_RPRT_ATTR] | |
645 | ||
646 | /* | |
647 | * The error-specific data will be filled in later. | |
648 | */ | |
649 | setx CPU_ERR_INVALID_RA, %g3, %g5 | |
650 | stx %g5, [%g4 + ERR_SUN4V_RPRT_ADDR] | |
651 | st %g0, [%g4 + ERR_SUN4V_RPRT_SZ] | |
652 | stub %g0, [%g4 + ERR_SUN4V_RPRT_ASI] | |
653 | ba error_handler_sun4v_report_exit | |
654 | nop | |
655 | ||
656 | error_handler_sun4v_pcie_report: | |
657 | GENERATE_EHDL(%g6, %g5) | |
658 | stx %g6, [%g4 + ERR_SUN4V_PCIE_EHDL] | |
659 | GET_ERR_STICK(%g5) | |
660 | stx %g5, [%g4 + ERR_SUN4V_PCIE_STICK] | |
661 | stx %g0, [%g4 + ERR_SUN4V_PCIE_SYSINO] | |
662 | st %g0, [%g4 + ERR_SUN4V_PCIE_DESC] | |
663 | st %g0, [%g4 + ERR_SUN4V_PCIE_SPECIFIC] | |
664 | stx %g0, [%g4 + ERR_SUN4V_PCIE_WORD4] | |
665 | stx %g0, [%g4 + ERR_SUN4V_PCIE_HDR1] | |
666 | stx %g0, [%g4 + ERR_SUN4V_PCIE_HDR2] | |
667 | ||
668 | error_handler_sun4v_report_exit: | |
669 | ||
670 | HVRET | |
671 | ||
672 | SET_SIZE(error_handler_sun4v_report) | |
673 | ||
674 | /* | |
675 | * allocate a DIAG report buffer | |
676 | * fill in common data | |
677 | * | |
678 | * %g1 &error_table_entry (preserved) | |
679 | * %g7 return address | |
680 | */ | |
681 | ENTRY(error_handler_diag_report) | |
682 | ||
683 | /* | |
684 | * Go through the array of err_diag_rprt looking | |
685 | * for in_use = 0. | |
686 | */ | |
687 | setx err_diag_rprt, %g2, %g4 | |
688 | RELOC_OFFSET(%g2, %g5) | |
689 | sub %g4, %g5, %g4 | |
690 | add %g4, ERR_DIAG_RPRT_IN_USE, %g4 | |
691 | mov MAX_ERROR_REPORT_BUFS, %g5 | |
692 | setx ERR_DIAG_RPRT_SIZE, %g3, %g6 | |
693 | 1: | |
694 | /* | |
695 | * The in_use flag is a 32-bit int to maintain compatibility | |
696 | * with svc_internal_send(). We use only the bottom eight | |
697 | * bits, hence the '+ 3'. | |
698 | */ | |
699 | ldstub [%g4 + 3], %g2 ! in_use | |
700 | brz,a,pt %g2, 2f ! REPORT_BUF_FREE | |
701 | sub %g4, ERR_DIAG_RPRT_IN_USE, %g4 ! back to &err_diag_rprt | |
702 | ||
703 | /* | |
704 | * This err_diag_rprt is in use, go again | |
705 | */ | |
706 | dec %g5 | |
707 | brz,a,pn %g5, error_handler_diag_report_exit | |
708 | clr %g4 ! no buf available | |
709 | ||
710 | ba 1b ! try next buffer | |
711 | add %g4, %g6, %g4 | |
712 | ||
713 | 2: | |
714 | brz,pn %g4, error_handler_diag_report_exit | |
715 | .empty | |
716 | ||
717 | /* | |
718 | * First we store the address of the err_diag_rprt | |
719 | * in strand->strand_err_diag_buf[TL] | |
720 | */ | |
721 | STRAND_STRUCT(%g2) | |
722 | rdpr %tl, %g3 | |
723 | dec %g3 | |
724 | mulx %g3, STRAND_DIAG_BUF_INCR, %g5 | |
725 | add %g5, STRAND_DIAG_BUF, %g5 | |
726 | stx %g4, [%g2 + %g5] | |
727 | ||
728 | add %g4, ERR_DIAG_RPRT_IN_USE, %g3 | |
729 | mov REPORT_BUF_PENDING, %g5 | |
730 | st %g5, [%g3] | |
731 | ||
732 | /* | |
733 | * Fill in the generic SP diagnosis report data | |
734 | * If we already have an EHDL in the Sun4v report just get | |
735 | * that sequence number | |
736 | */ | |
737 | GET_ERR_SUN4V_RPRT_BUF(%g5, %g3) | |
738 | brz,pt %g5, 1f | |
739 | nop | |
740 | ||
741 | ldub [%g1 + ERR_SUN4V_RPRT_TYPE], %g3 | |
742 | cmp %g3, SUN4V_PCIE_RPRT | |
743 | mov ERR_SUN4V_PCIE_EHDL, %g3 | |
744 | movne %xcc, ERR_SUN4V_RPRT_G_EHDL, %g3 | |
745 | ldx [%g5 + %g3], %g5 | |
746 | ba 2f | |
747 | nop | |
748 | ||
749 | 1: | |
750 | /* | |
751 | * generate a new strand sequence number | |
752 | */ | |
753 | GENERATE_EHDL(%g5, %g3) | |
754 | 2: | |
755 | stx %g5, [%g4 + ERR_DIAG_RPRT_EHDL] | |
756 | mov ERPT_TYPE_CPU, %g5 | |
757 | stx %g5, [%g4 + ERR_DIAG_RPRT_ERROR_TYPE] | |
758 | CONFIG_STRUCT(%g5) | |
759 | ldx [%g5 + CONFIG_TOD], %g5 | |
760 | brnz,a,pn %g5, 1f | |
761 | ldx [%g5], %g5 ! aborted if no TOD | |
762 | 1: | |
763 | stx %g5, [%g4 + ERR_DIAG_RPRT_TOD] | |
764 | ! error report type from error_table entry | |
765 | ldub [%g1 + ERR_SUN4V_EDESC], %g5 | |
766 | srl %g5, SER_TYPE_SHIFT, %g5 | |
767 | and %g5, SER_TYPE_MASK, %g5 | |
768 | stx %g5, [%g4 + ERR_DIAG_RPRT_REPORT_TYPE] | |
769 | ! error report size from error_table entry | |
770 | lduw [%g1 + ERR_REPORT_SIZE], %g5 | |
771 | stuw %g5, [%g4 + ERR_DIAG_RPRT_REPORT_SIZE] | |
772 | GET_ERR_STICK(%g5) | |
773 | stx %g5, [%g4 + ERR_DIAG_RPRT_ERR_STICK] | |
774 | rdhpr %hver, %g5 | |
775 | stx %g5, [%g4 + ERR_DIAG_RPRT_CPUVER] | |
776 | setx NCU_BASE + PROC_SER_NUM, %g5, %g3 | |
777 | ldx [%g3], %g5 | |
778 | stx %g5, [%g4 + ERR_DIAG_RPRT_SERIAL] | |
779 | ldub [%g2 + STRAND_ID], %g5 | |
780 | stuh %g5, [%g4 + ERR_DIAG_RPRT_CPUID] | |
781 | rdpr %tl, %g5 | |
782 | stub %g5, [%g4 + ERR_DIAG_RPRT_TL] | |
783 | rdpr %tt, %g5 | |
784 | stuh %g5, [%g4 + ERR_DIAG_RPRT_TT] | |
785 | rdpr %tstate, %g5 | |
786 | stx %g5, [%g4 + ERR_DIAG_RPRT_TSTATE] | |
787 | rdhpr %htstate, %g5 | |
788 | stx %g5, [%g4 + ERR_DIAG_RPRT_HTSTATE] | |
789 | rdpr %tpc, %g5 | |
790 | stx %g5, [%g4 + ERR_DIAG_RPRT_TPC] | |
791 | ||
792 | /* | |
793 | * Clear the diag_buf ESRs, up to the in_use flag | |
794 | */ | |
795 | add %g4, ERR_DIAG_RPRT_ERR_DIAG, %g4 | |
796 | ||
797 | STRAND_PUSH(%g7, %g1, %g2) | |
798 | mov %g4, %g1 | |
799 | mov ERR_DIAG_BUF_RPRT_IN_USE, %g2 | |
800 | HVCALL(bzero) | |
801 | STRAND_POP(%g7, %g1) | |
802 | ||
803 | /* | |
804 | * store the DESR | |
805 | */ | |
806 | GET_ERR_DESR(%g2, %g3) | |
807 | stx %g2, [%g4 + ERR_DIAG_BUF_SPARC_DESR] | |
808 | ||
809 | /* | |
810 | * store the DFESR | |
811 | */ | |
812 | GET_ERR_DFESR(%g2, %g3) | |
813 | stx %g2, [%g4 + ERR_DIAG_BUF_SPARC_DFESR] | |
814 | ||
815 | /* | |
816 | * store the D-SFSR/I-SFSR/D-SFAR | |
817 | */ | |
818 | GET_ERR_DSFSR(%g3, %g5) | |
819 | stx %g3, [%g4 + ERR_DIAG_BUF_SPARC_DSFSR] | |
820 | GET_ERR_DSFAR(%g3, %g5) | |
821 | stx %g3, [%g4 + ERR_DIAG_BUF_SPARC_DSFAR] | |
822 | GET_ERR_ISFSR(%g3, %g5) | |
823 | stx %g3, [%g4 + ERR_DIAG_BUF_SPARC_ISFSR] | |
824 | ||
825 | error_handler_diag_report_exit: | |
826 | ||
827 | HVRET | |
828 | SET_SIZE(error_handler_diag_report) | |
829 | ||
830 | /* | |
831 | * Send an error report for diagnosis to the SP | |
832 | * | |
833 | * %g1 - %g6 clobbered | |
834 | * %g7 return address | |
835 | */ | |
836 | ENTRY(transmit_diag_reports) | |
837 | ||
838 | STORE_ERR_RETURN_ADDR(%g7, %g1, %g2) | |
839 | ||
840 | /* | |
841 | * Go through the array of err_diag_rprt looking | |
842 | * for in_use = REPORT_BUF_PENDING. | |
843 | */ | |
844 | transmit_diag_reports_start: | |
845 | ||
846 | setx err_diag_rprt, %g2, %g4 | |
847 | RELOC_OFFSET(%g2, %g5) | |
848 | sub %g4, %g5, %g4 | |
849 | add %g4, ERR_DIAG_RPRT_IN_USE, %g4 | |
850 | mov MAX_ERROR_REPORT_BUFS, %g5 | |
851 | setx ERR_DIAG_RPRT_SIZE, %g2, %g6 | |
852 | 1: | |
853 | /* | |
854 | * The in_use flag is a 32-bit int to maintain compatibility | |
855 | * with svc_internal_send(). We use only the bottom eight | |
856 | * bits, hence the '+ 3'. | |
857 | */ | |
858 | ldub [%g4 + 3], %g2 ! in_use | |
859 | cmp %g2, REPORT_BUF_IN_USE | |
860 | be,a,pt %xcc, 2f | |
861 | sub %g4, ERR_DIAG_RPRT_IN_USE, %g4 ! back to &err_diag_rprt | |
862 | ||
863 | /* | |
864 | * This err_diag_rprt is not ready to be sent to the | |
865 | * SP, go again | |
866 | */ | |
867 | dec %g5 | |
868 | brz,a,pn %g5, transmit_diag_piu_reports | |
869 | clr %g4 ! no buf available | |
870 | ba 1b ! try next buffer | |
871 | add %g4, %g6, %g4 | |
872 | ||
873 | 2: | |
874 | /* | |
875 | * send the report to the SP | |
876 | * send_diag_erpt() will clear the in_use flag | |
877 | * %g4 diag_buf | |
878 | */ | |
879 | mov %g4, %g1 | |
880 | add %g1, ERR_DIAG_RPRT_IN_USE, %g2 | |
881 | add %g1, ERR_DIAG_RPRT_REPORT_SIZE, %g3 | |
882 | lduw [%g3], %g3 | |
883 | HVCALL(send_diag_erpt_nolock) | |
884 | ||
885 | ba transmit_diag_reports_exit | |
886 | nop | |
887 | ||
888 | transmit_diag_piu_reports: | |
889 | #ifdef CONFIG_PIU | |
890 | /* | |
891 | * Check whether any PIU error reports are waiting to be transmitted | |
892 | */ | |
893 | setx piu_dev, %g2, %g4 | |
894 | RELOC_OFFSET(%g2, %g5) | |
895 | sub %g4, %g5, %g4 | |
896 | set NPIUS, %g5 | |
897 | .check_next_piu_dev: | |
898 | ! each piu_dev has two error reports, DMU and PEU | |
899 | ! check the 'unsent' flag on each | |
900 | ! %g4 piu_dev[] | |
901 | add %g4, PIU_COOKIE_DMU_ERPT, %g1 | |
902 | ldsw [%g1 + PCI_UNSENT_PKT], %g2 | |
903 | brnz,pn %g2, .transmit_piu_dev_err | |
904 | nop | |
905 | add %g4, PIU_COOKIE_PEU_ERPT, %g1 | |
906 | ldsw [%g1 + PCI_UNSENT_PKT], %g2 | |
907 | brnz,pn %g2, .transmit_piu_dev_err | |
908 | dec %g5 | |
909 | brnz,pt %g5, .check_next_piu_dev | |
910 | add %g4, PIU_COOKIE_SIZE, %g4 | |
911 | ||
912 | ! nothing to send | |
913 | ba transmit_diag_reports_exit | |
914 | nop | |
915 | ||
916 | .transmit_piu_dev_err: | |
917 | ! PIU error ready to go | |
918 | ! %g1 PIU error report | |
919 | add %g1, PCI_UNSENT_PKT, %g2 | |
920 | add %g1, PCI_ERPT_U, %g1 | |
921 | mov PCIERPT_SIZE - EPKTSIZE, %g3 | |
922 | HVCALL(send_diag_erpt_nolock) | |
923 | ||
924 | #endif /* CONFIG_PIU */ | |
925 | ||
926 | transmit_diag_reports_exit: | |
927 | ||
928 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
929 | HVRET | |
930 | ||
931 | SET_SIZE(transmit_diag_reports) | |
932 | ||
933 | /* | |
934 | * Jump to the nonresumable_error trap of the privileged code. | |
935 | * Select trap table entry based on TL. | |
936 | */ | |
937 | ENTRY(nonresumable_guest_trap) | |
938 | ||
939 | PRINT_NOTRAP("nonresumable_guest_trap\r\n"); | |
940 | /* | |
941 | * Put the sun4v ereport onto the non-resumable queue for this | |
942 | * guest | |
943 | * | |
944 | * Note that both precise and deferred non-resumable error | |
945 | * reports will be queued on the guests non-resumable error | |
946 | * queue here. | |
947 | * | |
948 | * Note: We don't care about saving our return address (%g7) | |
949 | * here because we are not getting out of here alive. | |
950 | */ | |
951 | HVCALL(errors_queue_nonresumable_erpt) | |
952 | ||
953 | VCPU_STRUCT(%g1) | |
954 | IS_CPU_IN_ERROR(%g1, %g2) | |
955 | bne %xcc, 1f | |
956 | nop | |
957 | ||
958 | ! Mark the corresponding strand in error | |
959 | HVCALL(strand_in_error) | |
960 | 1: | |
961 | /* | |
962 | * Jump to the nonresumable_error trap of the privileged code. | |
963 | * Select trap table entry based on TL. | |
964 | * Set TT for the guest | |
965 | */ | |
966 | wrpr %g0, TT_NONRESUMABLE_ERR, %tt | |
967 | ||
968 | /* | |
969 | * ensure that the guest is not entered in an illegal state | |
970 | */ | |
971 | GET_ERR_GL(%g1) | |
972 | cmp %g1, MAXPGL | |
973 | bgu,pn %xcc, watchdog_guest | |
974 | rdpr %tl, %g1 | |
975 | cmp %g1, MAXPTL | |
976 | bgu,pn %xcc, watchdog_guest | |
977 | .empty | |
978 | ||
979 | /* | |
980 | * Build TSTATE from current state for the trap to the | |
981 | * guests non_resumable_error trap table entry. | |
982 | */ | |
983 | rdhpr %hpstate, %g3 | |
984 | mov PSTATE_PRIV, %g1 | |
985 | sllx %g1, TSTATE_PSTATE_SHIFT, %g4 | |
986 | ||
987 | GET_ERR_CWP(%g1) | |
988 | sllx %g1, TSTATE_CWP_SHIFT, %g1 | |
989 | or %g4, %g1, %g4 | |
990 | rdpr %tstate, %g1 | |
991 | srlx %g1, TSTATE_ASI_SHIFT, %g1 | |
992 | and %g1, TSTATE_ASI_MASK, %g1 | |
993 | sllx %g1, TSTATE_ASI_SHIFT, %g1 | |
994 | or %g4, %g1, %g4 | |
995 | rd %ccr, %g1 | |
996 | sllx %g1, TSTATE_CCR_SHIFT, %g1 | |
997 | or %g4, %g1, %g4 | |
998 | GET_ERR_GL(%g1) | |
999 | sllx %g1, TSTATE_GL_SHIFT, %g1 | |
1000 | or %g4, %g1, %g4 | |
1001 | rdpr %tba, %g1 | |
1002 | or %g1, (TT_NONRESUMABLE_ERR << TT_OFFSET_SHIFT), %g1 | |
1003 | rdpr %tl, %g2 | |
1004 | cmp %g2, 1 | |
1005 | be,pt %xcc, 2f ! if TL - 1 == 0, go to 2 | |
1006 | set TRAPTABLE_SIZE, %g5 ! set TL bit in trap address | |
1007 | add %g5, %g1, %g1 ! add TL for TL > 1 | |
1008 | 2: | |
1009 | /* | |
1010 | * Cache the err_flags before incrementing TL as | |
1011 | * the GET_ERR_TABLE_ENTRY() macro uses TL to find the | |
1012 | * error_table entry for this error | |
1013 | */ | |
1014 | GET_ERR_TABLE_ENTRY(%g5, %g6) | |
1015 | ld [%g5 + ERR_FLAGS], %g5 | |
1016 | ||
1017 | ! %g1 trap_table address | |
1018 | ! %g2 TL | |
1019 | ! %g4 TSTATE | |
1020 | ! %g5 error_table->err_flags | |
1021 | inc %g2 | |
1022 | wrpr %g2, %tl | |
1023 | wrpr %g0, TT_NONRESUMABLE_ERR, %tt | |
1024 | wrpr %g1, %tpc | |
1025 | add %g1, 4, %g1 | |
1026 | wrpr %g1, %tnpc | |
1027 | wrpr %g4, %tstate | |
1028 | wrhpr %g0, HPSTATE_GUEST, %htstate | |
1029 | ||
1030 | /* | |
1031 | * After RETRY we will have :- | |
1032 | * %pc guest non_resumable_error trap table entry | |
1033 | * %npc guest non_resumable_error trap table entry + 4 | |
1034 | * %gl Current GL which error handler is running at | |
1035 | * %tl Current TL which error handler is running at | |
1036 | * %tt TT_NONRESUMABLE_ERR | |
1037 | * %tpc PC of UE precise error trap | |
1038 | */ | |
1039 | ||
1040 | btst ERR_GL_STORED, %g5 | |
1041 | bz,pt %xcc, 1f | |
1042 | nop | |
1043 | ||
1044 | RESTORE_GLOBALS(retry) | |
1045 | ||
1046 | 1: | |
1047 | retry | |
1048 | ||
1049 | SET_SIZE(nonresumable_guest_trap) | |
1050 | ||
1051 | /* | |
1052 | * Queue a resumable error report on this CPU | |
1053 | * | |
1054 | * %g1 &error_table_entry | |
1055 | * %g2 sun4v report type | |
1056 | * %g7 return address | |
1057 | */ | |
1058 | ENTRY_NP(errors_queue_resumable_erpt) | |
1059 | ||
1060 | /* | |
1061 | * Before we send a report to the guest, we must make ensure that the | |
1062 | * error actually occurred on hardware resources owned by the guest and | |
1063 | * that the error trap was not simply steered to a CPU owned by the guest. | |
1064 | */ | |
1065 | STRAND_PUSH(%g7, %g3, %g4) | |
1066 | STRAND_PUSH(%g2, %g3, %g4) | |
1067 | STRAND_PUSH(%g1, %g3, %g4) | |
1068 | HVCALL(errors_check_steering) | |
1069 | STRAND_POP(%g1, %g3) | |
1070 | STRAND_POP(%g2, %g3) | |
1071 | STRAND_POP(%g7, %g3) | |
1072 | ||
1073 | cmp %g2, SUN4V_PCIE_RPRT | |
1074 | bne,pt %xcc, 1f | |
1075 | nop | |
1076 | ||
1077 | /* | |
1078 | * PCI-E error interrupt | |
1079 | */ | |
1080 | ba queue_pcie_erpt | |
1081 | nop | |
1082 | 1: | |
1083 | STORE_ERR_RETURN_ADDR(%g7, %g1, %g2) | |
1084 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g4) | |
1085 | add %g2, ERR_SUN4V_CPU_ERPT, %g2 | |
1086 | HVCALL(queue_resumable_erpt) | |
1087 | ba errors_queue_resumable_erpt_done | |
1088 | nop | |
1089 | ||
1090 | queue_pcie_erpt: | |
1091 | /* | |
1092 | * insert a dev_mondo using the PCI-E error packet | |
1093 | */ | |
1094 | STORE_ERR_RETURN_ADDR(%g7, %g1, %g2) | |
1095 | GET_ERR_SUN4V_RPRT_BUF(%g1, %g4) | |
1096 | add %g1, ERR_SUN4V_PCIE_ERPT, %g1 | |
1097 | HVCALL(insert_device_mondo_p) | |
1098 | ||
1099 | errors_queue_resumable_erpt_done: | |
1100 | /* | |
1101 | * Clear the in_use bit on the sun4v report buffer | |
1102 | */ | |
1103 | GET_ERR_SUN4V_RPRT_BUF(%g3, %g4) | |
1104 | stub %g0, [%g3 + ERR_SUN4V_RPRT_IN_USE] | |
1105 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
1106 | HVRET | |
1107 | ||
1108 | SET_SIZE(errors_queue_resumable_erpt) | |
1109 | ||
1110 | ||
1111 | /* | |
1112 | * Queue a nonresumable error report on this CPU | |
1113 | * | |
1114 | * If there is no free entry in the nonresumable error queue | |
1115 | * print a message and abort the guest. | |
1116 | */ | |
1117 | ENTRY_NP(errors_queue_nonresumable_erpt) | |
1118 | ||
1119 | /* | |
1120 | * Before we send a report to the guest, we must make ensure that the | |
1121 | * error actually occurred on hardware resources owned by the guest and | |
1122 | * that the error trap was not simply steered to a CPU owned by the guest. | |
1123 | */ | |
1124 | STRAND_PUSH(%g7, %g3, %g4) | |
1125 | HVCALL(errors_check_steering) | |
1126 | STRAND_POP(%g7, %g3) | |
1127 | ||
1128 | STORE_ERR_RETURN_ADDR(%g7, %g1, %g2) | |
1129 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g4) | |
1130 | ||
1131 | /* | |
1132 | * If this is a MEM report, set the SZ field here. | |
1133 | */ | |
1134 | ld [%g2 + ERR_SUN4V_RPRT_ATTR], %g4 ! attr | |
1135 | mov 1, %g3 | |
1136 | sllx %g3, SUN4V_MEM_RPRT, %g3 | |
1137 | and %g4, %g3, %g4 | |
1138 | brz,pt %g4, 1f | |
1139 | mov ERPT_MEM_SIZE, %g4 | |
1140 | st %g4, [%g4 + ERR_SUN4V_RPRT_SZ] | |
1141 | 1: | |
1142 | ||
1143 | add %g2, ERR_SUN4V_CPU_ERPT, %g2 | |
1144 | HVCALL(queue_nonresumable_erpt) | |
1145 | ||
1146 | #ifdef DEBUG_LEGION | |
1147 | /* | |
1148 | * print sun4v erpt data to console | |
1149 | */ | |
1150 | HVCALL(print_sun4v_erpt) | |
1151 | #endif | |
1152 | ||
1153 | /* | |
1154 | * Clear the in_use bit on the sun4v report buffer | |
1155 | */ | |
1156 | GET_ERR_SUN4V_RPRT_BUF(%g2, %g4) | |
1157 | stub %g0, [%g2 + ERR_SUN4V_RPRT_IN_USE] | |
1158 | GET_ERR_RETURN_ADDR(%g7, %g2) | |
1159 | ||
1160 | HVRET | |
1161 | SET_SIZE(errors_queue_nonresumable_erpt) | |
1162 | ||
1163 | ||
1164 | /* | |
1165 | * %g2 sun4v error report | |
1166 | * %g7 return address | |
1167 | * Returns | |
1168 | * %g1 0 - success; 1 - failure | |
1169 | */ | |
1170 | ENTRY(queue_resumable_erpt) | |
1171 | VCPU_STRUCT(%g1) | |
1172 | ||
1173 | ldx [%g1 + CPU_ERRQR_BASE_RA], %g3 ! get q base RA | |
1174 | brnz %g3, 1f ! if base RA is zero, skip | |
1175 | nop | |
1176 | ! The resumable error queue is not allocated/initialized | |
1177 | ! simply return. No guest is there to receive it. | |
1178 | queue_resumable_erpt_full: | |
1179 | mov 1, %g1 ! failed to queue | |
1180 | HVRET | |
1181 | ||
1182 | 1: | |
1183 | mov ERROR_RESUMABLE_QUEUE_TAIL, %g3 | |
1184 | ldxa [%g3]ASI_QUEUE, %g5 ! %g5 = rq_tail | |
1185 | add %g5, Q_EL_SIZE, %g6 ! %g6 = rq_next = rq_tail++ | |
1186 | ldx [%g1 + CPU_ERRQR_MASK], %g4 | |
1187 | and %g6, %g4, %g6 ! %g6 = rq_next mod | |
1188 | mov ERROR_RESUMABLE_QUEUE_HEAD, %g3 | |
1189 | ldxa [%g3] ASI_QUEUE, %g4 ! %g4 = rq_head | |
1190 | cmp %g6, %g4 ! head = ++tail? | |
1191 | be %xcc, queue_resumable_erpt_full | |
1192 | mov ERROR_RESUMABLE_QUEUE_TAIL, %g3 | |
1193 | stxa %g6, [%g3] ASI_QUEUE ! new tail = rq_next | |
1194 | ! write up the queue record | |
1195 | ! %g2 sun4v ereport buf | |
1196 | ldx [%g1 + CPU_ERRQR_BASE], %g4 | |
1197 | add %g5, %g4, %g3 ! %g3 = base + tail | |
1198 | ldx [%g2 + CPU_SUN4V_RPRT_G_EHDL], %g4 ! ehdl | |
1199 | stx %g4, [%g3 + SUN4V_EHDL_OFFSET] | |
1200 | ldx [%g2 + CPU_SUN4V_RPRT_G_STICK], %g4 ! stick | |
1201 | stx %g4, [%g3 + SUN4V_TICK_OFFSET] | |
1202 | ld [%g2 + CPU_SUN4V_RPRT_EDESC], %g4 ! edesc | |
1203 | st %g4, [%g3 + SUN4V_DESC_OFFSET] | |
1204 | ld [%g2 + CPU_SUN4V_RPRT_ATTR], %g4 ! attr | |
1205 | st %g4, [%g3 + SUN4V_ATTR_OFFSET] | |
1206 | ldx [%g2 + CPU_SUN4V_RPRT_ADDR], %g4 ! addr | |
1207 | stx %g4, [%g3 + SUN4V_ADDR_OFFSET] | |
1208 | ld [%g2 + CPU_SUN4V_RPRT_SZ], %g4 ! sz | |
1209 | st %g4, [%g3 + SUN4V_SZ_OFFSET] | |
1210 | lduh [%g2 + CPU_SUN4V_RPRT_G_CPUID], %g4 ! cpuid | |
1211 | stuh %g4, [%g3 + SUN4V_CPUID_OFFSET] | |
1212 | lduh [%g2 + CPU_SUN4V_RPRT_G_SECS], %g4 | |
1213 | stuh %g4, [%g3 + SUN4V_SECS_OFFSET] ! secs | |
1214 | ldub [%g2 + CPU_SUN4V_RPRT_ASI], %g4 | |
1215 | stub %g4, [%g3 + SUN4V_ASI_OFFSET] ! asi/pad | |
1216 | lduh [%g2 + CPU_SUN4V_RPRT_REG], %g4 | |
1217 | stuh %g4, [%g3 + SUN4V_REG_OFFSET] ! reg | |
1218 | st %g0, [%g3 + SUN4V_PAD0_OFFSET] | |
1219 | stx %g0, [%g3 + SUN4V_PAD1_OFFSET] | |
1220 | stx %g0, [%g3 + SUN4V_PAD2_OFFSET] | |
1221 | ||
1222 | clr %g1 ! success | |
1223 | HVRET | |
1224 | ||
1225 | SET_SIZE(queue_resumable_erpt) | |
1226 | ||
1227 | ||
1228 | /* | |
1229 | * %g2 sun4v error report | |
1230 | * %g7 return address | |
1231 | * Returns | |
1232 | * %g1 0 - success; 1 - failure | |
1233 | */ | |
1234 | ENTRY_NP(queue_nonresumable_erpt) | |
1235 | ||
1236 | ! %g1 vcpup | |
1237 | VCPU_STRUCT(%g1) | |
1238 | ! Get the guest structure this vcpu belongs | |
1239 | VCPU2GUEST_STRUCT(%g1, %g5) | |
1240 | ||
1241 | ! Determine the guest state | |
1242 | lduw [%g5 + GUEST_STATE], %g4 | |
1243 | set GUEST_STATE_SUSPENDED, %g3 | |
1244 | cmp %g4, %g3 | |
1245 | be,pn %xcc, .check_vcpu_queues | |
1246 | set GUEST_STATE_NORMAL, %g3 | |
1247 | cmp %g4, %g3 | |
1248 | be,pn %xcc, .check_vcpu_queues | |
1249 | set GUEST_STATE_EXITING, %g3 | |
1250 | cmp %g4, %g3 | |
1251 | be,pn %xcc, .drop_nrq_pkt | |
1252 | set GUEST_STATE_STOPPED, %g3 | |
1253 | cmp %g4, %g3 | |
1254 | be,pn %xcc, .drop_nrq_pkt | |
1255 | set GUEST_STATE_UNCONFIGURED, %g3 | |
1256 | cmp %g4, %g3 | |
1257 | be,pn %xcc, .drop_nrq_pkt | |
1258 | nop | |
1259 | ||
1260 | .check_vcpu_queues: | |
1261 | ldx [%g1 + CPU_ERRQNR_BASE_RA], %g3 ! get q base RA | |
1262 | brz %g3, abort_missing_guest_err_queue ! if base RA zero, abort | |
1263 | nop | |
1264 | ||
1265 | mov ERROR_NONRESUMABLE_QUEUE_TAIL, %g3 | |
1266 | ldxa [%g3]ASI_QUEUE, %g5 ! %g5 = rq_tail | |
1267 | add %g5, Q_EL_SIZE, %g6 ! %g6 = rq_next = rq_tail++ | |
1268 | ldx [%g1 + CPU_ERRQNR_MASK], %g4 | |
1269 | and %g6, %g4, %g6 ! %g6 = rq_next mod | |
1270 | mov ERROR_NONRESUMABLE_QUEUE_HEAD, %g3 | |
1271 | ldxa [%g3] ASI_QUEUE, %g4 ! %g4 = rq_head | |
1272 | cmp %g6, %g4 ! head = ++tail? | |
1273 | be %xcc, abort_bad_guest_err_queue | |
1274 | mov ERROR_NONRESUMABLE_QUEUE_TAIL, %g3 | |
1275 | stxa %g6, [%g3] ASI_QUEUE ! new tail = rq_next | |
1276 | ! write up the queue record | |
1277 | ! %g2 sun4v ereport buf | |
1278 | ldx [%g1 + CPU_ERRQNR_BASE], %g4 | |
1279 | add %g5, %g4, %g3 ! %g3 = base + tail | |
1280 | ldx [%g2 + ERR_SUN4V_RPRT_G_EHDL], %g4 ! ehdl | |
1281 | stx %g4, [%g3 + SUN4V_EHDL_OFFSET] | |
1282 | ldx [%g2 + ERR_SUN4V_RPRT_G_STICK], %g4 ! stick | |
1283 | stx %g4, [%g3 + SUN4V_TICK_OFFSET] | |
1284 | ld [%g2 + ERR_SUN4V_RPRT_EDESC], %g4 ! edesc | |
1285 | st %g4, [%g3 + SUN4V_DESC_OFFSET] | |
1286 | ld [%g2 + ERR_SUN4V_RPRT_ATTR], %g4 ! attr | |
1287 | st %g4, [%g3 + SUN4V_ATTR_OFFSET] | |
1288 | ldx [%g2 + ERR_SUN4V_RPRT_ADDR], %g4 ! addr | |
1289 | stx %g4, [%g3 + SUN4V_ADDR_OFFSET] | |
1290 | ld [%g2 + ERR_SUN4V_RPRT_SZ], %g4 ! sz | |
1291 | st %g4, [%g3 + SUN4V_SZ_OFFSET] | |
1292 | lduh [%g2 + ERR_SUN4V_RPRT_G_CPUID], %g4 ! cpuid | |
1293 | stuh %g4, [%g3 + SUN4V_CPUID_OFFSET] | |
1294 | lduh [%g2 + ERR_SUN4V_RPRT_G_SECS], %g4 | |
1295 | stuh %g4, [%g3 + SUN4V_SECS_OFFSET] ! secs | |
1296 | lduh [%g2 + ERR_SUN4V_RPRT_ASI], %g4 | |
1297 | stuh %g4, [%g3 + SUN4V_ASI_OFFSET] ! asi/pad | |
1298 | lduh [%g2 + ERR_SUN4V_RPRT_REG], %g4 | |
1299 | stuh %g4, [%g3 + SUN4V_REG_OFFSET] ! reg | |
1300 | st %g0, [%g3 + SUN4V_PAD0_OFFSET] | |
1301 | stx %g0, [%g3 + SUN4V_PAD1_OFFSET] | |
1302 | stx %g0, [%g3 + SUN4V_PAD2_OFFSET] | |
1303 | ||
1304 | clr %g1 ! success | |
1305 | HVRET | |
1306 | ||
1307 | .drop_nrq_pkt: | |
1308 | /* | |
1309 | * The guest is not in the proper state to receive pkts | |
1310 | * Drop packet by just returning | |
1311 | */ | |
1312 | #ifdef DEBUG | |
1313 | mov %g7, %g6 | |
1314 | PRINT("no guest to deliver NR error pkt. Dropping it\r\n") | |
1315 | mov %g6, %g7 | |
1316 | #endif | |
1317 | mov 1, %g1 ! failed to queue | |
1318 | HVRET | |
1319 | ||
1320 | /* | |
1321 | * The nonresumable error queue is full. | |
1322 | * Reset the guest | |
1323 | */ | |
1324 | abort_bad_guest_err_queue: | |
1325 | #ifdef DEBUG | |
1326 | mov %g7, %g6 | |
1327 | PRINT("queue_nonresumable_erpt: nrq full - exiting guest\r\n") | |
1328 | mov %g6, %g7 | |
1329 | #endif | |
1330 | SET_CPU_IN_ERROR(%g1, %g2) | |
1331 | ba queue_resumable_erpt | |
1332 | nop | |
1333 | ||
1334 | abort_missing_guest_err_queue: | |
1335 | #ifdef DEBUG | |
1336 | mov %g7, %g6 | |
1337 | PRINT("queue_nonresumable_erpt: q missing - exiting guest\r\n") | |
1338 | mov %g6, %g7 | |
1339 | #endif | |
1340 | mov 1, %g1 ! failed to queue | |
1341 | HVRET | |
1342 | ||
1343 | SET_SIZE(queue_nonresumable_erpt) | |
1344 | ||
1345 | ||
1346 | /* | |
1347 | * Uncorrectable error in HV | |
1348 | * | |
1349 | * Note that the diagnosis report should have been sent to the | |
1350 | * SP already | |
1351 | */ | |
1352 | hpriv_error: | |
1353 | #ifdef DEBUG | |
1354 | PRINT("ABORT ON HV UE!\r\n"); | |
1355 | #endif | |
1356 | HPRIV_ERROR() | |
1357 | ||
1358 | fatal_error: | |
1359 | #ifdef DEBUG | |
1360 | PRINT("ABORT ON FATAL ERROR!\r\n"); | |
1361 | #endif | |
1362 | FATAL_ERROR() | |
1363 | ||
1364 | hvabort_exit: | |
1365 | ||
1366 | /* | |
1367 | * make sure any outstanding error reports get sent | |
1368 | */ | |
1369 | HVCALL(transmit_diag_reports) | |
1370 | ||
1371 | #ifdef CONFIG_VBSC_SVC | |
1372 | HV_PRINT_NOTRAP(", contacting vbsc\r\n"); | |
1373 | ba,pt %xcc, vbsc_hv_abort | |
1374 | rd %pc, %g1 | |
1375 | #else | |
1376 | HV_PRINT_NOTRAP(", spinning\r\n"); | |
1377 | LEGION_EXIT(1) | |
1378 | 2: ba,a 2b | |
1379 | nop | |
1380 | #endif |