Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * c 2001 PPC 64 Team, IBM Corp | |
4 | * | |
1da177e4 | 5 | * /dev/nvram driver for PPC64 |
1da177e4 LT |
6 | */ |
7 | ||
1da177e4 LT |
8 | #include <linux/types.h> |
9 | #include <linux/errno.h> | |
10 | #include <linux/fs.h> | |
11 | #include <linux/miscdevice.h> | |
12 | #include <linux/fcntl.h> | |
13 | #include <linux/nvram.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/spinlock.h> | |
78989f0a | 17 | #include <linux/kmsg_dump.h> |
b808b1d6 | 18 | #include <linux/pagemap.h> |
78989f0a HB |
19 | #include <linux/pstore.h> |
20 | #include <linux/zlib.h> | |
7c0f6ba6 | 21 | #include <linux/uaccess.h> |
e6f6390a | 22 | #include <linux/of.h> |
1da177e4 LT |
23 | #include <asm/nvram.h> |
24 | #include <asm/rtas.h> | |
1da177e4 | 25 | #include <asm/machdep.h> |
1da177e4 LT |
26 | |
27 | #undef DEBUG_NVRAM | |
28 | ||
36673307 BH |
29 | #define NVRAM_HEADER_LEN sizeof(struct nvram_header) |
30 | #define NVRAM_BLOCK_LEN NVRAM_HEADER_LEN | |
74d51d02 BH |
31 | |
32 | /* If change this size, then change the size of NVNAME_LEN */ | |
33 | struct nvram_header { | |
34 | unsigned char signature; | |
35 | unsigned char checksum; | |
36 | unsigned short length; | |
6024ede9 | 37 | /* Terminating null required only for names < 12 chars. */ |
74d51d02 BH |
38 | char name[12]; |
39 | }; | |
40 | ||
41 | struct nvram_partition { | |
42 | struct list_head partition; | |
43 | struct nvram_header header; | |
44 | unsigned int index; | |
45 | }; | |
46 | ||
690d1a9b | 47 | static LIST_HEAD(nvram_partitions); |
1da177e4 | 48 | |
78989f0a HB |
49 | #ifdef CONFIG_PPC_PSERIES |
50 | struct nvram_os_partition rtas_log_partition = { | |
51 | .name = "ibm,rtas-log", | |
52 | .req_size = 2079, | |
53 | .min_size = 1055, | |
54 | .index = -1, | |
55 | .os_partition = true | |
56 | }; | |
57 | #endif | |
58 | ||
59 | struct nvram_os_partition oops_log_partition = { | |
60 | .name = "lnx,oops-log", | |
61 | .req_size = 4000, | |
62 | .min_size = 2000, | |
63 | .index = -1, | |
64 | .os_partition = true | |
65 | }; | |
66 | ||
67 | static const char *nvram_os_partitions[] = { | |
68 | #ifdef CONFIG_PPC_PSERIES | |
69 | "ibm,rtas-log", | |
70 | #endif | |
71 | "lnx,oops-log", | |
72 | NULL | |
73 | }; | |
74 | ||
75 | static void oops_to_nvram(struct kmsg_dumper *dumper, | |
e1a261ba | 76 | struct kmsg_dump_detail *detail); |
78989f0a HB |
77 | |
78 | static struct kmsg_dumper nvram_kmsg_dumper = { | |
79 | .dump = oops_to_nvram | |
80 | }; | |
81 | ||
82 | /* | |
83 | * For capturing and compressing an oops or panic report... | |
84 | ||
85 | * big_oops_buf[] holds the uncompressed text we're capturing. | |
86 | * | |
87 | * oops_buf[] holds the compressed text, preceded by a oops header. | |
88 | * oops header has u16 holding the version of oops header (to differentiate | |
89 | * between old and new format header) followed by u16 holding the length of | |
90 | * the compressed* text (*Or uncompressed, if compression fails.) and u64 | |
91 | * holding the timestamp. oops_buf[] gets written to NVRAM. | |
92 | * | |
93 | * oops_log_info points to the header. oops_data points to the compressed text. | |
94 | * | |
95 | * +- oops_buf | |
96 | * | +- oops_data | |
97 | * v v | |
98 | * +-----------+-----------+-----------+------------------------+ | |
99 | * | version | length | timestamp | text | | |
100 | * | (2 bytes) | (2 bytes) | (8 bytes) | (oops_data_sz bytes) | | |
101 | * +-----------+-----------+-----------+------------------------+ | |
102 | * ^ | |
103 | * +- oops_log_info | |
104 | * | |
105 | * We preallocate these buffers during init to avoid kmalloc during oops/panic. | |
106 | */ | |
107 | static size_t big_oops_buf_sz; | |
108 | static char *big_oops_buf, *oops_buf; | |
109 | static char *oops_data; | |
110 | static size_t oops_data_sz; | |
111 | ||
112 | /* Compression parameters */ | |
113 | #define COMPR_LEVEL 6 | |
114 | #define WINDOW_BITS 12 | |
115 | #define MEM_LEVEL 4 | |
116 | static struct z_stream_s stream; | |
117 | ||
118 | #ifdef CONFIG_PSTORE | |
f7618299 HB |
119 | #ifdef CONFIG_PPC_POWERNV |
120 | static struct nvram_os_partition skiboot_partition = { | |
121 | .name = "ibm,skiboot", | |
122 | .index = -1, | |
123 | .os_partition = false | |
124 | }; | |
125 | #endif | |
126 | ||
78989f0a HB |
127 | #ifdef CONFIG_PPC_PSERIES |
128 | static struct nvram_os_partition of_config_partition = { | |
129 | .name = "of-config", | |
130 | .index = -1, | |
131 | .os_partition = false | |
132 | }; | |
133 | #endif | |
134 | ||
135 | static struct nvram_os_partition common_partition = { | |
136 | .name = "common", | |
137 | .index = -1, | |
138 | .os_partition = false | |
139 | }; | |
140 | ||
141 | static enum pstore_type_id nvram_type_ids[] = { | |
142 | PSTORE_TYPE_DMESG, | |
143 | PSTORE_TYPE_PPC_COMMON, | |
144 | -1, | |
145 | -1, | |
146 | -1 | |
147 | }; | |
148 | static int read_type; | |
149 | #endif | |
150 | ||
151 | /* nvram_write_os_partition | |
152 | * | |
153 | * We need to buffer the error logs into nvram to ensure that we have | |
154 | * the failure information to decode. If we have a severe error there | |
155 | * is no way to guarantee that the OS or the machine is in a state to | |
156 | * get back to user land and write the error to disk. For example if | |
157 | * the SCSI device driver causes a Machine Check by writing to a bad | |
158 | * IO address, there is no way of guaranteeing that the device driver | |
159 | * is in any state that is would also be able to write the error data | |
160 | * captured to disk, thus we buffer it in NVRAM for analysis on the | |
161 | * next boot. | |
162 | * | |
163 | * In NVRAM the partition containing the error log buffer will looks like: | |
164 | * Header (in bytes): | |
165 | * +-----------+----------+--------+------------+------------------+ | |
166 | * | signature | checksum | length | name | data | | |
167 | * |0 |1 |2 3|4 15|16 length-1| | |
168 | * +-----------+----------+--------+------------+------------------+ | |
169 | * | |
170 | * The 'data' section would look like (in bytes): | |
171 | * +--------------+------------+-----------------------------------+ | |
172 | * | event_logged | sequence # | error log | | |
173 | * |0 3|4 7|8 error_log_size-1| | |
174 | * +--------------+------------+-----------------------------------+ | |
175 | * | |
176 | * event_logged: 0 if event has not been logged to syslog, 1 if it has | |
177 | * sequence #: The unique sequence # for each event. (until it wraps) | |
178 | * error log: The error log from event_scan | |
179 | */ | |
180 | int nvram_write_os_partition(struct nvram_os_partition *part, | |
181 | char *buff, int length, | |
182 | unsigned int err_type, | |
183 | unsigned int error_log_cnt) | |
184 | { | |
185 | int rc; | |
186 | loff_t tmp_index; | |
187 | struct err_log_info info; | |
188 | ||
189 | if (part->index == -1) | |
190 | return -ESPIPE; | |
191 | ||
192 | if (length > part->size) | |
193 | length = part->size; | |
194 | ||
195 | info.error_type = cpu_to_be32(err_type); | |
196 | info.seq_num = cpu_to_be32(error_log_cnt); | |
197 | ||
198 | tmp_index = part->index; | |
199 | ||
a0828cf5 | 200 | rc = ppc_md.nvram_write((char *)&info, sizeof(info), &tmp_index); |
78989f0a HB |
201 | if (rc <= 0) { |
202 | pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); | |
203 | return rc; | |
204 | } | |
205 | ||
206 | rc = ppc_md.nvram_write(buff, length, &tmp_index); | |
207 | if (rc <= 0) { | |
208 | pr_err("%s: Failed nvram_write (%d)\n", __func__, rc); | |
209 | return rc; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | /* nvram_read_partition | |
216 | * | |
217 | * Reads nvram partition for at most 'length' | |
218 | */ | |
219 | int nvram_read_partition(struct nvram_os_partition *part, char *buff, | |
220 | int length, unsigned int *err_type, | |
221 | unsigned int *error_log_cnt) | |
222 | { | |
223 | int rc; | |
224 | loff_t tmp_index; | |
225 | struct err_log_info info; | |
226 | ||
227 | if (part->index == -1) | |
228 | return -1; | |
229 | ||
230 | if (length > part->size) | |
231 | length = part->size; | |
232 | ||
233 | tmp_index = part->index; | |
234 | ||
235 | if (part->os_partition) { | |
a0828cf5 | 236 | rc = ppc_md.nvram_read((char *)&info, sizeof(info), &tmp_index); |
78989f0a HB |
237 | if (rc <= 0) { |
238 | pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); | |
239 | return rc; | |
240 | } | |
241 | } | |
242 | ||
243 | rc = ppc_md.nvram_read(buff, length, &tmp_index); | |
244 | if (rc <= 0) { | |
245 | pr_err("%s: Failed nvram_read (%d)\n", __func__, rc); | |
246 | return rc; | |
247 | } | |
248 | ||
249 | if (part->os_partition) { | |
250 | *error_log_cnt = be32_to_cpu(info.seq_num); | |
251 | *err_type = be32_to_cpu(info.error_type); | |
252 | } | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | /* nvram_init_os_partition | |
258 | * | |
259 | * This sets up a partition with an "OS" signature. | |
260 | * | |
261 | * The general strategy is the following: | |
262 | * 1.) If a partition with the indicated name already exists... | |
263 | * - If it's large enough, use it. | |
264 | * - Otherwise, recycle it and keep going. | |
265 | * 2.) Search for a free partition that is large enough. | |
266 | * 3.) If there's not a free partition large enough, recycle any obsolete | |
267 | * OS partitions and try again. | |
268 | * 4.) Will first try getting a chunk that will satisfy the requested size. | |
269 | * 5.) If a chunk of the requested size cannot be allocated, then try finding | |
270 | * a chunk that will satisfy the minum needed. | |
271 | * | |
272 | * Returns 0 on success, else -1. | |
273 | */ | |
274 | int __init nvram_init_os_partition(struct nvram_os_partition *part) | |
275 | { | |
276 | loff_t p; | |
277 | int size; | |
278 | ||
279 | /* Look for ours */ | |
280 | p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size); | |
281 | ||
282 | /* Found one but too small, remove it */ | |
283 | if (p && size < part->min_size) { | |
284 | pr_info("nvram: Found too small %s partition," | |
285 | " removing it...\n", part->name); | |
286 | nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL); | |
287 | p = 0; | |
288 | } | |
289 | ||
290 | /* Create one if we didn't find */ | |
291 | if (!p) { | |
292 | p = nvram_create_partition(part->name, NVRAM_SIG_OS, | |
293 | part->req_size, part->min_size); | |
294 | if (p == -ENOSPC) { | |
295 | pr_info("nvram: No room to create %s partition, " | |
296 | "deleting any obsolete OS partitions...\n", | |
297 | part->name); | |
298 | nvram_remove_partition(NULL, NVRAM_SIG_OS, | |
299 | nvram_os_partitions); | |
300 | p = nvram_create_partition(part->name, NVRAM_SIG_OS, | |
301 | part->req_size, part->min_size); | |
302 | } | |
303 | } | |
304 | ||
305 | if (p <= 0) { | |
306 | pr_err("nvram: Failed to find or create %s" | |
307 | " partition, err %d\n", part->name, (int)p); | |
308 | return -1; | |
309 | } | |
310 | ||
311 | part->index = p; | |
312 | part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info); | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | /* Derived from logfs_compress() */ | |
318 | static int nvram_compress(const void *in, void *out, size_t inlen, | |
319 | size_t outlen) | |
320 | { | |
321 | int err, ret; | |
322 | ||
323 | ret = -EIO; | |
324 | err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, | |
325 | MEM_LEVEL, Z_DEFAULT_STRATEGY); | |
326 | if (err != Z_OK) | |
327 | goto error; | |
328 | ||
329 | stream.next_in = in; | |
330 | stream.avail_in = inlen; | |
331 | stream.total_in = 0; | |
332 | stream.next_out = out; | |
333 | stream.avail_out = outlen; | |
334 | stream.total_out = 0; | |
335 | ||
336 | err = zlib_deflate(&stream, Z_FINISH); | |
337 | if (err != Z_STREAM_END) | |
338 | goto error; | |
339 | ||
340 | err = zlib_deflateEnd(&stream); | |
341 | if (err != Z_OK) | |
342 | goto error; | |
343 | ||
344 | if (stream.total_out >= stream.total_in) | |
345 | goto error; | |
346 | ||
347 | ret = stream.total_out; | |
348 | error: | |
349 | return ret; | |
350 | } | |
351 | ||
352 | /* Compress the text from big_oops_buf into oops_buf. */ | |
353 | static int zip_oops(size_t text_len) | |
354 | { | |
355 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; | |
356 | int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, | |
357 | oops_data_sz); | |
358 | if (zipped_len < 0) { | |
359 | pr_err("nvram: compression failed; returned %d\n", zipped_len); | |
360 | pr_err("nvram: logging uncompressed oops/panic report\n"); | |
361 | return -1; | |
362 | } | |
363 | oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); | |
364 | oops_hdr->report_length = cpu_to_be16(zipped_len); | |
e4a9616c | 365 | oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); |
78989f0a HB |
366 | return 0; |
367 | } | |
368 | ||
369 | #ifdef CONFIG_PSTORE | |
370 | static int nvram_pstore_open(struct pstore_info *psi) | |
371 | { | |
372 | /* Reset the iterator to start reading partitions again */ | |
373 | read_type = -1; | |
374 | return 0; | |
375 | } | |
376 | ||
377 | /** | |
378 | * nvram_pstore_write - pstore write callback for nvram | |
76cc9580 | 379 | * @record: pstore record to write, with @id to be set |
78989f0a HB |
380 | * |
381 | * Called by pstore_dump() when an oops or panic report is logged in the | |
382 | * printk buffer. | |
383 | * Returns 0 on successful write. | |
384 | */ | |
76cc9580 | 385 | static int nvram_pstore_write(struct pstore_record *record) |
78989f0a HB |
386 | { |
387 | int rc; | |
388 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC; | |
389 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; | |
390 | ||
391 | /* part 1 has the recent messages from printk buffer */ | |
76cc9580 | 392 | if (record->part > 1 || (record->type != PSTORE_TYPE_DMESG)) |
78989f0a HB |
393 | return -1; |
394 | ||
395 | if (clobbering_unread_rtas_event()) | |
396 | return -1; | |
397 | ||
398 | oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); | |
76cc9580 | 399 | oops_hdr->report_length = cpu_to_be16(record->size); |
e4a9616c | 400 | oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); |
78989f0a | 401 | |
76cc9580 | 402 | if (record->compressed) |
78989f0a HB |
403 | err_type = ERR_TYPE_KERNEL_PANIC_GZ; |
404 | ||
405 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, | |
76cc9580 KC |
406 | (int) (sizeof(*oops_hdr) + record->size), err_type, |
407 | record->count); | |
78989f0a HB |
408 | |
409 | if (rc != 0) | |
410 | return rc; | |
411 | ||
76cc9580 | 412 | record->id = record->part; |
78989f0a HB |
413 | return 0; |
414 | } | |
415 | ||
416 | /* | |
417 | * Reads the oops/panic report, rtas, of-config and common partition. | |
418 | * Returns the length of the data we read from each partition. | |
419 | * Returns 0 if we've been called before. | |
420 | */ | |
125cc42b | 421 | static ssize_t nvram_pstore_read(struct pstore_record *record) |
78989f0a HB |
422 | { |
423 | struct oops_log_info *oops_hdr; | |
424 | unsigned int err_type, id_no, size = 0; | |
425 | struct nvram_os_partition *part = NULL; | |
426 | char *buff = NULL; | |
427 | int sig = 0; | |
428 | loff_t p; | |
429 | ||
430 | read_type++; | |
431 | ||
432 | switch (nvram_type_ids[read_type]) { | |
433 | case PSTORE_TYPE_DMESG: | |
434 | part = &oops_log_partition; | |
125cc42b | 435 | record->type = PSTORE_TYPE_DMESG; |
78989f0a HB |
436 | break; |
437 | case PSTORE_TYPE_PPC_COMMON: | |
438 | sig = NVRAM_SIG_SYS; | |
439 | part = &common_partition; | |
125cc42b KC |
440 | record->type = PSTORE_TYPE_PPC_COMMON; |
441 | record->id = PSTORE_TYPE_PPC_COMMON; | |
442 | record->time.tv_sec = 0; | |
443 | record->time.tv_nsec = 0; | |
78989f0a HB |
444 | break; |
445 | #ifdef CONFIG_PPC_PSERIES | |
446 | case PSTORE_TYPE_PPC_RTAS: | |
447 | part = &rtas_log_partition; | |
125cc42b KC |
448 | record->type = PSTORE_TYPE_PPC_RTAS; |
449 | record->time.tv_sec = last_rtas_event; | |
450 | record->time.tv_nsec = 0; | |
78989f0a HB |
451 | break; |
452 | case PSTORE_TYPE_PPC_OF: | |
453 | sig = NVRAM_SIG_OF; | |
454 | part = &of_config_partition; | |
125cc42b KC |
455 | record->type = PSTORE_TYPE_PPC_OF; |
456 | record->id = PSTORE_TYPE_PPC_OF; | |
457 | record->time.tv_sec = 0; | |
458 | record->time.tv_nsec = 0; | |
78989f0a | 459 | break; |
f7618299 HB |
460 | #endif |
461 | #ifdef CONFIG_PPC_POWERNV | |
462 | case PSTORE_TYPE_PPC_OPAL: | |
463 | sig = NVRAM_SIG_FW; | |
464 | part = &skiboot_partition; | |
125cc42b KC |
465 | record->type = PSTORE_TYPE_PPC_OPAL; |
466 | record->id = PSTORE_TYPE_PPC_OPAL; | |
467 | record->time.tv_sec = 0; | |
468 | record->time.tv_nsec = 0; | |
f7618299 | 469 | break; |
78989f0a HB |
470 | #endif |
471 | default: | |
472 | return 0; | |
473 | } | |
474 | ||
475 | if (!part->os_partition) { | |
476 | p = nvram_find_partition(part->name, sig, &size); | |
477 | if (p <= 0) { | |
478 | pr_err("nvram: Failed to find partition %s, " | |
479 | "err %d\n", part->name, (int)p); | |
480 | return 0; | |
481 | } | |
482 | part->index = p; | |
483 | part->size = size; | |
484 | } | |
485 | ||
486 | buff = kmalloc(part->size, GFP_KERNEL); | |
487 | ||
488 | if (!buff) | |
489 | return -ENOMEM; | |
490 | ||
491 | if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { | |
492 | kfree(buff); | |
493 | return 0; | |
494 | } | |
495 | ||
125cc42b | 496 | record->count = 0; |
78989f0a HB |
497 | |
498 | if (part->os_partition) | |
125cc42b | 499 | record->id = id_no; |
78989f0a HB |
500 | |
501 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { | |
502 | size_t length, hdr_size; | |
503 | ||
504 | oops_hdr = (struct oops_log_info *)buff; | |
505 | if (be16_to_cpu(oops_hdr->version) < OOPS_HDR_VERSION) { | |
506 | /* Old format oops header had 2-byte record size */ | |
507 | hdr_size = sizeof(u16); | |
508 | length = be16_to_cpu(oops_hdr->version); | |
125cc42b KC |
509 | record->time.tv_sec = 0; |
510 | record->time.tv_nsec = 0; | |
78989f0a HB |
511 | } else { |
512 | hdr_size = sizeof(*oops_hdr); | |
513 | length = be16_to_cpu(oops_hdr->report_length); | |
125cc42b KC |
514 | record->time.tv_sec = be64_to_cpu(oops_hdr->timestamp); |
515 | record->time.tv_nsec = 0; | |
78989f0a | 516 | } |
125cc42b | 517 | record->buf = kmemdup(buff + hdr_size, length, GFP_KERNEL); |
0d0fecc5 | 518 | kfree(buff); |
125cc42b | 519 | if (record->buf == NULL) |
78989f0a | 520 | return -ENOMEM; |
78989f0a | 521 | |
125cc42b | 522 | record->ecc_notice_size = 0; |
78989f0a | 523 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) |
125cc42b | 524 | record->compressed = true; |
78989f0a | 525 | else |
125cc42b | 526 | record->compressed = false; |
78989f0a HB |
527 | return length; |
528 | } | |
529 | ||
125cc42b | 530 | record->buf = buff; |
78989f0a HB |
531 | return part->size; |
532 | } | |
533 | ||
534 | static struct pstore_info nvram_pstore_info = { | |
535 | .owner = THIS_MODULE, | |
536 | .name = "nvram", | |
041939c1 | 537 | .flags = PSTORE_FLAGS_DMESG, |
78989f0a HB |
538 | .open = nvram_pstore_open, |
539 | .read = nvram_pstore_read, | |
540 | .write = nvram_pstore_write, | |
541 | }; | |
542 | ||
d276960d | 543 | static int __init nvram_pstore_init(void) |
78989f0a HB |
544 | { |
545 | int rc = 0; | |
546 | ||
f7618299 HB |
547 | if (machine_is(pseries)) { |
548 | nvram_type_ids[2] = PSTORE_TYPE_PPC_RTAS; | |
549 | nvram_type_ids[3] = PSTORE_TYPE_PPC_OF; | |
550 | } else | |
551 | nvram_type_ids[2] = PSTORE_TYPE_PPC_OPAL; | |
78989f0a HB |
552 | |
553 | nvram_pstore_info.buf = oops_data; | |
554 | nvram_pstore_info.bufsize = oops_data_sz; | |
555 | ||
78989f0a | 556 | rc = pstore_register(&nvram_pstore_info); |
74943dab HB |
557 | if (rc && (rc != -EPERM)) |
558 | /* Print error only when pstore.backend == nvram */ | |
559 | pr_err("nvram: pstore_register() failed, returned %d. " | |
560 | "Defaults to kmsg_dump\n", rc); | |
78989f0a HB |
561 | |
562 | return rc; | |
563 | } | |
564 | #else | |
d276960d | 565 | static int __init nvram_pstore_init(void) |
78989f0a HB |
566 | { |
567 | return -1; | |
568 | } | |
569 | #endif | |
570 | ||
571 | void __init nvram_init_oops_partition(int rtas_partition_exists) | |
572 | { | |
573 | int rc; | |
574 | ||
575 | rc = nvram_init_os_partition(&oops_log_partition); | |
576 | if (rc != 0) { | |
577 | #ifdef CONFIG_PPC_PSERIES | |
578 | if (!rtas_partition_exists) { | |
579 | pr_err("nvram: Failed to initialize oops partition!"); | |
580 | return; | |
581 | } | |
582 | pr_notice("nvram: Using %s partition to log both" | |
583 | " RTAS errors and oops/panic reports\n", | |
584 | rtas_log_partition.name); | |
585 | memcpy(&oops_log_partition, &rtas_log_partition, | |
586 | sizeof(rtas_log_partition)); | |
587 | #else | |
588 | pr_err("nvram: Failed to initialize oops partition!"); | |
589 | return; | |
590 | #endif | |
591 | } | |
592 | oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); | |
593 | if (!oops_buf) { | |
594 | pr_err("nvram: No memory for %s partition\n", | |
595 | oops_log_partition.name); | |
596 | return; | |
597 | } | |
598 | oops_data = oops_buf + sizeof(struct oops_log_info); | |
599 | oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); | |
600 | ||
601 | rc = nvram_pstore_init(); | |
602 | ||
603 | if (!rc) | |
604 | return; | |
605 | ||
606 | /* | |
607 | * Figure compression (preceded by elimination of each line's <n> | |
608 | * severity prefix) will reduce the oops/panic report to at most | |
609 | * 45% of its original size. | |
610 | */ | |
611 | big_oops_buf_sz = (oops_data_sz * 100) / 45; | |
612 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | |
613 | if (big_oops_buf) { | |
614 | stream.workspace = kmalloc(zlib_deflate_workspacesize( | |
615 | WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); | |
616 | if (!stream.workspace) { | |
617 | pr_err("nvram: No memory for compression workspace; " | |
618 | "skipping compression of %s partition data\n", | |
619 | oops_log_partition.name); | |
620 | kfree(big_oops_buf); | |
621 | big_oops_buf = NULL; | |
622 | } | |
623 | } else { | |
624 | pr_err("No memory for uncompressed %s data; " | |
625 | "skipping compression\n", oops_log_partition.name); | |
626 | stream.workspace = NULL; | |
627 | } | |
628 | ||
629 | rc = kmsg_dump_register(&nvram_kmsg_dumper); | |
630 | if (rc != 0) { | |
631 | pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); | |
632 | kfree(oops_buf); | |
633 | kfree(big_oops_buf); | |
634 | kfree(stream.workspace); | |
635 | } | |
636 | } | |
637 | ||
638 | /* | |
639 | * This is our kmsg_dump callback, called after an oops or panic report | |
640 | * has been written to the printk buffer. We want to capture as much | |
641 | * of the printk buffer as possible. First, capture as much as we can | |
642 | * that we think will compress sufficiently to fit in the lnx,oops-log | |
643 | * partition. If that's too much, go back and capture uncompressed text. | |
644 | */ | |
645 | static void oops_to_nvram(struct kmsg_dumper *dumper, | |
e1a261ba | 646 | struct kmsg_dump_detail *detail) |
78989f0a HB |
647 | { |
648 | struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; | |
649 | static unsigned int oops_count = 0; | |
f9f3f02d | 650 | static struct kmsg_dump_iter iter; |
78989f0a HB |
651 | static bool panicking = false; |
652 | static DEFINE_SPINLOCK(lock); | |
653 | unsigned long flags; | |
654 | size_t text_len; | |
655 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; | |
656 | int rc = -1; | |
657 | ||
e1a261ba | 658 | switch (detail->reason) { |
6d3cf962 | 659 | case KMSG_DUMP_SHUTDOWN: |
78989f0a HB |
660 | /* These are almost always orderly shutdowns. */ |
661 | return; | |
662 | case KMSG_DUMP_OOPS: | |
663 | break; | |
664 | case KMSG_DUMP_PANIC: | |
665 | panicking = true; | |
666 | break; | |
667 | case KMSG_DUMP_EMERG: | |
668 | if (panicking) | |
669 | /* Panic report already captured. */ | |
670 | return; | |
671 | break; | |
672 | default: | |
673 | pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n", | |
e1a261ba | 674 | __func__, (int) detail->reason); |
78989f0a HB |
675 | return; |
676 | } | |
677 | ||
678 | if (clobbering_unread_rtas_event()) | |
679 | return; | |
680 | ||
681 | if (!spin_trylock_irqsave(&lock, flags)) | |
682 | return; | |
683 | ||
684 | if (big_oops_buf) { | |
f9f3f02d JO |
685 | kmsg_dump_rewind(&iter); |
686 | kmsg_dump_get_buffer(&iter, false, | |
78989f0a HB |
687 | big_oops_buf, big_oops_buf_sz, &text_len); |
688 | rc = zip_oops(text_len); | |
689 | } | |
690 | if (rc != 0) { | |
f9f3f02d JO |
691 | kmsg_dump_rewind(&iter); |
692 | kmsg_dump_get_buffer(&iter, false, | |
78989f0a HB |
693 | oops_data, oops_data_sz, &text_len); |
694 | err_type = ERR_TYPE_KERNEL_PANIC; | |
695 | oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); | |
696 | oops_hdr->report_length = cpu_to_be16(text_len); | |
e4a9616c | 697 | oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); |
78989f0a HB |
698 | } |
699 | ||
700 | (void) nvram_write_os_partition(&oops_log_partition, oops_buf, | |
701 | (int) (sizeof(*oops_hdr) + text_len), err_type, | |
702 | ++oops_count); | |
703 | ||
704 | spin_unlock_irqrestore(&lock, flags); | |
705 | } | |
706 | ||
1da177e4 | 707 | #ifdef DEBUG_NVRAM |
32c105c3 | 708 | static void __init nvram_print_partitions(char * label) |
1da177e4 | 709 | { |
1da177e4 LT |
710 | struct nvram_partition * tmp_part; |
711 | ||
712 | printk(KERN_WARNING "--------%s---------\n", label); | |
713 | printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); | |
690d1a9b | 714 | list_for_each_entry(tmp_part, &nvram_partitions, partition) { |
e0513d9e | 715 | printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%12.12s\n", |
1da177e4 LT |
716 | tmp_part->index, tmp_part->header.signature, |
717 | tmp_part->header.checksum, tmp_part->header.length, | |
718 | tmp_part->header.name); | |
719 | } | |
720 | } | |
721 | #endif | |
722 | ||
723 | ||
32c105c3 | 724 | static int __init nvram_write_header(struct nvram_partition * part) |
1da177e4 LT |
725 | { |
726 | loff_t tmp_index; | |
727 | int rc; | |
c81095a4 CLG |
728 | struct nvram_header phead; |
729 | ||
730 | memcpy(&phead, &part->header, NVRAM_HEADER_LEN); | |
731 | phead.length = cpu_to_be16(phead.length); | |
732 | ||
1da177e4 | 733 | tmp_index = part->index; |
c81095a4 | 734 | rc = ppc_md.nvram_write((char *)&phead, NVRAM_HEADER_LEN, &tmp_index); |
1da177e4 LT |
735 | |
736 | return rc; | |
737 | } | |
738 | ||
739 | ||
32c105c3 | 740 | static unsigned char __init nvram_checksum(struct nvram_header *p) |
1da177e4 LT |
741 | { |
742 | unsigned int c_sum, c_sum2; | |
743 | unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */ | |
744 | c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; | |
745 | ||
746 | /* The sum may have spilled into the 3rd byte. Fold it back. */ | |
747 | c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; | |
748 | /* The sum cannot exceed 2 bytes. Fold it into a checksum */ | |
749 | c_sum2 = (c_sum >> 8) + (c_sum << 8); | |
750 | c_sum = ((c_sum + c_sum2) >> 8) & 0xff; | |
751 | return c_sum; | |
752 | } | |
753 | ||
0f4ac132 JK |
754 | /* |
755 | * Per the criteria passed via nvram_remove_partition(), should this | |
756 | * partition be removed? 1=remove, 0=keep | |
757 | */ | |
d276960d | 758 | static int __init nvram_can_remove_partition(struct nvram_partition *part, |
0f4ac132 JK |
759 | const char *name, int sig, const char *exceptions[]) |
760 | { | |
761 | if (part->header.signature != sig) | |
762 | return 0; | |
763 | if (name) { | |
764 | if (strncmp(name, part->header.name, 12)) | |
765 | return 0; | |
766 | } else if (exceptions) { | |
767 | const char **except; | |
768 | for (except = exceptions; *except; except++) { | |
769 | if (!strncmp(*except, part->header.name, 12)) | |
770 | return 0; | |
771 | } | |
772 | } | |
773 | return 1; | |
774 | } | |
775 | ||
fa2b4e54 BH |
776 | /** |
777 | * nvram_remove_partition - Remove one or more partitions in nvram | |
778 | * @name: name of the partition to remove, or NULL for a | |
779 | * signature only match | |
780 | * @sig: signature of the partition(s) to remove | |
0f4ac132 JK |
781 | * @exceptions: When removing all partitions with a matching signature, |
782 | * leave these alone. | |
fa2b4e54 BH |
783 | */ |
784 | ||
0f4ac132 JK |
785 | int __init nvram_remove_partition(const char *name, int sig, |
786 | const char *exceptions[]) | |
1da177e4 | 787 | { |
fa2b4e54 | 788 | struct nvram_partition *part, *prev, *tmp; |
1da177e4 LT |
789 | int rc; |
790 | ||
690d1a9b | 791 | list_for_each_entry(part, &nvram_partitions, partition) { |
0f4ac132 | 792 | if (!nvram_can_remove_partition(part, name, sig, exceptions)) |
fa2b4e54 BH |
793 | continue; |
794 | ||
795 | /* Make partition a free partition */ | |
1da177e4 | 796 | part->header.signature = NVRAM_SIG_FREE; |
11b7e154 | 797 | memset(part->header.name, 'w', 12); |
1da177e4 | 798 | part->header.checksum = nvram_checksum(&part->header); |
1da177e4 LT |
799 | rc = nvram_write_header(part); |
800 | if (rc <= 0) { | |
fa2b4e54 | 801 | printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); |
1da177e4 LT |
802 | return rc; |
803 | } | |
fa2b4e54 | 804 | } |
1da177e4 | 805 | |
fa2b4e54 BH |
806 | /* Merge contiguous ones */ |
807 | prev = NULL; | |
690d1a9b | 808 | list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) { |
fa2b4e54 BH |
809 | if (part->header.signature != NVRAM_SIG_FREE) { |
810 | prev = NULL; | |
811 | continue; | |
812 | } | |
813 | if (prev) { | |
814 | prev->header.length += part->header.length; | |
11b7e154 PX |
815 | prev->header.checksum = nvram_checksum(&prev->header); |
816 | rc = nvram_write_header(prev); | |
fa2b4e54 BH |
817 | if (rc <= 0) { |
818 | printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); | |
819 | return rc; | |
820 | } | |
821 | list_del(&part->partition); | |
822 | kfree(part); | |
823 | } else | |
824 | prev = part; | |
1da177e4 LT |
825 | } |
826 | ||
827 | return 0; | |
828 | } | |
829 | ||
4e7c77a3 BH |
830 | /** |
831 | * nvram_create_partition - Create a partition in nvram | |
832 | * @name: name of the partition to create | |
833 | * @sig: signature of the partition to create | |
36673307 | 834 | * @req_size: size of data to allocate in bytes |
4e7c77a3 | 835 | * @min_size: minimum acceptable size (0 means req_size) |
e49e2e87 BH |
836 | * |
837 | * Returns a negative error code or a positive nvram index | |
838 | * of the beginning of the data area of the newly created | |
839 | * partition. If you provided a min_size smaller than req_size | |
840 | * you need to query for the actual size yourself after the | |
841 | * call using nvram_partition_get_size(). | |
1da177e4 | 842 | */ |
edc79a2f BH |
843 | loff_t __init nvram_create_partition(const char *name, int sig, |
844 | int req_size, int min_size) | |
1da177e4 | 845 | { |
a341ad97 AB |
846 | struct nvram_partition *part; |
847 | struct nvram_partition *new_part; | |
0339ad77 | 848 | struct nvram_partition *free_part = NULL; |
cef0d5ad | 849 | static char nv_init_vals[16]; |
1da177e4 LT |
850 | loff_t tmp_index; |
851 | long size = 0; | |
852 | int rc; | |
4e7c77a3 | 853 | |
20e07af7 FT |
854 | BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16); |
855 | ||
36673307 | 856 | /* Convert sizes from bytes to blocks */ |
b7115316 CL |
857 | req_size = ALIGN(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; |
858 | min_size = ALIGN(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; | |
36673307 | 859 | |
4e7c77a3 BH |
860 | /* If no minimum size specified, make it the same as the |
861 | * requested size | |
862 | */ | |
863 | if (min_size == 0) | |
864 | min_size = req_size; | |
e49e2e87 BH |
865 | if (min_size > req_size) |
866 | return -EINVAL; | |
4e7c77a3 | 867 | |
36673307 BH |
868 | /* Now add one block to each for the header */ |
869 | req_size += 1; | |
870 | min_size += 1; | |
871 | ||
1da177e4 LT |
872 | /* Find a free partition that will give us the maximum needed size |
873 | If can't find one that will give us the minimum size needed */ | |
690d1a9b | 874 | list_for_each_entry(part, &nvram_partitions, partition) { |
1da177e4 LT |
875 | if (part->header.signature != NVRAM_SIG_FREE) |
876 | continue; | |
877 | ||
4e7c77a3 BH |
878 | if (part->header.length >= req_size) { |
879 | size = req_size; | |
1da177e4 LT |
880 | free_part = part; |
881 | break; | |
882 | } | |
4e7c77a3 BH |
883 | if (part->header.length > size && |
884 | part->header.length >= min_size) { | |
885 | size = part->header.length; | |
1da177e4 LT |
886 | free_part = part; |
887 | } | |
888 | } | |
0339ad77 | 889 | if (!size) |
1da177e4 | 890 | return -ENOSPC; |
1da177e4 LT |
891 | |
892 | /* Create our OS partition */ | |
c9599881 | 893 | new_part = kzalloc(sizeof(*new_part), GFP_KERNEL); |
1da177e4 | 894 | if (!new_part) { |
b6080db4 | 895 | pr_err("%s: kmalloc failed\n", __func__); |
1da177e4 LT |
896 | return -ENOMEM; |
897 | } | |
898 | ||
899 | new_part->index = free_part->index; | |
4e7c77a3 | 900 | new_part->header.signature = sig; |
1da177e4 | 901 | new_part->header.length = size; |
c9599881 | 902 | memcpy(new_part->header.name, name, strnlen(name, sizeof(new_part->header.name))); |
1da177e4 LT |
903 | new_part->header.checksum = nvram_checksum(&new_part->header); |
904 | ||
905 | rc = nvram_write_header(new_part); | |
906 | if (rc <= 0) { | |
b6080db4 | 907 | pr_err("%s: nvram_write_header failed (%d)\n", __func__, rc); |
7d523187 | 908 | kfree(new_part); |
1da177e4 LT |
909 | return rc; |
910 | } | |
e49e2e87 BH |
911 | list_add_tail(&new_part->partition, &free_part->partition); |
912 | ||
913 | /* Adjust or remove the partition we stole the space from */ | |
914 | if (free_part->header.length > size) { | |
915 | free_part->index += size * NVRAM_BLOCK_LEN; | |
916 | free_part->header.length -= size; | |
917 | free_part->header.checksum = nvram_checksum(&free_part->header); | |
918 | rc = nvram_write_header(free_part); | |
919 | if (rc <= 0) { | |
b6080db4 CJ |
920 | pr_err("%s: nvram_write_header failed (%d)\n", |
921 | __func__, rc); | |
e49e2e87 BH |
922 | return rc; |
923 | } | |
924 | } else { | |
925 | list_del(&free_part->partition); | |
926 | kfree(free_part); | |
927 | } | |
1da177e4 | 928 | |
e49e2e87 | 929 | /* Clear the new partition */ |
cef0d5ad BH |
930 | for (tmp_index = new_part->index + NVRAM_HEADER_LEN; |
931 | tmp_index < ((size - 1) * NVRAM_BLOCK_LEN); | |
932 | tmp_index += NVRAM_BLOCK_LEN) { | |
933 | rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index); | |
934 | if (rc <= 0) { | |
b6080db4 CJ |
935 | pr_err("%s: nvram_write failed (%d)\n", |
936 | __func__, rc); | |
cef0d5ad BH |
937 | return rc; |
938 | } | |
1da177e4 | 939 | } |
b6080db4 | 940 | |
e49e2e87 BH |
941 | return new_part->index + NVRAM_HEADER_LEN; |
942 | } | |
1da177e4 | 943 | |
e49e2e87 BH |
944 | /** |
945 | * nvram_get_partition_size - Get the data size of an nvram partition | |
946 | * @data_index: This is the offset of the start of the data of | |
947 | * the partition. The same value that is returned by | |
948 | * nvram_create_partition(). | |
949 | */ | |
edc79a2f | 950 | int nvram_get_partition_size(loff_t data_index) |
e49e2e87 BH |
951 | { |
952 | struct nvram_partition *part; | |
1da177e4 | 953 | |
690d1a9b | 954 | list_for_each_entry(part, &nvram_partitions, partition) { |
e49e2e87 BH |
955 | if (part->index + NVRAM_HEADER_LEN == data_index) |
956 | return (part->header.length - 1) * NVRAM_BLOCK_LEN; | |
1da177e4 | 957 | } |
e49e2e87 | 958 | return -1; |
1da177e4 LT |
959 | } |
960 | ||
961 | ||
cf5cbf9f BH |
962 | /** |
963 | * nvram_find_partition - Find an nvram partition by signature and name | |
964 | * @name: Name of the partition or NULL for any name | |
965 | * @sig: Signature to test against | |
966 | * @out_size: if non-NULL, returns the size of the data part of the partition | |
967 | */ | |
968 | loff_t nvram_find_partition(const char *name, int sig, int *out_size) | |
969 | { | |
970 | struct nvram_partition *p; | |
971 | ||
690d1a9b | 972 | list_for_each_entry(p, &nvram_partitions, partition) { |
cf5cbf9f BH |
973 | if (p->header.signature == sig && |
974 | (!name || !strncmp(p->header.name, name, 12))) { | |
975 | if (out_size) | |
976 | *out_size = (p->header.length - 1) * | |
977 | NVRAM_BLOCK_LEN; | |
978 | return p->index + NVRAM_HEADER_LEN; | |
979 | } | |
980 | } | |
981 | return 0; | |
982 | } | |
983 | ||
edc79a2f | 984 | int __init nvram_scan_partitions(void) |
1da177e4 LT |
985 | { |
986 | loff_t cur_index = 0; | |
987 | struct nvram_header phead; | |
988 | struct nvram_partition * tmp_part; | |
989 | unsigned char c_sum; | |
990 | char * header; | |
991 | int total_size; | |
992 | int err; | |
993 | ||
edc79a2f | 994 | if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) |
1da177e4 LT |
995 | return -ENODEV; |
996 | total_size = ppc_md.nvram_size(); | |
997 | ||
5cbded58 | 998 | header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL); |
1da177e4 LT |
999 | if (!header) { |
1000 | printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n"); | |
1001 | return -ENOMEM; | |
1002 | } | |
1003 | ||
1004 | while (cur_index < total_size) { | |
1005 | ||
1006 | err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index); | |
1007 | if (err != NVRAM_HEADER_LEN) { | |
1008 | printk(KERN_ERR "nvram_scan_partitions: Error parsing " | |
1009 | "nvram partitions\n"); | |
1010 | goto out; | |
1011 | } | |
1012 | ||
1013 | cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */ | |
1014 | ||
1015 | memcpy(&phead, header, NVRAM_HEADER_LEN); | |
1016 | ||
c81095a4 CLG |
1017 | phead.length = be16_to_cpu(phead.length); |
1018 | ||
1da177e4 LT |
1019 | err = 0; |
1020 | c_sum = nvram_checksum(&phead); | |
1021 | if (c_sum != phead.checksum) { | |
1022 | printk(KERN_WARNING "WARNING: nvram partition checksum" | |
1023 | " was %02x, should be %02x!\n", | |
1024 | phead.checksum, c_sum); | |
1025 | printk(KERN_WARNING "Terminating nvram partition scan\n"); | |
1026 | goto out; | |
1027 | } | |
1028 | if (!phead.length) { | |
1029 | printk(KERN_WARNING "WARNING: nvram corruption " | |
1030 | "detected: 0-length partition\n"); | |
1031 | goto out; | |
1032 | } | |
a0828cf5 | 1033 | tmp_part = kmalloc(sizeof(*tmp_part), GFP_KERNEL); |
1da177e4 LT |
1034 | err = -ENOMEM; |
1035 | if (!tmp_part) { | |
1036 | printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n"); | |
1037 | goto out; | |
1038 | } | |
1039 | ||
1040 | memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); | |
1041 | tmp_part->index = cur_index; | |
690d1a9b | 1042 | list_add_tail(&tmp_part->partition, &nvram_partitions); |
1da177e4 LT |
1043 | |
1044 | cur_index += phead.length * NVRAM_BLOCK_LEN; | |
1045 | } | |
1046 | err = 0; | |
1047 | ||
edc79a2f BH |
1048 | #ifdef DEBUG_NVRAM |
1049 | nvram_print_partitions("NVRAM Partitions"); | |
1050 | #endif | |
1051 | ||
1da177e4 LT |
1052 | out: |
1053 | kfree(header); | |
1054 | return err; | |
1055 | } |