Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * c 2001 PPC 64 Team, IBM Corp | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * /dev/nvram driver for PPC64 | |
10 | * | |
11 | * This perhaps should live in drivers/char | |
12 | */ | |
13 | ||
14 | ||
15 | #include <linux/types.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/init.h> | |
1da177e4 | 18 | #include <linux/spinlock.h> |
a5cf4b08 JK |
19 | #include <linux/slab.h> |
20 | #include <linux/kmsg_dump.h> | |
d7563c94 | 21 | #include <linux/pstore.h> |
6c493685 JK |
22 | #include <linux/ctype.h> |
23 | #include <linux/zlib.h> | |
1da177e4 LT |
24 | #include <asm/uaccess.h> |
25 | #include <asm/nvram.h> | |
26 | #include <asm/rtas.h> | |
27 | #include <asm/prom.h> | |
28 | #include <asm/machdep.h> | |
29 | ||
4e7c77a3 BH |
30 | /* Max bytes to read/write in one go */ |
31 | #define NVRW_CNT 0x20 | |
32 | ||
b1f70e1f AB |
33 | /* |
34 | * Set oops header version to distingush between old and new format header. | |
35 | * lnx,oops-log partition max size is 4000, header version > 4000 will | |
36 | * help in identifying new header. | |
37 | */ | |
38 | #define OOPS_HDR_VERSION 5000 | |
39 | ||
1da177e4 LT |
40 | static unsigned int nvram_size; |
41 | static int nvram_fetch, nvram_store; | |
42 | static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ | |
43 | static DEFINE_SPINLOCK(nvram_lock); | |
44 | ||
edc79a2f BH |
45 | struct err_log_info { |
46 | int error_type; | |
47 | unsigned int seq_num; | |
48 | }; | |
edc79a2f | 49 | |
0f4ac132 JK |
50 | struct nvram_os_partition { |
51 | const char *name; | |
52 | int req_size; /* desired size, in bytes */ | |
53 | int min_size; /* minimum acceptable size (0 means req_size) */ | |
a5cf4b08 | 54 | long size; /* size of data portion (excluding err_log_info) */ |
0f4ac132 | 55 | long index; /* offset of data portion of partition */ |
edf38465 | 56 | bool os_partition; /* partition initialized by OS, not FW */ |
0f4ac132 JK |
57 | }; |
58 | ||
59 | static struct nvram_os_partition rtas_log_partition = { | |
60 | .name = "ibm,rtas-log", | |
61 | .req_size = 2079, | |
62 | .min_size = 1055, | |
edf38465 AB |
63 | .index = -1, |
64 | .os_partition = true | |
0f4ac132 JK |
65 | }; |
66 | ||
a5cf4b08 JK |
67 | static struct nvram_os_partition oops_log_partition = { |
68 | .name = "lnx,oops-log", | |
69 | .req_size = 4000, | |
70 | .min_size = 2000, | |
edf38465 AB |
71 | .index = -1, |
72 | .os_partition = true | |
a5cf4b08 JK |
73 | }; |
74 | ||
0f4ac132 JK |
75 | static const char *pseries_nvram_os_partitions[] = { |
76 | "ibm,rtas-log", | |
a5cf4b08 | 77 | "lnx,oops-log", |
0f4ac132 JK |
78 | NULL |
79 | }; | |
9a866b87 | 80 | |
b1f70e1f AB |
81 | struct oops_log_info { |
82 | u16 version; | |
83 | u16 report_length; | |
84 | u64 timestamp; | |
85 | } __attribute__((packed)); | |
86 | ||
a5cf4b08 | 87 | static void oops_to_nvram(struct kmsg_dumper *dumper, |
e2ae715d | 88 | enum kmsg_dump_reason reason); |
a5cf4b08 JK |
89 | |
90 | static struct kmsg_dumper nvram_kmsg_dumper = { | |
91 | .dump = oops_to_nvram | |
92 | }; | |
93 | ||
94 | /* See clobbering_unread_rtas_event() */ | |
95 | #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ | |
96 | static unsigned long last_unread_rtas_event; /* timestamp */ | |
97 | ||
6c493685 JK |
98 | /* |
99 | * For capturing and compressing an oops or panic report... | |
100 | ||
101 | * big_oops_buf[] holds the uncompressed text we're capturing. | |
102 | * | |
b1f70e1f AB |
103 | * oops_buf[] holds the compressed text, preceded by a oops header. |
104 | * oops header has u16 holding the version of oops header (to differentiate | |
105 | * between old and new format header) followed by u16 holding the length of | |
106 | * the compressed* text (*Or uncompressed, if compression fails.) and u64 | |
107 | * holding the timestamp. oops_buf[] gets written to NVRAM. | |
6c493685 | 108 | * |
b1f70e1f | 109 | * oops_log_info points to the header. oops_data points to the compressed text. |
6c493685 JK |
110 | * |
111 | * +- oops_buf | |
b1f70e1f AB |
112 | * | +- oops_data |
113 | * v v | |
114 | * +-----------+-----------+-----------+------------------------+ | |
115 | * | version | length | timestamp | text | | |
116 | * | (2 bytes) | (2 bytes) | (8 bytes) | (oops_data_sz bytes) | | |
117 | * +-----------+-----------+-----------+------------------------+ | |
6c493685 | 118 | * ^ |
b1f70e1f | 119 | * +- oops_log_info |
6c493685 JK |
120 | * |
121 | * We preallocate these buffers during init to avoid kmalloc during oops/panic. | |
122 | */ | |
123 | static size_t big_oops_buf_sz; | |
124 | static char *big_oops_buf, *oops_buf; | |
6c493685 JK |
125 | static char *oops_data; |
126 | static size_t oops_data_sz; | |
127 | ||
128 | /* Compression parameters */ | |
129 | #define COMPR_LEVEL 6 | |
130 | #define WINDOW_BITS 12 | |
131 | #define MEM_LEVEL 4 | |
132 | static struct z_stream_s stream; | |
a5cf4b08 | 133 | |
d7563c94 | 134 | #ifdef CONFIG_PSTORE |
f33f748c AB |
135 | static struct nvram_os_partition of_config_partition = { |
136 | .name = "of-config", | |
137 | .index = -1, | |
138 | .os_partition = false | |
139 | }; | |
140 | ||
a5e4797b AB |
141 | static struct nvram_os_partition common_partition = { |
142 | .name = "common", | |
143 | .index = -1, | |
144 | .os_partition = false | |
145 | }; | |
146 | ||
d7563c94 AB |
147 | static enum pstore_type_id nvram_type_ids[] = { |
148 | PSTORE_TYPE_DMESG, | |
69020eea | 149 | PSTORE_TYPE_PPC_RTAS, |
f33f748c | 150 | PSTORE_TYPE_PPC_OF, |
a5e4797b | 151 | PSTORE_TYPE_PPC_COMMON, |
d7563c94 AB |
152 | -1 |
153 | }; | |
154 | static int read_type; | |
69020eea | 155 | static unsigned long last_rtas_event; |
d7563c94 AB |
156 | #endif |
157 | ||
1da177e4 LT |
158 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) |
159 | { | |
160 | unsigned int i; | |
161 | unsigned long len; | |
162 | int done; | |
163 | unsigned long flags; | |
164 | char *p = buf; | |
165 | ||
166 | ||
167 | if (nvram_size == 0 || nvram_fetch == RTAS_UNKNOWN_SERVICE) | |
168 | return -ENODEV; | |
169 | ||
170 | if (*index >= nvram_size) | |
171 | return 0; | |
172 | ||
173 | i = *index; | |
174 | if (i + count > nvram_size) | |
175 | count = nvram_size - i; | |
176 | ||
177 | spin_lock_irqsave(&nvram_lock, flags); | |
178 | ||
179 | for (; count != 0; count -= len) { | |
180 | len = count; | |
181 | if (len > NVRW_CNT) | |
182 | len = NVRW_CNT; | |
183 | ||
184 | if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf), | |
185 | len) != 0) || len != done) { | |
186 | spin_unlock_irqrestore(&nvram_lock, flags); | |
187 | return -EIO; | |
188 | } | |
189 | ||
190 | memcpy(p, nvram_buf, len); | |
191 | ||
192 | p += len; | |
193 | i += len; | |
194 | } | |
195 | ||
196 | spin_unlock_irqrestore(&nvram_lock, flags); | |
197 | ||
198 | *index = i; | |
199 | return p - buf; | |
200 | } | |
201 | ||
202 | static ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index) | |
203 | { | |
204 | unsigned int i; | |
205 | unsigned long len; | |
206 | int done; | |
207 | unsigned long flags; | |
208 | const char *p = buf; | |
209 | ||
210 | if (nvram_size == 0 || nvram_store == RTAS_UNKNOWN_SERVICE) | |
211 | return -ENODEV; | |
212 | ||
213 | if (*index >= nvram_size) | |
214 | return 0; | |
215 | ||
216 | i = *index; | |
217 | if (i + count > nvram_size) | |
218 | count = nvram_size - i; | |
219 | ||
220 | spin_lock_irqsave(&nvram_lock, flags); | |
221 | ||
222 | for (; count != 0; count -= len) { | |
223 | len = count; | |
224 | if (len > NVRW_CNT) | |
225 | len = NVRW_CNT; | |
226 | ||
227 | memcpy(nvram_buf, p, len); | |
228 | ||
229 | if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf), | |
230 | len) != 0) || len != done) { | |
231 | spin_unlock_irqrestore(&nvram_lock, flags); | |
232 | return -EIO; | |
233 | } | |
234 | ||
235 | p += len; | |
236 | i += len; | |
237 | } | |
238 | spin_unlock_irqrestore(&nvram_lock, flags); | |
239 | ||
240 | *index = i; | |
241 | return p - buf; | |
242 | } | |
243 | ||
244 | static ssize_t pSeries_nvram_get_size(void) | |
245 | { | |
246 | return nvram_size ? nvram_size : -ENODEV; | |
247 | } | |
248 | ||
edc79a2f | 249 | |
0f4ac132 | 250 | /* nvram_write_os_partition, nvram_write_error_log |
edc79a2f BH |
251 | * |
252 | * We need to buffer the error logs into nvram to ensure that we have | |
253 | * the failure information to decode. If we have a severe error there | |
254 | * is no way to guarantee that the OS or the machine is in a state to | |
255 | * get back to user land and write the error to disk. For example if | |
256 | * the SCSI device driver causes a Machine Check by writing to a bad | |
257 | * IO address, there is no way of guaranteeing that the device driver | |
258 | * is in any state that is would also be able to write the error data | |
259 | * captured to disk, thus we buffer it in NVRAM for analysis on the | |
260 | * next boot. | |
261 | * | |
262 | * In NVRAM the partition containing the error log buffer will looks like: | |
263 | * Header (in bytes): | |
264 | * +-----------+----------+--------+------------+------------------+ | |
265 | * | signature | checksum | length | name | data | | |
266 | * |0 |1 |2 3|4 15|16 length-1| | |
267 | * +-----------+----------+--------+------------+------------------+ | |
268 | * | |
269 | * The 'data' section would look like (in bytes): | |
270 | * +--------------+------------+-----------------------------------+ | |
271 | * | event_logged | sequence # | error log | | |
0f4ac132 | 272 | * |0 3|4 7|8 error_log_size-1| |
edc79a2f BH |
273 | * +--------------+------------+-----------------------------------+ |
274 | * | |
275 | * event_logged: 0 if event has not been logged to syslog, 1 if it has | |
276 | * sequence #: The unique sequence # for each event. (until it wraps) | |
277 | * error log: The error log from event_scan | |
278 | */ | |
0f4ac132 JK |
279 | int nvram_write_os_partition(struct nvram_os_partition *part, char * buff, |
280 | int length, unsigned int err_type, unsigned int error_log_cnt) | |
edc79a2f BH |
281 | { |
282 | int rc; | |
283 | loff_t tmp_index; | |
284 | struct err_log_info info; | |
285 | ||
0f4ac132 | 286 | if (part->index == -1) { |
edc79a2f BH |
287 | return -ESPIPE; |
288 | } | |
289 | ||
0f4ac132 JK |
290 | if (length > part->size) { |
291 | length = part->size; | |
edc79a2f BH |
292 | } |
293 | ||
294 | info.error_type = err_type; | |
295 | info.seq_num = error_log_cnt; | |
296 | ||
0f4ac132 | 297 | tmp_index = part->index; |
edc79a2f BH |
298 | |
299 | rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); | |
300 | if (rc <= 0) { | |
0f4ac132 | 301 | pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc); |
edc79a2f BH |
302 | return rc; |
303 | } | |
304 | ||
305 | rc = ppc_md.nvram_write(buff, length, &tmp_index); | |
306 | if (rc <= 0) { | |
0f4ac132 | 307 | pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc); |
edc79a2f BH |
308 | return rc; |
309 | } | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
0f4ac132 JK |
314 | int nvram_write_error_log(char * buff, int length, |
315 | unsigned int err_type, unsigned int error_log_cnt) | |
316 | { | |
a5cf4b08 | 317 | int rc = nvram_write_os_partition(&rtas_log_partition, buff, length, |
0f4ac132 | 318 | err_type, error_log_cnt); |
69020eea | 319 | if (!rc) { |
a5cf4b08 | 320 | last_unread_rtas_event = get_seconds(); |
69020eea AB |
321 | #ifdef CONFIG_PSTORE |
322 | last_rtas_event = get_seconds(); | |
323 | #endif | |
324 | } | |
325 | ||
a5cf4b08 | 326 | return rc; |
0f4ac132 JK |
327 | } |
328 | ||
12674610 | 329 | /* nvram_read_partition |
edc79a2f | 330 | * |
12674610 | 331 | * Reads nvram partition for at most 'length' |
edc79a2f | 332 | */ |
12674610 AB |
333 | int nvram_read_partition(struct nvram_os_partition *part, char *buff, |
334 | int length, unsigned int *err_type, | |
335 | unsigned int *error_log_cnt) | |
edc79a2f BH |
336 | { |
337 | int rc; | |
338 | loff_t tmp_index; | |
339 | struct err_log_info info; | |
340 | ||
12674610 | 341 | if (part->index == -1) |
edc79a2f BH |
342 | return -1; |
343 | ||
12674610 AB |
344 | if (length > part->size) |
345 | length = part->size; | |
edc79a2f | 346 | |
12674610 | 347 | tmp_index = part->index; |
edc79a2f | 348 | |
f33f748c AB |
349 | if (part->os_partition) { |
350 | rc = ppc_md.nvram_read((char *)&info, | |
351 | sizeof(struct err_log_info), | |
352 | &tmp_index); | |
353 | if (rc <= 0) { | |
354 | pr_err("%s: Failed nvram_read (%d)\n", __FUNCTION__, | |
355 | rc); | |
356 | return rc; | |
357 | } | |
edc79a2f BH |
358 | } |
359 | ||
360 | rc = ppc_md.nvram_read(buff, length, &tmp_index); | |
361 | if (rc <= 0) { | |
12674610 | 362 | pr_err("%s: Failed nvram_read (%d)\n", __FUNCTION__, rc); |
edc79a2f BH |
363 | return rc; |
364 | } | |
365 | ||
f33f748c AB |
366 | if (part->os_partition) { |
367 | *error_log_cnt = info.seq_num; | |
368 | *err_type = info.error_type; | |
369 | } | |
edc79a2f BH |
370 | |
371 | return 0; | |
372 | } | |
373 | ||
12674610 AB |
374 | /* nvram_read_error_log |
375 | * | |
376 | * Reads nvram for error log for at most 'length' | |
377 | */ | |
378 | int nvram_read_error_log(char *buff, int length, | |
379 | unsigned int *err_type, unsigned int *error_log_cnt) | |
380 | { | |
381 | return nvram_read_partition(&rtas_log_partition, buff, length, | |
382 | err_type, error_log_cnt); | |
383 | } | |
384 | ||
edc79a2f BH |
385 | /* This doesn't actually zero anything, but it sets the event_logged |
386 | * word to tell that this event is safely in syslog. | |
387 | */ | |
388 | int nvram_clear_error_log(void) | |
389 | { | |
390 | loff_t tmp_index; | |
391 | int clear_word = ERR_FLAG_ALREADY_LOGGED; | |
392 | int rc; | |
393 | ||
0f4ac132 | 394 | if (rtas_log_partition.index == -1) |
edc79a2f BH |
395 | return -1; |
396 | ||
0f4ac132 | 397 | tmp_index = rtas_log_partition.index; |
edc79a2f BH |
398 | |
399 | rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); | |
400 | if (rc <= 0) { | |
401 | printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc); | |
402 | return rc; | |
403 | } | |
a5cf4b08 | 404 | last_unread_rtas_event = 0; |
edc79a2f BH |
405 | |
406 | return 0; | |
407 | } | |
408 | ||
0f4ac132 | 409 | /* pseries_nvram_init_os_partition |
edc79a2f | 410 | * |
0f4ac132 | 411 | * This sets up a partition with an "OS" signature. |
edc79a2f BH |
412 | * |
413 | * The general strategy is the following: | |
0f4ac132 JK |
414 | * 1.) If a partition with the indicated name already exists... |
415 | * - If it's large enough, use it. | |
416 | * - Otherwise, recycle it and keep going. | |
417 | * 2.) Search for a free partition that is large enough. | |
418 | * 3.) If there's not a free partition large enough, recycle any obsolete | |
419 | * OS partitions and try again. | |
420 | * 4.) Will first try getting a chunk that will satisfy the requested size. | |
421 | * 5.) If a chunk of the requested size cannot be allocated, then try finding | |
422 | * a chunk that will satisfy the minum needed. | |
423 | * | |
424 | * Returns 0 on success, else -1. | |
edc79a2f | 425 | */ |
0f4ac132 JK |
426 | static int __init pseries_nvram_init_os_partition(struct nvram_os_partition |
427 | *part) | |
edc79a2f BH |
428 | { |
429 | loff_t p; | |
430 | int size; | |
431 | ||
432 | /* Scan nvram for partitions */ | |
433 | nvram_scan_partitions(); | |
434 | ||
0f4ac132 JK |
435 | /* Look for ours */ |
436 | p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size); | |
edc79a2f BH |
437 | |
438 | /* Found one but too small, remove it */ | |
0f4ac132 JK |
439 | if (p && size < part->min_size) { |
440 | pr_info("nvram: Found too small %s partition," | |
441 | " removing it...\n", part->name); | |
442 | nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL); | |
edc79a2f BH |
443 | p = 0; |
444 | } | |
445 | ||
446 | /* Create one if we didn't find */ | |
447 | if (!p) { | |
0f4ac132 JK |
448 | p = nvram_create_partition(part->name, NVRAM_SIG_OS, |
449 | part->req_size, part->min_size); | |
edc79a2f | 450 | if (p == -ENOSPC) { |
0f4ac132 JK |
451 | pr_info("nvram: No room to create %s partition, " |
452 | "deleting any obsolete OS partitions...\n", | |
453 | part->name); | |
454 | nvram_remove_partition(NULL, NVRAM_SIG_OS, | |
455 | pseries_nvram_os_partitions); | |
456 | p = nvram_create_partition(part->name, NVRAM_SIG_OS, | |
457 | part->req_size, part->min_size); | |
edc79a2f BH |
458 | } |
459 | } | |
460 | ||
461 | if (p <= 0) { | |
0f4ac132 JK |
462 | pr_err("nvram: Failed to find or create %s" |
463 | " partition, err %d\n", part->name, (int)p); | |
464 | return -1; | |
edc79a2f BH |
465 | } |
466 | ||
0f4ac132 JK |
467 | part->index = p; |
468 | part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info); | |
edc79a2f BH |
469 | |
470 | return 0; | |
471 | } | |
0f4ac132 | 472 | |
d7563c94 AB |
473 | /* |
474 | * Are we using the ibm,rtas-log for oops/panic reports? And if so, | |
475 | * would logging this oops/panic overwrite an RTAS event that rtas_errd | |
476 | * hasn't had a chance to read and process? Return 1 if so, else 0. | |
477 | * | |
478 | * We assume that if rtas_errd hasn't read the RTAS event in | |
479 | * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to. | |
480 | */ | |
481 | static int clobbering_unread_rtas_event(void) | |
482 | { | |
483 | return (oops_log_partition.index == rtas_log_partition.index | |
484 | && last_unread_rtas_event | |
485 | && get_seconds() - last_unread_rtas_event <= | |
486 | NVRAM_RTAS_READ_TIMEOUT); | |
487 | } | |
488 | ||
fbfe86fc AB |
489 | /* Derived from logfs_compress() */ |
490 | static int nvram_compress(const void *in, void *out, size_t inlen, | |
491 | size_t outlen) | |
492 | { | |
493 | int err, ret; | |
494 | ||
495 | ret = -EIO; | |
496 | err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, | |
497 | MEM_LEVEL, Z_DEFAULT_STRATEGY); | |
498 | if (err != Z_OK) | |
499 | goto error; | |
500 | ||
501 | stream.next_in = in; | |
502 | stream.avail_in = inlen; | |
503 | stream.total_in = 0; | |
504 | stream.next_out = out; | |
505 | stream.avail_out = outlen; | |
506 | stream.total_out = 0; | |
507 | ||
508 | err = zlib_deflate(&stream, Z_FINISH); | |
509 | if (err != Z_STREAM_END) | |
510 | goto error; | |
511 | ||
512 | err = zlib_deflateEnd(&stream); | |
513 | if (err != Z_OK) | |
514 | goto error; | |
515 | ||
516 | if (stream.total_out >= stream.total_in) | |
517 | goto error; | |
518 | ||
519 | ret = stream.total_out; | |
520 | error: | |
521 | return ret; | |
522 | } | |
523 | ||
524 | /* Compress the text from big_oops_buf into oops_buf. */ | |
525 | static int zip_oops(size_t text_len) | |
526 | { | |
527 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; | |
528 | int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, | |
529 | oops_data_sz); | |
530 | if (zipped_len < 0) { | |
531 | pr_err("nvram: compression failed; returned %d\n", zipped_len); | |
532 | pr_err("nvram: logging uncompressed oops/panic report\n"); | |
533 | return -1; | |
534 | } | |
535 | oops_hdr->version = OOPS_HDR_VERSION; | |
536 | oops_hdr->report_length = (u16) zipped_len; | |
537 | oops_hdr->timestamp = get_seconds(); | |
538 | return 0; | |
539 | } | |
540 | ||
d7563c94 AB |
541 | #ifdef CONFIG_PSTORE |
542 | static int nvram_pstore_open(struct pstore_info *psi) | |
543 | { | |
544 | /* Reset the iterator to start reading partitions again */ | |
545 | read_type = -1; | |
546 | return 0; | |
547 | } | |
548 | ||
549 | /** | |
550 | * nvram_pstore_write - pstore write callback for nvram | |
551 | * @type: Type of message logged | |
552 | * @reason: reason behind dump (oops/panic) | |
553 | * @id: identifier to indicate the write performed | |
554 | * @part: pstore writes data to registered buffer in parts, | |
555 | * part number will indicate the same. | |
556 | * @count: Indicates oops count | |
b3b515bb | 557 | * @compressed: Flag to indicate the log is compressed |
d7563c94 AB |
558 | * @size: number of bytes written to the registered buffer |
559 | * @psi: registered pstore_info structure | |
560 | * | |
561 | * Called by pstore_dump() when an oops or panic report is logged in the | |
562 | * printk buffer. | |
563 | * Returns 0 on successful write. | |
564 | */ | |
565 | static int nvram_pstore_write(enum pstore_type_id type, | |
566 | enum kmsg_dump_reason reason, | |
567 | u64 *id, unsigned int part, int count, | |
b3b515bb | 568 | bool compressed, size_t size, |
6bbbca73 | 569 | struct pstore_info *psi) |
d7563c94 AB |
570 | { |
571 | int rc; | |
40847e56 | 572 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC; |
d7563c94 AB |
573 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; |
574 | ||
575 | /* part 1 has the recent messages from printk buffer */ | |
576 | if (part > 1 || type != PSTORE_TYPE_DMESG || | |
577 | clobbering_unread_rtas_event()) | |
578 | return -1; | |
579 | ||
580 | oops_hdr->version = OOPS_HDR_VERSION; | |
581 | oops_hdr->report_length = (u16) size; | |
582 | oops_hdr->timestamp = get_seconds(); | |
40847e56 | 583 | |
40594264 AB |
584 | if (compressed) |
585 | err_type = ERR_TYPE_KERNEL_PANIC_GZ; | |
586 | ||
d7563c94 | 587 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, |
a3a5e941 | 588 | (int) (sizeof(*oops_hdr) + size), err_type, count); |
d7563c94 AB |
589 | |
590 | if (rc != 0) | |
591 | return rc; | |
592 | ||
593 | *id = part; | |
594 | return 0; | |
595 | } | |
596 | ||
597 | /* | |
a5e4797b | 598 | * Reads the oops/panic report, rtas, of-config and common partition. |
d7563c94 AB |
599 | * Returns the length of the data we read from each partition. |
600 | * Returns 0 if we've been called before. | |
601 | */ | |
602 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | |
603 | int *count, struct timespec *time, char **buf, | |
9a4e1398 | 604 | bool *compressed, struct pstore_info *psi) |
d7563c94 AB |
605 | { |
606 | struct oops_log_info *oops_hdr; | |
f33f748c | 607 | unsigned int err_type, id_no, size = 0; |
d7563c94 | 608 | struct nvram_os_partition *part = NULL; |
a3a5e941 | 609 | char *buff = NULL; |
7e76f34f | 610 | int sig = 0; |
f33f748c | 611 | loff_t p; |
d7563c94 AB |
612 | |
613 | read_type++; | |
614 | ||
615 | switch (nvram_type_ids[read_type]) { | |
616 | case PSTORE_TYPE_DMESG: | |
617 | part = &oops_log_partition; | |
618 | *type = PSTORE_TYPE_DMESG; | |
619 | break; | |
69020eea AB |
620 | case PSTORE_TYPE_PPC_RTAS: |
621 | part = &rtas_log_partition; | |
622 | *type = PSTORE_TYPE_PPC_RTAS; | |
623 | time->tv_sec = last_rtas_event; | |
624 | time->tv_nsec = 0; | |
625 | break; | |
f33f748c AB |
626 | case PSTORE_TYPE_PPC_OF: |
627 | sig = NVRAM_SIG_OF; | |
628 | part = &of_config_partition; | |
629 | *type = PSTORE_TYPE_PPC_OF; | |
630 | *id = PSTORE_TYPE_PPC_OF; | |
631 | time->tv_sec = 0; | |
632 | time->tv_nsec = 0; | |
633 | break; | |
a5e4797b AB |
634 | case PSTORE_TYPE_PPC_COMMON: |
635 | sig = NVRAM_SIG_SYS; | |
636 | part = &common_partition; | |
637 | *type = PSTORE_TYPE_PPC_COMMON; | |
638 | *id = PSTORE_TYPE_PPC_COMMON; | |
639 | time->tv_sec = 0; | |
640 | time->tv_nsec = 0; | |
641 | break; | |
d7563c94 AB |
642 | default: |
643 | return 0; | |
644 | } | |
645 | ||
f33f748c AB |
646 | if (!part->os_partition) { |
647 | p = nvram_find_partition(part->name, sig, &size); | |
648 | if (p <= 0) { | |
649 | pr_err("nvram: Failed to find partition %s, " | |
650 | "err %d\n", part->name, (int)p); | |
651 | return 0; | |
652 | } | |
653 | part->index = p; | |
654 | part->size = size; | |
655 | } | |
656 | ||
d7563c94 AB |
657 | buff = kmalloc(part->size, GFP_KERNEL); |
658 | ||
659 | if (!buff) | |
660 | return -ENOMEM; | |
661 | ||
662 | if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { | |
663 | kfree(buff); | |
664 | return 0; | |
665 | } | |
666 | ||
667 | *count = 0; | |
f33f748c AB |
668 | |
669 | if (part->os_partition) | |
670 | *id = id_no; | |
69020eea AB |
671 | |
672 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { | |
a3a5e941 | 673 | size_t length, hdr_size; |
7e76f34f | 674 | |
69020eea | 675 | oops_hdr = (struct oops_log_info *)buff; |
156c9ebd AB |
676 | if (oops_hdr->version < OOPS_HDR_VERSION) { |
677 | /* Old format oops header had 2-byte record size */ | |
678 | hdr_size = sizeof(u16); | |
679 | length = oops_hdr->version; | |
680 | time->tv_sec = 0; | |
681 | time->tv_nsec = 0; | |
682 | } else { | |
683 | hdr_size = sizeof(*oops_hdr); | |
684 | length = oops_hdr->report_length; | |
685 | time->tv_sec = oops_hdr->timestamp; | |
686 | time->tv_nsec = 0; | |
687 | } | |
7e76f34f AB |
688 | *buf = kmalloc(length, GFP_KERNEL); |
689 | if (*buf == NULL) | |
690 | return -ENOMEM; | |
156c9ebd | 691 | memcpy(*buf, buff + hdr_size, length); |
7e76f34f | 692 | kfree(buff); |
40594264 AB |
693 | |
694 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) | |
695 | *compressed = true; | |
696 | else | |
697 | *compressed = false; | |
7e76f34f | 698 | return length; |
69020eea AB |
699 | } |
700 | ||
701 | *buf = buff; | |
702 | return part->size; | |
d7563c94 AB |
703 | } |
704 | ||
705 | static struct pstore_info nvram_pstore_info = { | |
706 | .owner = THIS_MODULE, | |
707 | .name = "nvram", | |
708 | .open = nvram_pstore_open, | |
709 | .read = nvram_pstore_read, | |
710 | .write = nvram_pstore_write, | |
711 | }; | |
712 | ||
713 | static int nvram_pstore_init(void) | |
714 | { | |
715 | int rc = 0; | |
716 | ||
a3a5e941 AB |
717 | nvram_pstore_info.buf = oops_data; |
718 | nvram_pstore_info.bufsize = oops_data_sz; | |
d7563c94 AB |
719 | |
720 | rc = pstore_register(&nvram_pstore_info); | |
721 | if (rc != 0) | |
722 | pr_err("nvram: pstore_register() failed, defaults to " | |
723 | "kmsg_dump; returned %d\n", rc); | |
d7563c94 AB |
724 | |
725 | return rc; | |
726 | } | |
727 | #else | |
728 | static int nvram_pstore_init(void) | |
729 | { | |
730 | return -1; | |
731 | } | |
732 | #endif | |
733 | ||
a5cf4b08 JK |
734 | static void __init nvram_init_oops_partition(int rtas_partition_exists) |
735 | { | |
736 | int rc; | |
737 | ||
738 | rc = pseries_nvram_init_os_partition(&oops_log_partition); | |
739 | if (rc != 0) { | |
740 | if (!rtas_partition_exists) | |
741 | return; | |
742 | pr_notice("nvram: Using %s partition to log both" | |
743 | " RTAS errors and oops/panic reports\n", | |
744 | rtas_log_partition.name); | |
745 | memcpy(&oops_log_partition, &rtas_log_partition, | |
746 | sizeof(rtas_log_partition)); | |
747 | } | |
748 | oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); | |
6c493685 JK |
749 | if (!oops_buf) { |
750 | pr_err("nvram: No memory for %s partition\n", | |
751 | oops_log_partition.name); | |
752 | return; | |
753 | } | |
b1f70e1f AB |
754 | oops_data = oops_buf + sizeof(struct oops_log_info); |
755 | oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); | |
6c493685 | 756 | |
a3a5e941 AB |
757 | rc = nvram_pstore_init(); |
758 | ||
759 | if (!rc) | |
760 | return; | |
761 | ||
6c493685 JK |
762 | /* |
763 | * Figure compression (preceded by elimination of each line's <n> | |
764 | * severity prefix) will reduce the oops/panic report to at most | |
765 | * 45% of its original size. | |
766 | */ | |
767 | big_oops_buf_sz = (oops_data_sz * 100) / 45; | |
768 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | |
769 | if (big_oops_buf) { | |
a3a5e941 AB |
770 | stream.workspace = kmalloc(zlib_deflate_workspacesize( |
771 | WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); | |
6c493685 JK |
772 | if (!stream.workspace) { |
773 | pr_err("nvram: No memory for compression workspace; " | |
774 | "skipping compression of %s partition data\n", | |
775 | oops_log_partition.name); | |
776 | kfree(big_oops_buf); | |
777 | big_oops_buf = NULL; | |
778 | } | |
779 | } else { | |
780 | pr_err("No memory for uncompressed %s data; " | |
781 | "skipping compression\n", oops_log_partition.name); | |
782 | stream.workspace = NULL; | |
783 | } | |
784 | ||
a5cf4b08 JK |
785 | rc = kmsg_dump_register(&nvram_kmsg_dumper); |
786 | if (rc != 0) { | |
787 | pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); | |
788 | kfree(oops_buf); | |
6c493685 JK |
789 | kfree(big_oops_buf); |
790 | kfree(stream.workspace); | |
a5cf4b08 JK |
791 | } |
792 | } | |
793 | ||
0f4ac132 JK |
794 | static int __init pseries_nvram_init_log_partitions(void) |
795 | { | |
a5cf4b08 JK |
796 | int rc; |
797 | ||
798 | rc = pseries_nvram_init_os_partition(&rtas_log_partition); | |
799 | nvram_init_oops_partition(rc == 0); | |
0f4ac132 JK |
800 | return 0; |
801 | } | |
802 | machine_arch_initcall(pseries, pseries_nvram_init_log_partitions); | |
edc79a2f | 803 | |
1da177e4 LT |
804 | int __init pSeries_nvram_init(void) |
805 | { | |
806 | struct device_node *nvram; | |
954a46e2 JK |
807 | const unsigned int *nbytes_p; |
808 | unsigned int proplen; | |
1da177e4 LT |
809 | |
810 | nvram = of_find_node_by_type(NULL, "nvram"); | |
811 | if (nvram == NULL) | |
812 | return -ENODEV; | |
813 | ||
e2eb6392 | 814 | nbytes_p = of_get_property(nvram, "#bytes", &proplen); |
bad5232b JL |
815 | if (nbytes_p == NULL || proplen != sizeof(unsigned int)) { |
816 | of_node_put(nvram); | |
1da177e4 | 817 | return -EIO; |
bad5232b | 818 | } |
1da177e4 LT |
819 | |
820 | nvram_size = *nbytes_p; | |
821 | ||
822 | nvram_fetch = rtas_token("nvram-fetch"); | |
823 | nvram_store = rtas_token("nvram-store"); | |
824 | printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size); | |
825 | of_node_put(nvram); | |
826 | ||
827 | ppc_md.nvram_read = pSeries_nvram_read; | |
828 | ppc_md.nvram_write = pSeries_nvram_write; | |
829 | ppc_md.nvram_size = pSeries_nvram_get_size; | |
830 | ||
831 | return 0; | |
832 | } | |
a5cf4b08 | 833 | |
a5cf4b08 | 834 | |
6c493685 JK |
835 | /* |
836 | * This is our kmsg_dump callback, called after an oops or panic report | |
837 | * has been written to the printk buffer. We want to capture as much | |
838 | * of the printk buffer as possible. First, capture as much as we can | |
839 | * that we think will compress sufficiently to fit in the lnx,oops-log | |
840 | * partition. If that's too much, go back and capture uncompressed text. | |
841 | */ | |
a5cf4b08 | 842 | static void oops_to_nvram(struct kmsg_dumper *dumper, |
e2ae715d | 843 | enum kmsg_dump_reason reason) |
a5cf4b08 | 844 | { |
b1f70e1f | 845 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; |
a5cf4b08 | 846 | static unsigned int oops_count = 0; |
15d260b3 | 847 | static bool panicking = false; |
120a52c3 AB |
848 | static DEFINE_SPINLOCK(lock); |
849 | unsigned long flags; | |
a5cf4b08 | 850 | size_t text_len; |
6c493685 JK |
851 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; |
852 | int rc = -1; | |
a5cf4b08 | 853 | |
15d260b3 JK |
854 | switch (reason) { |
855 | case KMSG_DUMP_RESTART: | |
856 | case KMSG_DUMP_HALT: | |
857 | case KMSG_DUMP_POWEROFF: | |
858 | /* These are almost always orderly shutdowns. */ | |
859 | return; | |
860 | case KMSG_DUMP_OOPS: | |
15d260b3 JK |
861 | break; |
862 | case KMSG_DUMP_PANIC: | |
863 | panicking = true; | |
864 | break; | |
865 | case KMSG_DUMP_EMERG: | |
866 | if (panicking) | |
867 | /* Panic report already captured. */ | |
868 | return; | |
869 | break; | |
870 | default: | |
871 | pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", | |
872 | __FUNCTION__, (int) reason); | |
873 | return; | |
874 | } | |
875 | ||
a5cf4b08 JK |
876 | if (clobbering_unread_rtas_event()) |
877 | return; | |
878 | ||
120a52c3 AB |
879 | if (!spin_trylock_irqsave(&lock, flags)) |
880 | return; | |
881 | ||
6c493685 | 882 | if (big_oops_buf) { |
e2ae715d KS |
883 | kmsg_dump_get_buffer(dumper, false, |
884 | big_oops_buf, big_oops_buf_sz, &text_len); | |
6c493685 JK |
885 | rc = zip_oops(text_len); |
886 | } | |
887 | if (rc != 0) { | |
e2ae715d | 888 | kmsg_dump_rewind(dumper); |
1bf247f8 | 889 | kmsg_dump_get_buffer(dumper, false, |
e2ae715d | 890 | oops_data, oops_data_sz, &text_len); |
6c493685 | 891 | err_type = ERR_TYPE_KERNEL_PANIC; |
b1f70e1f AB |
892 | oops_hdr->version = OOPS_HDR_VERSION; |
893 | oops_hdr->report_length = (u16) text_len; | |
894 | oops_hdr->timestamp = get_seconds(); | |
6c493685 JK |
895 | } |
896 | ||
a5cf4b08 | 897 | (void) nvram_write_os_partition(&oops_log_partition, oops_buf, |
b1f70e1f AB |
898 | (int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type, |
899 | ++oops_count); | |
120a52c3 AB |
900 | |
901 | spin_unlock_irqrestore(&lock, flags); | |
a5cf4b08 | 902 | } |