diff options
Diffstat (limited to 'arch/powerpc/kernel/nvram_64.c')
-rw-r--r-- | arch/powerpc/kernel/nvram_64.c | 504 |
1 files changed, 183 insertions, 321 deletions
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 9cf197f01e94..bec1e930ed73 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c | |||
@@ -34,15 +34,26 @@ | |||
34 | 34 | ||
35 | #undef DEBUG_NVRAM | 35 | #undef DEBUG_NVRAM |
36 | 36 | ||
37 | static struct nvram_partition * nvram_part; | 37 | #define NVRAM_HEADER_LEN sizeof(struct nvram_header) |
38 | static long nvram_error_log_index = -1; | 38 | #define NVRAM_BLOCK_LEN NVRAM_HEADER_LEN |
39 | static long nvram_error_log_size = 0; | 39 | |
40 | /* If change this size, then change the size of NVNAME_LEN */ | ||
41 | struct nvram_header { | ||
42 | unsigned char signature; | ||
43 | unsigned char checksum; | ||
44 | unsigned short length; | ||
45 | /* Terminating null required only for names < 12 chars. */ | ||
46 | char name[12]; | ||
47 | }; | ||
40 | 48 | ||
41 | struct err_log_info { | 49 | struct nvram_partition { |
42 | int error_type; | 50 | struct list_head partition; |
43 | unsigned int seq_num; | 51 | struct nvram_header header; |
52 | unsigned int index; | ||
44 | }; | 53 | }; |
45 | 54 | ||
55 | static LIST_HEAD(nvram_partitions); | ||
56 | |||
46 | static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) | 57 | static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) |
47 | { | 58 | { |
48 | int size; | 59 | int size; |
@@ -186,14 +197,12 @@ static struct miscdevice nvram_dev = { | |||
186 | #ifdef DEBUG_NVRAM | 197 | #ifdef DEBUG_NVRAM |
187 | static void __init nvram_print_partitions(char * label) | 198 | static void __init nvram_print_partitions(char * label) |
188 | { | 199 | { |
189 | struct list_head * p; | ||
190 | struct nvram_partition * tmp_part; | 200 | struct nvram_partition * tmp_part; |
191 | 201 | ||
192 | printk(KERN_WARNING "--------%s---------\n", label); | 202 | printk(KERN_WARNING "--------%s---------\n", label); |
193 | printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); | 203 | printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); |
194 | list_for_each(p, &nvram_part->partition) { | 204 | list_for_each_entry(tmp_part, &nvram_partitions, partition) { |
195 | tmp_part = list_entry(p, struct nvram_partition, partition); | 205 | printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%12s\n", |
196 | printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%s\n", | ||
197 | tmp_part->index, tmp_part->header.signature, | 206 | tmp_part->index, tmp_part->header.signature, |
198 | tmp_part->header.checksum, tmp_part->header.length, | 207 | tmp_part->header.checksum, tmp_part->header.length, |
199 | tmp_part->header.name); | 208 | tmp_part->header.name); |
@@ -228,95 +237,136 @@ static unsigned char __init nvram_checksum(struct nvram_header *p) | |||
228 | return c_sum; | 237 | return c_sum; |
229 | } | 238 | } |
230 | 239 | ||
231 | static int __init nvram_remove_os_partition(void) | 240 | /* |
241 | * Per the criteria passed via nvram_remove_partition(), should this | ||
242 | * partition be removed? 1=remove, 0=keep | ||
243 | */ | ||
244 | static int nvram_can_remove_partition(struct nvram_partition *part, | ||
245 | const char *name, int sig, const char *exceptions[]) | ||
246 | { | ||
247 | if (part->header.signature != sig) | ||
248 | return 0; | ||
249 | if (name) { | ||
250 | if (strncmp(name, part->header.name, 12)) | ||
251 | return 0; | ||
252 | } else if (exceptions) { | ||
253 | const char **except; | ||
254 | for (except = exceptions; *except; except++) { | ||
255 | if (!strncmp(*except, part->header.name, 12)) | ||
256 | return 0; | ||
257 | } | ||
258 | } | ||
259 | return 1; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * nvram_remove_partition - Remove one or more partitions in nvram | ||
264 | * @name: name of the partition to remove, or NULL for a | ||
265 | * signature only match | ||
266 | * @sig: signature of the partition(s) to remove | ||
267 | * @exceptions: When removing all partitions with a matching signature, | ||
268 | * leave these alone. | ||
269 | */ | ||
270 | |||
271 | int __init nvram_remove_partition(const char *name, int sig, | ||
272 | const char *exceptions[]) | ||
232 | { | 273 | { |
233 | struct list_head *i; | 274 | struct nvram_partition *part, *prev, *tmp; |
234 | struct list_head *j; | ||
235 | struct nvram_partition * part; | ||
236 | struct nvram_partition * cur_part; | ||
237 | int rc; | 275 | int rc; |
238 | 276 | ||
239 | list_for_each(i, &nvram_part->partition) { | 277 | list_for_each_entry(part, &nvram_partitions, partition) { |
240 | part = list_entry(i, struct nvram_partition, partition); | 278 | if (!nvram_can_remove_partition(part, name, sig, exceptions)) |
241 | if (part->header.signature != NVRAM_SIG_OS) | ||
242 | continue; | 279 | continue; |
243 | 280 | ||
244 | /* Make os partition a free partition */ | 281 | /* Make partition a free partition */ |
245 | part->header.signature = NVRAM_SIG_FREE; | 282 | part->header.signature = NVRAM_SIG_FREE; |
246 | sprintf(part->header.name, "wwwwwwwwwwww"); | 283 | strncpy(part->header.name, "wwwwwwwwwwww", 12); |
247 | part->header.checksum = nvram_checksum(&part->header); | 284 | part->header.checksum = nvram_checksum(&part->header); |
248 | |||
249 | /* Merge contiguous free partitions backwards */ | ||
250 | list_for_each_prev(j, &part->partition) { | ||
251 | cur_part = list_entry(j, struct nvram_partition, partition); | ||
252 | if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) { | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | part->header.length += cur_part->header.length; | ||
257 | part->header.checksum = nvram_checksum(&part->header); | ||
258 | part->index = cur_part->index; | ||
259 | |||
260 | list_del(&cur_part->partition); | ||
261 | kfree(cur_part); | ||
262 | j = &part->partition; /* fixup our loop */ | ||
263 | } | ||
264 | |||
265 | /* Merge contiguous free partitions forwards */ | ||
266 | list_for_each(j, &part->partition) { | ||
267 | cur_part = list_entry(j, struct nvram_partition, partition); | ||
268 | if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) { | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | part->header.length += cur_part->header.length; | ||
273 | part->header.checksum = nvram_checksum(&part->header); | ||
274 | |||
275 | list_del(&cur_part->partition); | ||
276 | kfree(cur_part); | ||
277 | j = &part->partition; /* fixup our loop */ | ||
278 | } | ||
279 | |||
280 | rc = nvram_write_header(part); | 285 | rc = nvram_write_header(part); |
281 | if (rc <= 0) { | 286 | if (rc <= 0) { |
282 | printk(KERN_ERR "nvram_remove_os_partition: nvram_write failed (%d)\n", rc); | 287 | printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); |
283 | return rc; | 288 | return rc; |
284 | } | 289 | } |
290 | } | ||
285 | 291 | ||
292 | /* Merge contiguous ones */ | ||
293 | prev = NULL; | ||
294 | list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) { | ||
295 | if (part->header.signature != NVRAM_SIG_FREE) { | ||
296 | prev = NULL; | ||
297 | continue; | ||
298 | } | ||
299 | if (prev) { | ||
300 | prev->header.length += part->header.length; | ||
301 | prev->header.checksum = nvram_checksum(&part->header); | ||
302 | rc = nvram_write_header(part); | ||
303 | if (rc <= 0) { | ||
304 | printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); | ||
305 | return rc; | ||
306 | } | ||
307 | list_del(&part->partition); | ||
308 | kfree(part); | ||
309 | } else | ||
310 | prev = part; | ||
286 | } | 311 | } |
287 | 312 | ||
288 | return 0; | 313 | return 0; |
289 | } | 314 | } |
290 | 315 | ||
291 | /* nvram_create_os_partition | 316 | /** |
317 | * nvram_create_partition - Create a partition in nvram | ||
318 | * @name: name of the partition to create | ||
319 | * @sig: signature of the partition to create | ||
320 | * @req_size: size of data to allocate in bytes | ||
321 | * @min_size: minimum acceptable size (0 means req_size) | ||
292 | * | 322 | * |
293 | * Create a OS linux partition to buffer error logs. | 323 | * Returns a negative error code or a positive nvram index |
294 | * Will create a partition starting at the first free | 324 | * of the beginning of the data area of the newly created |
295 | * space found if space has enough room. | 325 | * partition. If you provided a min_size smaller than req_size |
326 | * you need to query for the actual size yourself after the | ||
327 | * call using nvram_partition_get_size(). | ||
296 | */ | 328 | */ |
297 | static int __init nvram_create_os_partition(void) | 329 | loff_t __init nvram_create_partition(const char *name, int sig, |
330 | int req_size, int min_size) | ||
298 | { | 331 | { |
299 | struct nvram_partition *part; | 332 | struct nvram_partition *part; |
300 | struct nvram_partition *new_part; | 333 | struct nvram_partition *new_part; |
301 | struct nvram_partition *free_part = NULL; | 334 | struct nvram_partition *free_part = NULL; |
302 | int seq_init[2] = { 0, 0 }; | 335 | static char nv_init_vals[16]; |
303 | loff_t tmp_index; | 336 | loff_t tmp_index; |
304 | long size = 0; | 337 | long size = 0; |
305 | int rc; | 338 | int rc; |
306 | 339 | ||
340 | /* Convert sizes from bytes to blocks */ | ||
341 | req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; | ||
342 | min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; | ||
343 | |||
344 | /* If no minimum size specified, make it the same as the | ||
345 | * requested size | ||
346 | */ | ||
347 | if (min_size == 0) | ||
348 | min_size = req_size; | ||
349 | if (min_size > req_size) | ||
350 | return -EINVAL; | ||
351 | |||
352 | /* Now add one block to each for the header */ | ||
353 | req_size += 1; | ||
354 | min_size += 1; | ||
355 | |||
307 | /* Find a free partition that will give us the maximum needed size | 356 | /* Find a free partition that will give us the maximum needed size |
308 | If can't find one that will give us the minimum size needed */ | 357 | If can't find one that will give us the minimum size needed */ |
309 | list_for_each_entry(part, &nvram_part->partition, partition) { | 358 | list_for_each_entry(part, &nvram_partitions, partition) { |
310 | if (part->header.signature != NVRAM_SIG_FREE) | 359 | if (part->header.signature != NVRAM_SIG_FREE) |
311 | continue; | 360 | continue; |
312 | 361 | ||
313 | if (part->header.length >= NVRAM_MAX_REQ) { | 362 | if (part->header.length >= req_size) { |
314 | size = NVRAM_MAX_REQ; | 363 | size = req_size; |
315 | free_part = part; | 364 | free_part = part; |
316 | break; | 365 | break; |
317 | } | 366 | } |
318 | if (!size && part->header.length >= NVRAM_MIN_REQ) { | 367 | if (part->header.length > size && |
319 | size = NVRAM_MIN_REQ; | 368 | part->header.length >= min_size) { |
369 | size = part->header.length; | ||
320 | free_part = part; | 370 | free_part = part; |
321 | } | 371 | } |
322 | } | 372 | } |
@@ -326,136 +376,95 @@ static int __init nvram_create_os_partition(void) | |||
326 | /* Create our OS partition */ | 376 | /* Create our OS partition */ |
327 | new_part = kmalloc(sizeof(*new_part), GFP_KERNEL); | 377 | new_part = kmalloc(sizeof(*new_part), GFP_KERNEL); |
328 | if (!new_part) { | 378 | if (!new_part) { |
329 | printk(KERN_ERR "nvram_create_os_partition: kmalloc failed\n"); | 379 | pr_err("nvram_create_os_partition: kmalloc failed\n"); |
330 | return -ENOMEM; | 380 | return -ENOMEM; |
331 | } | 381 | } |
332 | 382 | ||
333 | new_part->index = free_part->index; | 383 | new_part->index = free_part->index; |
334 | new_part->header.signature = NVRAM_SIG_OS; | 384 | new_part->header.signature = sig; |
335 | new_part->header.length = size; | 385 | new_part->header.length = size; |
336 | strcpy(new_part->header.name, "ppc64,linux"); | 386 | strncpy(new_part->header.name, name, 12); |
337 | new_part->header.checksum = nvram_checksum(&new_part->header); | 387 | new_part->header.checksum = nvram_checksum(&new_part->header); |
338 | 388 | ||
339 | rc = nvram_write_header(new_part); | 389 | rc = nvram_write_header(new_part); |
340 | if (rc <= 0) { | 390 | if (rc <= 0) { |
341 | printk(KERN_ERR "nvram_create_os_partition: nvram_write_header " | 391 | pr_err("nvram_create_os_partition: nvram_write_header " |
342 | "failed (%d)\n", rc); | ||
343 | return rc; | ||
344 | } | ||
345 | |||
346 | /* make sure and initialize to zero the sequence number and the error | ||
347 | type logged */ | ||
348 | tmp_index = new_part->index + NVRAM_HEADER_LEN; | ||
349 | rc = ppc_md.nvram_write((char *)&seq_init, sizeof(seq_init), &tmp_index); | ||
350 | if (rc <= 0) { | ||
351 | printk(KERN_ERR "nvram_create_os_partition: nvram_write " | ||
352 | "failed (%d)\n", rc); | 392 | "failed (%d)\n", rc); |
353 | return rc; | 393 | return rc; |
354 | } | 394 | } |
355 | |||
356 | nvram_error_log_index = new_part->index + NVRAM_HEADER_LEN; | ||
357 | nvram_error_log_size = ((part->header.length - 1) * | ||
358 | NVRAM_BLOCK_LEN) - sizeof(struct err_log_info); | ||
359 | |||
360 | list_add_tail(&new_part->partition, &free_part->partition); | 395 | list_add_tail(&new_part->partition, &free_part->partition); |
361 | 396 | ||
362 | if (free_part->header.length <= size) { | 397 | /* Adjust or remove the partition we stole the space from */ |
398 | if (free_part->header.length > size) { | ||
399 | free_part->index += size * NVRAM_BLOCK_LEN; | ||
400 | free_part->header.length -= size; | ||
401 | free_part->header.checksum = nvram_checksum(&free_part->header); | ||
402 | rc = nvram_write_header(free_part); | ||
403 | if (rc <= 0) { | ||
404 | pr_err("nvram_create_os_partition: nvram_write_header " | ||
405 | "failed (%d)\n", rc); | ||
406 | return rc; | ||
407 | } | ||
408 | } else { | ||
363 | list_del(&free_part->partition); | 409 | list_del(&free_part->partition); |
364 | kfree(free_part); | 410 | kfree(free_part); |
365 | return 0; | ||
366 | } | 411 | } |
367 | 412 | ||
368 | /* Adjust the partition we stole the space from */ | 413 | /* Clear the new partition */ |
369 | free_part->index += size * NVRAM_BLOCK_LEN; | 414 | for (tmp_index = new_part->index + NVRAM_HEADER_LEN; |
370 | free_part->header.length -= size; | 415 | tmp_index < ((size - 1) * NVRAM_BLOCK_LEN); |
371 | free_part->header.checksum = nvram_checksum(&free_part->header); | 416 | tmp_index += NVRAM_BLOCK_LEN) { |
372 | 417 | rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index); | |
373 | rc = nvram_write_header(free_part); | 418 | if (rc <= 0) { |
374 | if (rc <= 0) { | 419 | pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc); |
375 | printk(KERN_ERR "nvram_create_os_partition: nvram_write_header " | 420 | return rc; |
376 | "failed (%d)\n", rc); | 421 | } |
377 | return rc; | ||
378 | } | 422 | } |
379 | 423 | ||
380 | return 0; | 424 | return new_part->index + NVRAM_HEADER_LEN; |
381 | } | 425 | } |
382 | 426 | ||
383 | 427 | /** | |
384 | /* nvram_setup_partition | 428 | * nvram_get_partition_size - Get the data size of an nvram partition |
385 | * | 429 | * @data_index: This is the offset of the start of the data of |
386 | * This will setup the partition we need for buffering the | 430 | * the partition. The same value that is returned by |
387 | * error logs and cleanup partitions if needed. | 431 | * nvram_create_partition(). |
388 | * | ||
389 | * The general strategy is the following: | ||
390 | * 1.) If there is ppc64,linux partition large enough then use it. | ||
391 | * 2.) If there is not a ppc64,linux partition large enough, search | ||
392 | * for a free partition that is large enough. | ||
393 | * 3.) If there is not a free partition large enough remove | ||
394 | * _all_ OS partitions and consolidate the space. | ||
395 | * 4.) Will first try getting a chunk that will satisfy the maximum | ||
396 | * error log size (NVRAM_MAX_REQ). | ||
397 | * 5.) If the max chunk cannot be allocated then try finding a chunk | ||
398 | * that will satisfy the minum needed (NVRAM_MIN_REQ). | ||
399 | */ | 432 | */ |
400 | static int __init nvram_setup_partition(void) | 433 | int nvram_get_partition_size(loff_t data_index) |
401 | { | 434 | { |
402 | struct list_head * p; | 435 | struct nvram_partition *part; |
403 | struct nvram_partition * part; | 436 | |
404 | int rc; | 437 | list_for_each_entry(part, &nvram_partitions, partition) { |
405 | 438 | if (part->index + NVRAM_HEADER_LEN == data_index) | |
406 | /* For now, we don't do any of this on pmac, until I | 439 | return (part->header.length - 1) * NVRAM_BLOCK_LEN; |
407 | * have figured out if it's worth killing some unused stuffs | 440 | } |
408 | * in our nvram, as Apple defined partitions use pretty much | 441 | return -1; |
409 | * all of the space | 442 | } |
410 | */ | ||
411 | if (machine_is(powermac)) | ||
412 | return -ENOSPC; | ||
413 | |||
414 | /* see if we have an OS partition that meets our needs. | ||
415 | will try getting the max we need. If not we'll delete | ||
416 | partitions and try again. */ | ||
417 | list_for_each(p, &nvram_part->partition) { | ||
418 | part = list_entry(p, struct nvram_partition, partition); | ||
419 | if (part->header.signature != NVRAM_SIG_OS) | ||
420 | continue; | ||
421 | 443 | ||
422 | if (strcmp(part->header.name, "ppc64,linux")) | ||
423 | continue; | ||
424 | 444 | ||
425 | if (part->header.length >= NVRAM_MIN_REQ) { | 445 | /** |
426 | /* found our partition */ | 446 | * nvram_find_partition - Find an nvram partition by signature and name |
427 | nvram_error_log_index = part->index + NVRAM_HEADER_LEN; | 447 | * @name: Name of the partition or NULL for any name |
428 | nvram_error_log_size = ((part->header.length - 1) * | 448 | * @sig: Signature to test against |
429 | NVRAM_BLOCK_LEN) - sizeof(struct err_log_info); | 449 | * @out_size: if non-NULL, returns the size of the data part of the partition |
430 | return 0; | 450 | */ |
451 | loff_t nvram_find_partition(const char *name, int sig, int *out_size) | ||
452 | { | ||
453 | struct nvram_partition *p; | ||
454 | |||
455 | list_for_each_entry(p, &nvram_partitions, partition) { | ||
456 | if (p->header.signature == sig && | ||
457 | (!name || !strncmp(p->header.name, name, 12))) { | ||
458 | if (out_size) | ||
459 | *out_size = (p->header.length - 1) * | ||
460 | NVRAM_BLOCK_LEN; | ||
461 | return p->index + NVRAM_HEADER_LEN; | ||
431 | } | 462 | } |
432 | } | 463 | } |
433 | |||
434 | /* try creating a partition with the free space we have */ | ||
435 | rc = nvram_create_os_partition(); | ||
436 | if (!rc) { | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | /* need to free up some space */ | ||
441 | rc = nvram_remove_os_partition(); | ||
442 | if (rc) { | ||
443 | return rc; | ||
444 | } | ||
445 | |||
446 | /* create a partition in this new space */ | ||
447 | rc = nvram_create_os_partition(); | ||
448 | if (rc) { | ||
449 | printk(KERN_ERR "nvram_create_os_partition: Could not find a " | ||
450 | "NVRAM partition large enough\n"); | ||
451 | return rc; | ||
452 | } | ||
453 | |||
454 | return 0; | 464 | return 0; |
455 | } | 465 | } |
456 | 466 | ||
457 | 467 | int __init nvram_scan_partitions(void) | |
458 | static int __init nvram_scan_partitions(void) | ||
459 | { | 468 | { |
460 | loff_t cur_index = 0; | 469 | loff_t cur_index = 0; |
461 | struct nvram_header phead; | 470 | struct nvram_header phead; |
@@ -465,7 +474,7 @@ static int __init nvram_scan_partitions(void) | |||
465 | int total_size; | 474 | int total_size; |
466 | int err; | 475 | int err; |
467 | 476 | ||
468 | if (ppc_md.nvram_size == NULL) | 477 | if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) |
469 | return -ENODEV; | 478 | return -ENODEV; |
470 | total_size = ppc_md.nvram_size(); | 479 | total_size = ppc_md.nvram_size(); |
471 | 480 | ||
@@ -512,12 +521,16 @@ static int __init nvram_scan_partitions(void) | |||
512 | 521 | ||
513 | memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); | 522 | memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); |
514 | tmp_part->index = cur_index; | 523 | tmp_part->index = cur_index; |
515 | list_add_tail(&tmp_part->partition, &nvram_part->partition); | 524 | list_add_tail(&tmp_part->partition, &nvram_partitions); |
516 | 525 | ||
517 | cur_index += phead.length * NVRAM_BLOCK_LEN; | 526 | cur_index += phead.length * NVRAM_BLOCK_LEN; |
518 | } | 527 | } |
519 | err = 0; | 528 | err = 0; |
520 | 529 | ||
530 | #ifdef DEBUG_NVRAM | ||
531 | nvram_print_partitions("NVRAM Partitions"); | ||
532 | #endif | ||
533 | |||
521 | out: | 534 | out: |
522 | kfree(header); | 535 | kfree(header); |
523 | return err; | 536 | return err; |
@@ -525,9 +538,10 @@ static int __init nvram_scan_partitions(void) | |||
525 | 538 | ||
526 | static int __init nvram_init(void) | 539 | static int __init nvram_init(void) |
527 | { | 540 | { |
528 | int error; | ||
529 | int rc; | 541 | int rc; |
530 | 542 | ||
543 | BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16); | ||
544 | |||
531 | if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) | 545 | if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) |
532 | return -ENODEV; | 546 | return -ENODEV; |
533 | 547 | ||
@@ -537,29 +551,6 @@ static int __init nvram_init(void) | |||
537 | return rc; | 551 | return rc; |
538 | } | 552 | } |
539 | 553 | ||
540 | /* initialize our anchor for the nvram partition list */ | ||
541 | nvram_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); | ||
542 | if (!nvram_part) { | ||
543 | printk(KERN_ERR "nvram_init: Failed kmalloc\n"); | ||
544 | return -ENOMEM; | ||
545 | } | ||
546 | INIT_LIST_HEAD(&nvram_part->partition); | ||
547 | |||
548 | /* Get all the NVRAM partitions */ | ||
549 | error = nvram_scan_partitions(); | ||
550 | if (error) { | ||
551 | printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n"); | ||
552 | return error; | ||
553 | } | ||
554 | |||
555 | if(nvram_setup_partition()) | ||
556 | printk(KERN_WARNING "nvram_init: Could not find nvram partition" | ||
557 | " for nvram buffered error logging.\n"); | ||
558 | |||
559 | #ifdef DEBUG_NVRAM | ||
560 | nvram_print_partitions("NVRAM Partitions"); | ||
561 | #endif | ||
562 | |||
563 | return rc; | 554 | return rc; |
564 | } | 555 | } |
565 | 556 | ||
@@ -568,135 +559,6 @@ void __exit nvram_cleanup(void) | |||
568 | misc_deregister( &nvram_dev ); | 559 | misc_deregister( &nvram_dev ); |
569 | } | 560 | } |
570 | 561 | ||
571 | |||
572 | #ifdef CONFIG_PPC_PSERIES | ||
573 | |||
574 | /* nvram_write_error_log | ||
575 | * | ||
576 | * We need to buffer the error logs into nvram to ensure that we have | ||
577 | * the failure information to decode. If we have a severe error there | ||
578 | * is no way to guarantee that the OS or the machine is in a state to | ||
579 | * get back to user land and write the error to disk. For example if | ||
580 | * the SCSI device driver causes a Machine Check by writing to a bad | ||
581 | * IO address, there is no way of guaranteeing that the device driver | ||
582 | * is in any state that is would also be able to write the error data | ||
583 | * captured to disk, thus we buffer it in NVRAM for analysis on the | ||
584 | * next boot. | ||
585 | * | ||
586 | * In NVRAM the partition containing the error log buffer will looks like: | ||
587 | * Header (in bytes): | ||
588 | * +-----------+----------+--------+------------+------------------+ | ||
589 | * | signature | checksum | length | name | data | | ||
590 | * |0 |1 |2 3|4 15|16 length-1| | ||
591 | * +-----------+----------+--------+------------+------------------+ | ||
592 | * | ||
593 | * The 'data' section would look like (in bytes): | ||
594 | * +--------------+------------+-----------------------------------+ | ||
595 | * | event_logged | sequence # | error log | | ||
596 | * |0 3|4 7|8 nvram_error_log_size-1| | ||
597 | * +--------------+------------+-----------------------------------+ | ||
598 | * | ||
599 | * event_logged: 0 if event has not been logged to syslog, 1 if it has | ||
600 | * sequence #: The unique sequence # for each event. (until it wraps) | ||
601 | * error log: The error log from event_scan | ||
602 | */ | ||
603 | int nvram_write_error_log(char * buff, int length, | ||
604 | unsigned int err_type, unsigned int error_log_cnt) | ||
605 | { | ||
606 | int rc; | ||
607 | loff_t tmp_index; | ||
608 | struct err_log_info info; | ||
609 | |||
610 | if (nvram_error_log_index == -1) { | ||
611 | return -ESPIPE; | ||
612 | } | ||
613 | |||
614 | if (length > nvram_error_log_size) { | ||
615 | length = nvram_error_log_size; | ||
616 | } | ||
617 | |||
618 | info.error_type = err_type; | ||
619 | info.seq_num = error_log_cnt; | ||
620 | |||
621 | tmp_index = nvram_error_log_index; | ||
622 | |||
623 | rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); | ||
624 | if (rc <= 0) { | ||
625 | printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); | ||
626 | return rc; | ||
627 | } | ||
628 | |||
629 | rc = ppc_md.nvram_write(buff, length, &tmp_index); | ||
630 | if (rc <= 0) { | ||
631 | printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); | ||
632 | return rc; | ||
633 | } | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | /* nvram_read_error_log | ||
639 | * | ||
640 | * Reads nvram for error log for at most 'length' | ||
641 | */ | ||
642 | int nvram_read_error_log(char * buff, int length, | ||
643 | unsigned int * err_type, unsigned int * error_log_cnt) | ||
644 | { | ||
645 | int rc; | ||
646 | loff_t tmp_index; | ||
647 | struct err_log_info info; | ||
648 | |||
649 | if (nvram_error_log_index == -1) | ||
650 | return -1; | ||
651 | |||
652 | if (length > nvram_error_log_size) | ||
653 | length = nvram_error_log_size; | ||
654 | |||
655 | tmp_index = nvram_error_log_index; | ||
656 | |||
657 | rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index); | ||
658 | if (rc <= 0) { | ||
659 | printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); | ||
660 | return rc; | ||
661 | } | ||
662 | |||
663 | rc = ppc_md.nvram_read(buff, length, &tmp_index); | ||
664 | if (rc <= 0) { | ||
665 | printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); | ||
666 | return rc; | ||
667 | } | ||
668 | |||
669 | *error_log_cnt = info.seq_num; | ||
670 | *err_type = info.error_type; | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | /* This doesn't actually zero anything, but it sets the event_logged | ||
676 | * word to tell that this event is safely in syslog. | ||
677 | */ | ||
678 | int nvram_clear_error_log(void) | ||
679 | { | ||
680 | loff_t tmp_index; | ||
681 | int clear_word = ERR_FLAG_ALREADY_LOGGED; | ||
682 | int rc; | ||
683 | |||
684 | if (nvram_error_log_index == -1) | ||
685 | return -1; | ||
686 | |||
687 | tmp_index = nvram_error_log_index; | ||
688 | |||
689 | rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); | ||
690 | if (rc <= 0) { | ||
691 | printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc); | ||
692 | return rc; | ||
693 | } | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | #endif /* CONFIG_PPC_PSERIES */ | ||
699 | |||
700 | module_init(nvram_init); | 562 | module_init(nvram_init); |
701 | module_exit(nvram_cleanup); | 563 | module_exit(nvram_cleanup); |
702 | MODULE_LICENSE("GPL"); | 564 | MODULE_LICENSE("GPL"); |