diff options
Diffstat (limited to 'arch/alpha/kernel/err_common.c')
-rw-r--r-- | arch/alpha/kernel/err_common.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/arch/alpha/kernel/err_common.c b/arch/alpha/kernel/err_common.c new file mode 100644 index 000000000000..687580b16b41 --- /dev/null +++ b/arch/alpha/kernel/err_common.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_common.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Error handling code supporting Alpha systems | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/sched.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/hwrpb.h> | ||
15 | #include <asm/smp.h> | ||
16 | #include <asm/err_common.h> | ||
17 | |||
18 | #include "err_impl.h" | ||
19 | #include "proto.h" | ||
20 | |||
21 | /* | ||
22 | * err_print_prefix -- error handling print routines should prefix | ||
23 | * all prints with this | ||
24 | */ | ||
25 | char *err_print_prefix = KERN_NOTICE; | ||
26 | |||
27 | |||
28 | /* | ||
29 | * Generic | ||
30 | */ | ||
31 | void | ||
32 | mchk_dump_mem(void *data, size_t length, char **annotation) | ||
33 | { | ||
34 | unsigned long *ldata = data; | ||
35 | size_t i; | ||
36 | |||
37 | for (i = 0; (i * sizeof(*ldata)) < length; i++) { | ||
38 | if (annotation && !annotation[i]) | ||
39 | annotation = NULL; | ||
40 | printk("%s %08x: %016lx %s\n", | ||
41 | err_print_prefix, | ||
42 | (unsigned)(i * sizeof(*ldata)), ldata[i], | ||
43 | annotation ? annotation[i] : ""); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | void | ||
48 | mchk_dump_logout_frame(struct el_common *mchk_header) | ||
49 | { | ||
50 | printk("%s -- Frame Header --\n" | ||
51 | " Frame Size: %d (0x%x) bytes\n" | ||
52 | " Flags: %s%s\n" | ||
53 | " MCHK Code: 0x%x\n" | ||
54 | " Frame Rev: %d\n" | ||
55 | " Proc Offset: 0x%08x\n" | ||
56 | " Sys Offset: 0x%08x\n" | ||
57 | " -- Processor Region --\n", | ||
58 | err_print_prefix, | ||
59 | mchk_header->size, mchk_header->size, | ||
60 | mchk_header->retry ? "RETRY " : "", | ||
61 | mchk_header->err2 ? "SECOND_ERR " : "", | ||
62 | mchk_header->code, | ||
63 | mchk_header->frame_rev, | ||
64 | mchk_header->proc_offset, | ||
65 | mchk_header->sys_offset); | ||
66 | |||
67 | mchk_dump_mem((void *) | ||
68 | ((unsigned long)mchk_header + mchk_header->proc_offset), | ||
69 | mchk_header->sys_offset - mchk_header->proc_offset, | ||
70 | NULL); | ||
71 | |||
72 | printk("%s -- System Region --\n", err_print_prefix); | ||
73 | mchk_dump_mem((void *) | ||
74 | ((unsigned long)mchk_header + mchk_header->sys_offset), | ||
75 | mchk_header->size - mchk_header->sys_offset, | ||
76 | NULL); | ||
77 | printk("%s -- End of Frame --\n", err_print_prefix); | ||
78 | } | ||
79 | |||
80 | |||
81 | /* | ||
82 | * Console Data Log | ||
83 | */ | ||
84 | /* Data */ | ||
85 | static struct el_subpacket_handler *subpacket_handler_list = NULL; | ||
86 | static struct el_subpacket_annotation *subpacket_annotation_list = NULL; | ||
87 | |||
88 | static struct el_subpacket * | ||
89 | el_process_header_subpacket(struct el_subpacket *header) | ||
90 | { | ||
91 | union el_timestamp timestamp; | ||
92 | char *name = "UNKNOWN EVENT"; | ||
93 | int packet_count = 0; | ||
94 | int length = 0; | ||
95 | |||
96 | if (header->class != EL_CLASS__HEADER) { | ||
97 | printk("%s** Unexpected header CLASS %d TYPE %d, aborting\n", | ||
98 | err_print_prefix, | ||
99 | header->class, header->type); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | switch(header->type) { | ||
104 | case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME: | ||
105 | name = "SYSTEM ERROR"; | ||
106 | length = header->by_type.sys_err.frame_length; | ||
107 | packet_count = | ||
108 | header->by_type.sys_err.frame_packet_count; | ||
109 | timestamp.as_int = 0; | ||
110 | break; | ||
111 | case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME: | ||
112 | name = "SYSTEM EVENT"; | ||
113 | length = header->by_type.sys_event.frame_length; | ||
114 | packet_count = | ||
115 | header->by_type.sys_event.frame_packet_count; | ||
116 | timestamp = header->by_type.sys_event.timestamp; | ||
117 | break; | ||
118 | case EL_TYPE__HEADER__HALT_FRAME: | ||
119 | name = "ERROR HALT"; | ||
120 | length = header->by_type.err_halt.frame_length; | ||
121 | packet_count = | ||
122 | header->by_type.err_halt.frame_packet_count; | ||
123 | timestamp = header->by_type.err_halt.timestamp; | ||
124 | break; | ||
125 | case EL_TYPE__HEADER__LOGOUT_FRAME: | ||
126 | name = "LOGOUT FRAME"; | ||
127 | length = header->by_type.logout_header.frame_length; | ||
128 | packet_count = 1; | ||
129 | timestamp.as_int = 0; | ||
130 | break; | ||
131 | default: /* Unknown */ | ||
132 | printk("%s** Unknown header - CLASS %d TYPE %d, aborting\n", | ||
133 | err_print_prefix, | ||
134 | header->class, header->type); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | printk("%s*** %s:\n" | ||
139 | " CLASS %d, TYPE %d\n", | ||
140 | err_print_prefix, | ||
141 | name, | ||
142 | header->class, header->type); | ||
143 | el_print_timestamp(×tamp); | ||
144 | |||
145 | /* | ||
146 | * Process the subpackets | ||
147 | */ | ||
148 | el_process_subpackets(header, packet_count); | ||
149 | |||
150 | /* return the next header */ | ||
151 | header = (struct el_subpacket *) | ||
152 | ((unsigned long)header + header->length + length); | ||
153 | return header; | ||
154 | } | ||
155 | |||
156 | static struct el_subpacket * | ||
157 | el_process_subpacket_reg(struct el_subpacket *header) | ||
158 | { | ||
159 | struct el_subpacket *next = NULL; | ||
160 | struct el_subpacket_handler *h = subpacket_handler_list; | ||
161 | |||
162 | for (; h && h->class != header->class; h = h->next); | ||
163 | if (h) next = h->handler(header); | ||
164 | |||
165 | return next; | ||
166 | } | ||
167 | |||
168 | void | ||
169 | el_print_timestamp(union el_timestamp *timestamp) | ||
170 | { | ||
171 | if (timestamp->as_int) | ||
172 | printk("%s TIMESTAMP: %d/%d/%02d %d:%02d:%0d\n", | ||
173 | err_print_prefix, | ||
174 | timestamp->b.month, timestamp->b.day, | ||
175 | timestamp->b.year, timestamp->b.hour, | ||
176 | timestamp->b.minute, timestamp->b.second); | ||
177 | } | ||
178 | |||
179 | void | ||
180 | el_process_subpackets(struct el_subpacket *header, int packet_count) | ||
181 | { | ||
182 | struct el_subpacket *subpacket; | ||
183 | int i; | ||
184 | |||
185 | subpacket = (struct el_subpacket *) | ||
186 | ((unsigned long)header + header->length); | ||
187 | |||
188 | for (i = 0; subpacket && i < packet_count; i++) { | ||
189 | printk("%sPROCESSING SUBPACKET %d\n", err_print_prefix, i); | ||
190 | subpacket = el_process_subpacket(subpacket); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | struct el_subpacket * | ||
195 | el_process_subpacket(struct el_subpacket *header) | ||
196 | { | ||
197 | struct el_subpacket *next = NULL; | ||
198 | |||
199 | switch(header->class) { | ||
200 | case EL_CLASS__TERMINATION: | ||
201 | /* Termination packet, there are no more */ | ||
202 | break; | ||
203 | case EL_CLASS__HEADER: | ||
204 | next = el_process_header_subpacket(header); | ||
205 | break; | ||
206 | default: | ||
207 | if (NULL == (next = el_process_subpacket_reg(header))) { | ||
208 | printk("%s** Unexpected header CLASS %d TYPE %d" | ||
209 | " -- aborting.\n", | ||
210 | err_print_prefix, | ||
211 | header->class, header->type); | ||
212 | } | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | return next; | ||
217 | } | ||
218 | |||
219 | void | ||
220 | el_annotate_subpacket(struct el_subpacket *header) | ||
221 | { | ||
222 | struct el_subpacket_annotation *a; | ||
223 | char **annotation = NULL; | ||
224 | |||
225 | for (a = subpacket_annotation_list; a; a = a->next) { | ||
226 | if (a->class == header->class && | ||
227 | a->type == header->type && | ||
228 | a->revision == header->revision) { | ||
229 | /* | ||
230 | * We found the annotation | ||
231 | */ | ||
232 | annotation = a->annotation; | ||
233 | printk("%s %s\n", err_print_prefix, a->description); | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | mchk_dump_mem(header, header->length, annotation); | ||
239 | } | ||
240 | |||
241 | static void __init | ||
242 | cdl_process_console_data_log(int cpu, struct percpu_struct *pcpu) | ||
243 | { | ||
244 | struct el_subpacket *header = (struct el_subpacket *) | ||
245 | (IDENT_ADDR | pcpu->console_data_log_pa); | ||
246 | int err; | ||
247 | |||
248 | printk("%s******* CONSOLE DATA LOG FOR CPU %d. *******\n" | ||
249 | "*** Error(s) were logged on a previous boot\n", | ||
250 | err_print_prefix, cpu); | ||
251 | |||
252 | for (err = 0; header && (header->class != EL_CLASS__TERMINATION); err++) | ||
253 | header = el_process_subpacket(header); | ||
254 | |||
255 | /* let the console know it's ok to clear the error(s) at restart */ | ||
256 | pcpu->console_data_log_pa = 0; | ||
257 | |||
258 | printk("%s*** %d total error(s) logged\n" | ||
259 | "**** END OF CONSOLE DATA LOG FOR CPU %d ****\n", | ||
260 | err_print_prefix, err, cpu); | ||
261 | } | ||
262 | |||
263 | void __init | ||
264 | cdl_check_console_data_log(void) | ||
265 | { | ||
266 | struct percpu_struct *pcpu; | ||
267 | unsigned long cpu; | ||
268 | |||
269 | for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) { | ||
270 | pcpu = (struct percpu_struct *) | ||
271 | ((unsigned long)hwrpb + hwrpb->processor_offset | ||
272 | + cpu * hwrpb->processor_size); | ||
273 | if (pcpu->console_data_log_pa) | ||
274 | cdl_process_console_data_log(cpu, pcpu); | ||
275 | } | ||
276 | |||
277 | } | ||
278 | |||
279 | int __init | ||
280 | cdl_register_subpacket_annotation(struct el_subpacket_annotation *new) | ||
281 | { | ||
282 | struct el_subpacket_annotation *a = subpacket_annotation_list; | ||
283 | |||
284 | if (a == NULL) subpacket_annotation_list = new; | ||
285 | else { | ||
286 | for (; a->next != NULL; a = a->next) { | ||
287 | if ((a->class == new->class && a->type == new->type) || | ||
288 | a == new) { | ||
289 | printk("Attempted to re-register " | ||
290 | "subpacket annotation\n"); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | } | ||
294 | a->next = new; | ||
295 | } | ||
296 | new->next = NULL; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | int __init | ||
302 | cdl_register_subpacket_handler(struct el_subpacket_handler *new) | ||
303 | { | ||
304 | struct el_subpacket_handler *h = subpacket_handler_list; | ||
305 | |||
306 | if (h == NULL) subpacket_handler_list = new; | ||
307 | else { | ||
308 | for (; h->next != NULL; h = h->next) { | ||
309 | if (h->class == new->class || h == new) { | ||
310 | printk("Attempted to re-register " | ||
311 | "subpacket handler\n"); | ||
312 | return -EINVAL; | ||
313 | } | ||
314 | } | ||
315 | h->next = new; | ||
316 | } | ||
317 | new->next = NULL; | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||