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 | ||
489 | #ifdef CONFIG_PSTORE | |
490 | static int nvram_pstore_open(struct pstore_info *psi) | |
491 | { | |
492 | /* Reset the iterator to start reading partitions again */ | |
493 | read_type = -1; | |
494 | return 0; | |
495 | } | |
496 | ||
497 | /** | |
498 | * nvram_pstore_write - pstore write callback for nvram | |
499 | * @type: Type of message logged | |
500 | * @reason: reason behind dump (oops/panic) | |
501 | * @id: identifier to indicate the write performed | |
502 | * @part: pstore writes data to registered buffer in parts, | |
503 | * part number will indicate the same. | |
504 | * @count: Indicates oops count | |
6bbbca73 | 505 | * @hsize: Size of header added by pstore |
d7563c94 AB |
506 | * @size: number of bytes written to the registered buffer |
507 | * @psi: registered pstore_info structure | |
508 | * | |
509 | * Called by pstore_dump() when an oops or panic report is logged in the | |
510 | * printk buffer. | |
511 | * Returns 0 on successful write. | |
512 | */ | |
513 | static int nvram_pstore_write(enum pstore_type_id type, | |
514 | enum kmsg_dump_reason reason, | |
515 | u64 *id, unsigned int part, int count, | |
6bbbca73 AB |
516 | size_t hsize, size_t size, |
517 | struct pstore_info *psi) | |
d7563c94 AB |
518 | { |
519 | int rc; | |
520 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; | |
521 | ||
522 | /* part 1 has the recent messages from printk buffer */ | |
523 | if (part > 1 || type != PSTORE_TYPE_DMESG || | |
524 | clobbering_unread_rtas_event()) | |
525 | return -1; | |
526 | ||
527 | oops_hdr->version = OOPS_HDR_VERSION; | |
528 | oops_hdr->report_length = (u16) size; | |
529 | oops_hdr->timestamp = get_seconds(); | |
530 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, | |
531 | (int) (sizeof(*oops_hdr) + size), ERR_TYPE_KERNEL_PANIC, | |
532 | count); | |
533 | ||
534 | if (rc != 0) | |
535 | return rc; | |
536 | ||
537 | *id = part; | |
538 | return 0; | |
539 | } | |
540 | ||
541 | /* | |
a5e4797b | 542 | * Reads the oops/panic report, rtas, of-config and common partition. |
d7563c94 AB |
543 | * Returns the length of the data we read from each partition. |
544 | * Returns 0 if we've been called before. | |
545 | */ | |
546 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | |
547 | int *count, struct timespec *time, char **buf, | |
548 | struct pstore_info *psi) | |
549 | { | |
550 | struct oops_log_info *oops_hdr; | |
f33f748c | 551 | unsigned int err_type, id_no, size = 0; |
d7563c94 AB |
552 | struct nvram_os_partition *part = NULL; |
553 | char *buff = NULL; | |
f33f748c AB |
554 | int sig = 0; |
555 | loff_t p; | |
d7563c94 AB |
556 | |
557 | read_type++; | |
558 | ||
559 | switch (nvram_type_ids[read_type]) { | |
560 | case PSTORE_TYPE_DMESG: | |
561 | part = &oops_log_partition; | |
562 | *type = PSTORE_TYPE_DMESG; | |
563 | break; | |
69020eea AB |
564 | case PSTORE_TYPE_PPC_RTAS: |
565 | part = &rtas_log_partition; | |
566 | *type = PSTORE_TYPE_PPC_RTAS; | |
567 | time->tv_sec = last_rtas_event; | |
568 | time->tv_nsec = 0; | |
569 | break; | |
f33f748c AB |
570 | case PSTORE_TYPE_PPC_OF: |
571 | sig = NVRAM_SIG_OF; | |
572 | part = &of_config_partition; | |
573 | *type = PSTORE_TYPE_PPC_OF; | |
574 | *id = PSTORE_TYPE_PPC_OF; | |
575 | time->tv_sec = 0; | |
576 | time->tv_nsec = 0; | |
577 | break; | |
a5e4797b AB |
578 | case PSTORE_TYPE_PPC_COMMON: |
579 | sig = NVRAM_SIG_SYS; | |
580 | part = &common_partition; | |
581 | *type = PSTORE_TYPE_PPC_COMMON; | |
582 | *id = PSTORE_TYPE_PPC_COMMON; | |
583 | time->tv_sec = 0; | |
584 | time->tv_nsec = 0; | |
585 | break; | |
d7563c94 AB |
586 | default: |
587 | return 0; | |
588 | } | |
589 | ||
f33f748c AB |
590 | if (!part->os_partition) { |
591 | p = nvram_find_partition(part->name, sig, &size); | |
592 | if (p <= 0) { | |
593 | pr_err("nvram: Failed to find partition %s, " | |
594 | "err %d\n", part->name, (int)p); | |
595 | return 0; | |
596 | } | |
597 | part->index = p; | |
598 | part->size = size; | |
599 | } | |
600 | ||
d7563c94 AB |
601 | buff = kmalloc(part->size, GFP_KERNEL); |
602 | ||
603 | if (!buff) | |
604 | return -ENOMEM; | |
605 | ||
606 | if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { | |
607 | kfree(buff); | |
608 | return 0; | |
609 | } | |
610 | ||
611 | *count = 0; | |
f33f748c AB |
612 | |
613 | if (part->os_partition) | |
614 | *id = id_no; | |
69020eea AB |
615 | |
616 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { | |
617 | oops_hdr = (struct oops_log_info *)buff; | |
618 | *buf = buff + sizeof(*oops_hdr); | |
619 | time->tv_sec = oops_hdr->timestamp; | |
620 | time->tv_nsec = 0; | |
621 | return oops_hdr->report_length; | |
622 | } | |
623 | ||
624 | *buf = buff; | |
625 | return part->size; | |
d7563c94 AB |
626 | } |
627 | ||
628 | static struct pstore_info nvram_pstore_info = { | |
629 | .owner = THIS_MODULE, | |
630 | .name = "nvram", | |
631 | .open = nvram_pstore_open, | |
632 | .read = nvram_pstore_read, | |
633 | .write = nvram_pstore_write, | |
634 | }; | |
635 | ||
636 | static int nvram_pstore_init(void) | |
637 | { | |
638 | int rc = 0; | |
639 | ||
640 | nvram_pstore_info.buf = oops_data; | |
641 | nvram_pstore_info.bufsize = oops_data_sz; | |
642 | ||
643 | rc = pstore_register(&nvram_pstore_info); | |
644 | if (rc != 0) | |
645 | pr_err("nvram: pstore_register() failed, defaults to " | |
646 | "kmsg_dump; returned %d\n", rc); | |
647 | else | |
648 | /*TODO: Support compression when pstore is configured */ | |
649 | pr_info("nvram: Compression of oops text supported only when " | |
650 | "pstore is not configured"); | |
651 | ||
652 | return rc; | |
653 | } | |
654 | #else | |
655 | static int nvram_pstore_init(void) | |
656 | { | |
657 | return -1; | |
658 | } | |
659 | #endif | |
660 | ||
a5cf4b08 JK |
661 | static void __init nvram_init_oops_partition(int rtas_partition_exists) |
662 | { | |
663 | int rc; | |
664 | ||
665 | rc = pseries_nvram_init_os_partition(&oops_log_partition); | |
666 | if (rc != 0) { | |
667 | if (!rtas_partition_exists) | |
668 | return; | |
669 | pr_notice("nvram: Using %s partition to log both" | |
670 | " RTAS errors and oops/panic reports\n", | |
671 | rtas_log_partition.name); | |
672 | memcpy(&oops_log_partition, &rtas_log_partition, | |
673 | sizeof(rtas_log_partition)); | |
674 | } | |
675 | oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); | |
6c493685 JK |
676 | if (!oops_buf) { |
677 | pr_err("nvram: No memory for %s partition\n", | |
678 | oops_log_partition.name); | |
679 | return; | |
680 | } | |
b1f70e1f AB |
681 | oops_data = oops_buf + sizeof(struct oops_log_info); |
682 | oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); | |
6c493685 | 683 | |
d7563c94 AB |
684 | rc = nvram_pstore_init(); |
685 | ||
686 | if (!rc) | |
687 | return; | |
688 | ||
6c493685 JK |
689 | /* |
690 | * Figure compression (preceded by elimination of each line's <n> | |
691 | * severity prefix) will reduce the oops/panic report to at most | |
692 | * 45% of its original size. | |
693 | */ | |
694 | big_oops_buf_sz = (oops_data_sz * 100) / 45; | |
695 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | |
696 | if (big_oops_buf) { | |
697 | stream.workspace = kmalloc(zlib_deflate_workspacesize( | |
698 | WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); | |
699 | if (!stream.workspace) { | |
700 | pr_err("nvram: No memory for compression workspace; " | |
701 | "skipping compression of %s partition data\n", | |
702 | oops_log_partition.name); | |
703 | kfree(big_oops_buf); | |
704 | big_oops_buf = NULL; | |
705 | } | |
706 | } else { | |
707 | pr_err("No memory for uncompressed %s data; " | |
708 | "skipping compression\n", oops_log_partition.name); | |
709 | stream.workspace = NULL; | |
710 | } | |
711 | ||
a5cf4b08 JK |
712 | rc = kmsg_dump_register(&nvram_kmsg_dumper); |
713 | if (rc != 0) { | |
714 | pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); | |
715 | kfree(oops_buf); | |
6c493685 JK |
716 | kfree(big_oops_buf); |
717 | kfree(stream.workspace); | |
a5cf4b08 JK |
718 | } |
719 | } | |
720 | ||
0f4ac132 JK |
721 | static int __init pseries_nvram_init_log_partitions(void) |
722 | { | |
a5cf4b08 JK |
723 | int rc; |
724 | ||
725 | rc = pseries_nvram_init_os_partition(&rtas_log_partition); | |
726 | nvram_init_oops_partition(rc == 0); | |
0f4ac132 JK |
727 | return 0; |
728 | } | |
729 | machine_arch_initcall(pseries, pseries_nvram_init_log_partitions); | |
edc79a2f | 730 | |
1da177e4 LT |
731 | int __init pSeries_nvram_init(void) |
732 | { | |
733 | struct device_node *nvram; | |
954a46e2 JK |
734 | const unsigned int *nbytes_p; |
735 | unsigned int proplen; | |
1da177e4 LT |
736 | |
737 | nvram = of_find_node_by_type(NULL, "nvram"); | |
738 | if (nvram == NULL) | |
739 | return -ENODEV; | |
740 | ||
e2eb6392 | 741 | nbytes_p = of_get_property(nvram, "#bytes", &proplen); |
bad5232b JL |
742 | if (nbytes_p == NULL || proplen != sizeof(unsigned int)) { |
743 | of_node_put(nvram); | |
1da177e4 | 744 | return -EIO; |
bad5232b | 745 | } |
1da177e4 LT |
746 | |
747 | nvram_size = *nbytes_p; | |
748 | ||
749 | nvram_fetch = rtas_token("nvram-fetch"); | |
750 | nvram_store = rtas_token("nvram-store"); | |
751 | printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size); | |
752 | of_node_put(nvram); | |
753 | ||
754 | ppc_md.nvram_read = pSeries_nvram_read; | |
755 | ppc_md.nvram_write = pSeries_nvram_write; | |
756 | ppc_md.nvram_size = pSeries_nvram_get_size; | |
757 | ||
758 | return 0; | |
759 | } | |
a5cf4b08 | 760 | |
a5cf4b08 | 761 | |
6c493685 JK |
762 | /* Derived from logfs_compress() */ |
763 | static int nvram_compress(const void *in, void *out, size_t inlen, | |
764 | size_t outlen) | |
765 | { | |
766 | int err, ret; | |
767 | ||
768 | ret = -EIO; | |
769 | err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, | |
770 | MEM_LEVEL, Z_DEFAULT_STRATEGY); | |
771 | if (err != Z_OK) | |
772 | goto error; | |
773 | ||
774 | stream.next_in = in; | |
775 | stream.avail_in = inlen; | |
776 | stream.total_in = 0; | |
777 | stream.next_out = out; | |
778 | stream.avail_out = outlen; | |
779 | stream.total_out = 0; | |
780 | ||
781 | err = zlib_deflate(&stream, Z_FINISH); | |
782 | if (err != Z_STREAM_END) | |
783 | goto error; | |
784 | ||
785 | err = zlib_deflateEnd(&stream); | |
786 | if (err != Z_OK) | |
787 | goto error; | |
788 | ||
789 | if (stream.total_out >= stream.total_in) | |
790 | goto error; | |
791 | ||
792 | ret = stream.total_out; | |
793 | error: | |
794 | return ret; | |
795 | } | |
796 | ||
797 | /* Compress the text from big_oops_buf into oops_buf. */ | |
798 | static int zip_oops(size_t text_len) | |
799 | { | |
b1f70e1f | 800 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; |
6c493685 JK |
801 | int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, |
802 | oops_data_sz); | |
803 | if (zipped_len < 0) { | |
804 | pr_err("nvram: compression failed; returned %d\n", zipped_len); | |
805 | pr_err("nvram: logging uncompressed oops/panic report\n"); | |
806 | return -1; | |
807 | } | |
b1f70e1f AB |
808 | oops_hdr->version = OOPS_HDR_VERSION; |
809 | oops_hdr->report_length = (u16) zipped_len; | |
810 | oops_hdr->timestamp = get_seconds(); | |
6c493685 JK |
811 | return 0; |
812 | } | |
813 | ||
814 | /* | |
815 | * This is our kmsg_dump callback, called after an oops or panic report | |
816 | * has been written to the printk buffer. We want to capture as much | |
817 | * of the printk buffer as possible. First, capture as much as we can | |
818 | * that we think will compress sufficiently to fit in the lnx,oops-log | |
819 | * partition. If that's too much, go back and capture uncompressed text. | |
820 | */ | |
a5cf4b08 | 821 | static void oops_to_nvram(struct kmsg_dumper *dumper, |
e2ae715d | 822 | enum kmsg_dump_reason reason) |
a5cf4b08 | 823 | { |
b1f70e1f | 824 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; |
a5cf4b08 | 825 | static unsigned int oops_count = 0; |
15d260b3 | 826 | static bool panicking = false; |
120a52c3 AB |
827 | static DEFINE_SPINLOCK(lock); |
828 | unsigned long flags; | |
a5cf4b08 | 829 | size_t text_len; |
6c493685 JK |
830 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; |
831 | int rc = -1; | |
a5cf4b08 | 832 | |
15d260b3 JK |
833 | switch (reason) { |
834 | case KMSG_DUMP_RESTART: | |
835 | case KMSG_DUMP_HALT: | |
836 | case KMSG_DUMP_POWEROFF: | |
837 | /* These are almost always orderly shutdowns. */ | |
838 | return; | |
839 | case KMSG_DUMP_OOPS: | |
15d260b3 JK |
840 | break; |
841 | case KMSG_DUMP_PANIC: | |
842 | panicking = true; | |
843 | break; | |
844 | case KMSG_DUMP_EMERG: | |
845 | if (panicking) | |
846 | /* Panic report already captured. */ | |
847 | return; | |
848 | break; | |
849 | default: | |
850 | pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", | |
851 | __FUNCTION__, (int) reason); | |
852 | return; | |
853 | } | |
854 | ||
a5cf4b08 JK |
855 | if (clobbering_unread_rtas_event()) |
856 | return; | |
857 | ||
120a52c3 AB |
858 | if (!spin_trylock_irqsave(&lock, flags)) |
859 | return; | |
860 | ||
6c493685 | 861 | if (big_oops_buf) { |
e2ae715d KS |
862 | kmsg_dump_get_buffer(dumper, false, |
863 | big_oops_buf, big_oops_buf_sz, &text_len); | |
6c493685 JK |
864 | rc = zip_oops(text_len); |
865 | } | |
866 | if (rc != 0) { | |
e2ae715d | 867 | kmsg_dump_rewind(dumper); |
1bf247f8 | 868 | kmsg_dump_get_buffer(dumper, false, |
e2ae715d | 869 | oops_data, oops_data_sz, &text_len); |
6c493685 | 870 | err_type = ERR_TYPE_KERNEL_PANIC; |
b1f70e1f AB |
871 | oops_hdr->version = OOPS_HDR_VERSION; |
872 | oops_hdr->report_length = (u16) text_len; | |
873 | oops_hdr->timestamp = get_seconds(); | |
6c493685 JK |
874 | } |
875 | ||
a5cf4b08 | 876 | (void) nvram_write_os_partition(&oops_log_partition, oops_buf, |
b1f70e1f AB |
877 | (int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type, |
878 | ++oops_count); | |
120a52c3 AB |
879 | |
880 | spin_unlock_irqrestore(&lock, flags); | |
a5cf4b08 | 881 | } |