1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/arch/alpha/kernel/err_ev6.c |
4 | * |
5 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) |
6 | * |
7 | * Error handling code supporting Alpha systems |
8 | */ |
9 | |
10 | #include <linux/sched.h> |
11 | |
12 | #include <asm/io.h> |
13 | #include <asm/irq_regs.h> |
14 | #include <asm/hwrpb.h> |
15 | #include <asm/smp.h> |
16 | #include <asm/err_common.h> |
17 | #include <asm/err_ev6.h> |
18 | |
19 | #include "err_impl.h" |
20 | #include "proto.h" |
21 | |
22 | static int |
23 | ev6_parse_ibox(u64 i_stat, int print) |
24 | { |
25 | int status = MCHK_DISPOSITION_REPORT; |
26 | |
27 | #define EV6__I_STAT__PAR (1UL << 29) |
28 | #define EV6__I_STAT__ERRMASK (EV6__I_STAT__PAR) |
29 | |
30 | if (!(i_stat & EV6__I_STAT__ERRMASK)) |
31 | return MCHK_DISPOSITION_UNKNOWN_ERROR; |
32 | |
33 | if (!print) |
34 | return status; |
35 | |
36 | if (i_stat & EV6__I_STAT__PAR) |
37 | printk("%s Icache parity error\n" , err_print_prefix); |
38 | |
39 | return status; |
40 | } |
41 | |
42 | static int |
43 | ev6_parse_mbox(u64 mm_stat, u64 d_stat, u64 c_stat, int print) |
44 | { |
45 | int status = MCHK_DISPOSITION_REPORT; |
46 | |
47 | #define EV6__MM_STAT__DC_TAG_PERR (1UL << 10) |
48 | #define EV6__MM_STAT__ERRMASK (EV6__MM_STAT__DC_TAG_PERR) |
49 | #define EV6__D_STAT__TPERR_P0 (1UL << 0) |
50 | #define EV6__D_STAT__TPERR_P1 (1UL << 1) |
51 | #define EV6__D_STAT__ECC_ERR_ST (1UL << 2) |
52 | #define EV6__D_STAT__ECC_ERR_LD (1UL << 3) |
53 | #define EV6__D_STAT__SEO (1UL << 4) |
54 | #define EV6__D_STAT__ERRMASK (EV6__D_STAT__TPERR_P0 | \ |
55 | EV6__D_STAT__TPERR_P1 | \ |
56 | EV6__D_STAT__ECC_ERR_ST | \ |
57 | EV6__D_STAT__ECC_ERR_LD | \ |
58 | EV6__D_STAT__SEO) |
59 | |
60 | if (!(d_stat & EV6__D_STAT__ERRMASK) && |
61 | !(mm_stat & EV6__MM_STAT__ERRMASK)) |
62 | return MCHK_DISPOSITION_UNKNOWN_ERROR; |
63 | |
64 | if (!print) |
65 | return status; |
66 | |
67 | if (mm_stat & EV6__MM_STAT__DC_TAG_PERR) |
68 | printk("%s Dcache tag parity error on probe\n" , |
69 | err_print_prefix); |
70 | if (d_stat & EV6__D_STAT__TPERR_P0) |
71 | printk("%s Dcache tag parity error - pipe 0\n" , |
72 | err_print_prefix); |
73 | if (d_stat & EV6__D_STAT__TPERR_P1) |
74 | printk("%s Dcache tag parity error - pipe 1\n" , |
75 | err_print_prefix); |
76 | if (d_stat & EV6__D_STAT__ECC_ERR_ST) |
77 | printk("%s ECC error occurred on a store\n" , |
78 | err_print_prefix); |
79 | if (d_stat & EV6__D_STAT__ECC_ERR_LD) |
80 | printk("%s ECC error occurred on a %s load\n" , |
81 | err_print_prefix, |
82 | c_stat ? "" : "speculative " ); |
83 | if (d_stat & EV6__D_STAT__SEO) |
84 | printk("%s Dcache second error\n" , err_print_prefix); |
85 | |
86 | return status; |
87 | } |
88 | |
89 | static int |
90 | ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn, |
91 | u64 c_stat, u64 c_sts, int print) |
92 | { |
93 | static const char * const sourcename[] = { |
94 | "UNKNOWN" , "UNKNOWN" , "UNKNOWN" , |
95 | "MEMORY" , "BCACHE" , "DCACHE" , |
96 | "BCACHE PROBE" , "BCACHE PROBE" |
97 | }; |
98 | static const char * const streamname[] = { "D" , "I" }; |
99 | static const char * const bitsname[] = { "SINGLE" , "DOUBLE" }; |
100 | int status = MCHK_DISPOSITION_REPORT; |
101 | int source = -1, stream = -1, bits = -1; |
102 | |
103 | #define EV6__C_STAT__BC_PERR (0x01) |
104 | #define EV6__C_STAT__DC_PERR (0x02) |
105 | #define EV6__C_STAT__DSTREAM_MEM_ERR (0x03) |
106 | #define EV6__C_STAT__DSTREAM_BC_ERR (0x04) |
107 | #define EV6__C_STAT__DSTREAM_DC_ERR (0x05) |
108 | #define EV6__C_STAT__PROBE_BC_ERR0 (0x06) /* both 6 and 7 indicate... */ |
109 | #define EV6__C_STAT__PROBE_BC_ERR1 (0x07) /* ...probe bc error. */ |
110 | #define EV6__C_STAT__ISTREAM_MEM_ERR (0x0B) |
111 | #define EV6__C_STAT__ISTREAM_BC_ERR (0x0C) |
112 | #define EV6__C_STAT__DSTREAM_MEM_DBL (0x13) |
113 | #define EV6__C_STAT__DSTREAM_BC_DBL (0x14) |
114 | #define EV6__C_STAT__ISTREAM_MEM_DBL (0x1B) |
115 | #define EV6__C_STAT__ISTREAM_BC_DBL (0x1C) |
116 | #define EV6__C_STAT__SOURCE_MEMORY (0x03) |
117 | #define EV6__C_STAT__SOURCE_BCACHE (0x04) |
118 | #define EV6__C_STAT__SOURCE__S (0) |
119 | #define EV6__C_STAT__SOURCE__M (0x07) |
120 | #define EV6__C_STAT__ISTREAM__S (3) |
121 | #define EV6__C_STAT__ISTREAM__M (0x01) |
122 | #define EV6__C_STAT__DOUBLE__S (4) |
123 | #define EV6__C_STAT__DOUBLE__M (0x01) |
124 | #define EV6__C_STAT__ERRMASK (0x1F) |
125 | #define EV6__C_STS__SHARED (1 << 0) |
126 | #define EV6__C_STS__DIRTY (1 << 1) |
127 | #define EV6__C_STS__VALID (1 << 2) |
128 | #define EV6__C_STS__PARITY (1 << 3) |
129 | |
130 | if (!(c_stat & EV6__C_STAT__ERRMASK)) |
131 | return MCHK_DISPOSITION_UNKNOWN_ERROR; |
132 | |
133 | if (!print) |
134 | return status; |
135 | |
136 | source = EXTRACT(c_stat, EV6__C_STAT__SOURCE); |
137 | stream = EXTRACT(c_stat, EV6__C_STAT__ISTREAM); |
138 | bits = EXTRACT(c_stat, EV6__C_STAT__DOUBLE); |
139 | |
140 | if (c_stat & EV6__C_STAT__BC_PERR) { |
141 | printk("%s Bcache tag parity error\n" , err_print_prefix); |
142 | source = -1; |
143 | } |
144 | |
145 | if (c_stat & EV6__C_STAT__DC_PERR) { |
146 | printk("%s Dcache tag parity error\n" , err_print_prefix); |
147 | source = -1; |
148 | } |
149 | |
150 | if (c_stat == EV6__C_STAT__PROBE_BC_ERR0 || |
151 | c_stat == EV6__C_STAT__PROBE_BC_ERR1) { |
152 | printk("%s Bcache single-bit error on a probe hit\n" , |
153 | err_print_prefix); |
154 | source = -1; |
155 | } |
156 | |
157 | if (source != -1) |
158 | printk("%s %s-STREAM %s-BIT ECC error from %s\n" , |
159 | err_print_prefix, |
160 | streamname[stream], bitsname[bits], sourcename[source]); |
161 | |
162 | printk("%s Address: 0x%016llx\n" |
163 | " Syndrome[upper.lower]: %02llx.%02llx\n" , |
164 | err_print_prefix, |
165 | c_addr, |
166 | c2_syn, c1_syn); |
167 | |
168 | if (source == EV6__C_STAT__SOURCE_MEMORY || |
169 | source == EV6__C_STAT__SOURCE_BCACHE) |
170 | printk("%s Block status: %s%s%s%s\n" , |
171 | err_print_prefix, |
172 | (c_sts & EV6__C_STS__SHARED) ? "SHARED " : "" , |
173 | (c_sts & EV6__C_STS__DIRTY) ? "DIRTY " : "" , |
174 | (c_sts & EV6__C_STS__VALID) ? "VALID " : "" , |
175 | (c_sts & EV6__C_STS__PARITY) ? "PARITY " : "" ); |
176 | |
177 | return status; |
178 | } |
179 | |
180 | void |
181 | ev6_register_error_handlers(void) |
182 | { |
183 | /* None right now. */ |
184 | } |
185 | |
186 | int |
187 | ev6_process_logout_frame(struct el_common *, int print) |
188 | { |
189 | struct el_common_EV6_mcheck *ev6mchk = |
190 | (struct el_common_EV6_mcheck *)mchk_header; |
191 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; |
192 | |
193 | status |= ev6_parse_ibox(i_stat: ev6mchk->I_STAT, print); |
194 | status |= ev6_parse_mbox(mm_stat: ev6mchk->MM_STAT, d_stat: ev6mchk->DC_STAT, |
195 | c_stat: ev6mchk->C_STAT, print); |
196 | status |= ev6_parse_cbox(c_addr: ev6mchk->C_ADDR, c1_syn: ev6mchk->DC1_SYNDROME, |
197 | c2_syn: ev6mchk->DC0_SYNDROME, c_stat: ev6mchk->C_STAT, |
198 | c_sts: ev6mchk->C_STS, print); |
199 | |
200 | if (!print) |
201 | return status; |
202 | |
203 | if (status != MCHK_DISPOSITION_DISMISS) { |
204 | char *saved_err_prefix = err_print_prefix; |
205 | |
206 | /* |
207 | * Dump some additional information from the frame |
208 | */ |
209 | printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" |
210 | " ISUM: 0x%016lx\n" |
211 | " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" |
212 | " PCTX: 0x%016lx\n" , |
213 | err_print_prefix, |
214 | ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, |
215 | ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); |
216 | |
217 | if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) { |
218 | printk("%s UNKNOWN error, frame follows:\n" , |
219 | err_print_prefix); |
220 | } else { |
221 | /* had decode -- downgrade print level for frame */ |
222 | err_print_prefix = KERN_NOTICE; |
223 | } |
224 | |
225 | mchk_dump_logout_frame(mchk_header); |
226 | |
227 | err_print_prefix = saved_err_prefix; |
228 | } |
229 | |
230 | return status; |
231 | } |
232 | |
233 | void |
234 | ev6_machine_check(unsigned long vector, unsigned long la_ptr) |
235 | { |
236 | struct el_common * = (struct el_common *)la_ptr; |
237 | |
238 | /* |
239 | * Sync the processor |
240 | */ |
241 | mb(); |
242 | draina(); |
243 | |
244 | /* |
245 | * Parse the logout frame without printing first. If the only error(s) |
246 | * found are have a disposition of "dismiss", then just dismiss them |
247 | * and don't print any message |
248 | */ |
249 | if (ev6_process_logout_frame(mchk_header, 0) != |
250 | MCHK_DISPOSITION_DISMISS) { |
251 | char *saved_err_prefix = err_print_prefix; |
252 | err_print_prefix = KERN_CRIT; |
253 | |
254 | /* |
255 | * Either a nondismissable error was detected or no |
256 | * recognized error was detected in the logout frame |
257 | * -- report the error in either case |
258 | */ |
259 | printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d:\n" , |
260 | err_print_prefix, |
261 | (vector == SCB_Q_PROCERR)?"Correctable" :"Uncorrectable" , |
262 | (unsigned int)vector, (int)smp_processor_id()); |
263 | |
264 | ev6_process_logout_frame(mchk_header, 1); |
265 | dik_show_regs(regs: get_irq_regs(), NULL); |
266 | |
267 | err_print_prefix = saved_err_prefix; |
268 | } |
269 | |
270 | /* |
271 | * Release the logout frame |
272 | */ |
273 | wrmces(mces: 0x7); |
274 | mb(); |
275 | } |
276 | |
277 | |