1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/module.h>
19
20#include <linux/types.h>
21#include <linux/errno.h>
22#include <linux/fs.h>
23#include <linux/miscdevice.h>
24#include <linux/fcntl.h>
25#include <linux/nvram.h>
26#include <linux/init.h>
27#include <linux/slab.h>
28#include <linux/spinlock.h>
29#include <asm/uaccess.h>
30#include <asm/nvram.h>
31#include <asm/rtas.h>
32#include <asm/prom.h>
33#include <asm/machdep.h>
34
35#undef DEBUG_NVRAM
36
37static struct nvram_partition * nvram_part;
38static long nvram_error_log_index = -1;
39static long nvram_error_log_size = 0;
40
41struct err_log_info {
42 int error_type;
43 unsigned int seq_num;
44};
45
46static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
47{
48 int size;
49
50 if (ppc_md.nvram_size == NULL)
51 return -ENODEV;
52 size = ppc_md.nvram_size();
53
54 switch (origin) {
55 case 1:
56 offset += file->f_pos;
57 break;
58 case 2:
59 offset += size;
60 break;
61 }
62 if (offset < 0)
63 return -EINVAL;
64 file->f_pos = offset;
65 return file->f_pos;
66}
67
68
69static ssize_t dev_nvram_read(struct file *file, char __user *buf,
70 size_t count, loff_t *ppos)
71{
72 ssize_t ret;
73 char *tmp = NULL;
74 ssize_t size;
75
76 ret = -ENODEV;
77 if (!ppc_md.nvram_size)
78 goto out;
79
80 ret = 0;
81 size = ppc_md.nvram_size();
82 if (*ppos >= size || size < 0)
83 goto out;
84
85 count = min_t(size_t, count, size - *ppos);
86 count = min(count, PAGE_SIZE);
87
88 ret = -ENOMEM;
89 tmp = kmalloc(count, GFP_KERNEL);
90 if (!tmp)
91 goto out;
92
93 ret = ppc_md.nvram_read(tmp, count, ppos);
94 if (ret <= 0)
95 goto out;
96
97 if (copy_to_user(buf, tmp, ret))
98 ret = -EFAULT;
99
100out:
101 kfree(tmp);
102 return ret;
103
104}
105
106static ssize_t dev_nvram_write(struct file *file, const char __user *buf,
107 size_t count, loff_t *ppos)
108{
109 ssize_t ret;
110 char *tmp = NULL;
111 ssize_t size;
112
113 ret = -ENODEV;
114 if (!ppc_md.nvram_size)
115 goto out;
116
117 ret = 0;
118 size = ppc_md.nvram_size();
119 if (*ppos >= size || size < 0)
120 goto out;
121
122 count = min_t(size_t, count, size - *ppos);
123 count = min(count, PAGE_SIZE);
124
125 ret = -ENOMEM;
126 tmp = kmalloc(count, GFP_KERNEL);
127 if (!tmp)
128 goto out;
129
130 ret = -EFAULT;
131 if (copy_from_user(tmp, buf, count))
132 goto out;
133
134 ret = ppc_md.nvram_write(tmp, count, ppos);
135
136out:
137 kfree(tmp);
138 return ret;
139
140}
141
142static int dev_nvram_ioctl(struct inode *inode, struct file *file,
143 unsigned int cmd, unsigned long arg)
144{
145 switch(cmd) {
146#ifdef CONFIG_PPC_PMAC
147 case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
148 printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
149 case IOC_NVRAM_GET_OFFSET: {
150 int part, offset;
151
152 if (!machine_is(powermac))
153 return -EINVAL;
154 if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
155 return -EFAULT;
156 if (part < pmac_nvram_OF || part > pmac_nvram_NR)
157 return -EINVAL;
158 offset = pmac_get_partition(part);
159 if (offset < 0)
160 return offset;
161 if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
162 return -EFAULT;
163 return 0;
164 }
165#endif
166 default:
167 return -EINVAL;
168 }
169}
170
171const struct file_operations nvram_fops = {
172 .owner = THIS_MODULE,
173 .llseek = dev_nvram_llseek,
174 .read = dev_nvram_read,
175 .write = dev_nvram_write,
176 .ioctl = dev_nvram_ioctl,
177};
178
179static struct miscdevice nvram_dev = {
180 NVRAM_MINOR,
181 "nvram",
182 &nvram_fops
183};
184
185
186#ifdef DEBUG_NVRAM
187static void nvram_print_partitions(char * label)
188{
189 struct list_head * p;
190 struct nvram_partition * tmp_part;
191
192 printk(KERN_WARNING "--------%s---------\n", label);
193 printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n");
194 list_for_each(p, &nvram_part->partition) {
195 tmp_part = list_entry(p, struct nvram_partition, partition);
196 printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%s\n",
197 tmp_part->index, tmp_part->header.signature,
198 tmp_part->header.checksum, tmp_part->header.length,
199 tmp_part->header.name);
200 }
201}
202#endif
203
204
205static int nvram_write_header(struct nvram_partition * part)
206{
207 loff_t tmp_index;
208 int rc;
209
210 tmp_index = part->index;
211 rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index);
212
213 return rc;
214}
215
216
217static unsigned char nvram_checksum(struct nvram_header *p)
218{
219 unsigned int c_sum, c_sum2;
220 unsigned short *sp = (unsigned short *)p->name;
221 c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5];
222
223
224 c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff;
225
226 c_sum2 = (c_sum >> 8) + (c_sum << 8);
227 c_sum = ((c_sum + c_sum2) >> 8) & 0xff;
228 return c_sum;
229}
230
231
232
233
234
235
236
237struct nvram_partition *nvram_find_partition(int sig, const char *name)
238{
239 struct nvram_partition * part;
240 struct list_head * p;
241
242 list_for_each(p, &nvram_part->partition) {
243 part = list_entry(p, struct nvram_partition, partition);
244
245 if (sig && part->header.signature != sig)
246 continue;
247 if (name && 0 != strncmp(name, part->header.name, 12))
248 continue;
249 return part;
250 }
251 return NULL;
252}
253EXPORT_SYMBOL(nvram_find_partition);
254
255
256static int nvram_remove_os_partition(void)
257{
258 struct list_head *i;
259 struct list_head *j;
260 struct nvram_partition * part;
261 struct nvram_partition * cur_part;
262 int rc;
263
264 list_for_each(i, &nvram_part->partition) {
265 part = list_entry(i, struct nvram_partition, partition);
266 if (part->header.signature != NVRAM_SIG_OS)
267 continue;
268
269
270 part->header.signature = NVRAM_SIG_FREE;
271 sprintf(part->header.name, "wwwwwwwwwwww");
272 part->header.checksum = nvram_checksum(&part->header);
273
274
275 list_for_each_prev(j, &part->partition) {
276 cur_part = list_entry(j, struct nvram_partition, partition);
277 if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
278 break;
279 }
280
281 part->header.length += cur_part->header.length;
282 part->header.checksum = nvram_checksum(&part->header);
283 part->index = cur_part->index;
284
285 list_del(&cur_part->partition);
286 kfree(cur_part);
287 j = &part->partition;
288 }
289
290
291 list_for_each(j, &part->partition) {
292 cur_part = list_entry(j, struct nvram_partition, partition);
293 if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
294 break;
295 }
296
297 part->header.length += cur_part->header.length;
298 part->header.checksum = nvram_checksum(&part->header);
299
300 list_del(&cur_part->partition);
301 kfree(cur_part);
302 j = &part->partition;
303 }
304
305 rc = nvram_write_header(part);
306 if (rc <= 0) {
307 printk(KERN_ERR "nvram_remove_os_partition: nvram_write failed (%d)\n", rc);
308 return rc;
309 }
310
311 }
312
313 return 0;
314}
315
316
317
318
319
320
321
322static int nvram_create_os_partition(void)
323{
324 struct nvram_partition *part;
325 struct nvram_partition *new_part;
326 struct nvram_partition *free_part = NULL;
327 int seq_init[2] = { 0, 0 };
328 loff_t tmp_index;
329 long size = 0;
330 int rc;
331
332
333
334 list_for_each_entry(part, &nvram_part->partition, partition) {
335 if (part->header.signature != NVRAM_SIG_FREE)
336 continue;
337
338 if (part->header.length >= NVRAM_MAX_REQ) {
339 size = NVRAM_MAX_REQ;
340 free_part = part;
341 break;
342 }
343 if (!size && part->header.length >= NVRAM_MIN_REQ) {
344 size = NVRAM_MIN_REQ;
345 free_part = part;
346 }
347 }
348 if (!size)
349 return -ENOSPC;
350
351
352 new_part = kmalloc(sizeof(*new_part), GFP_KERNEL);
353 if (!new_part) {
354 printk(KERN_ERR "nvram_create_os_partition: kmalloc failed\n");
355 return -ENOMEM;
356 }
357
358 new_part->index = free_part->index;
359 new_part->header.signature = NVRAM_SIG_OS;
360 new_part->header.length = size;
361 strcpy(new_part->header.name, "ppc64,linux");
362 new_part->header.checksum = nvram_checksum(&new_part->header);
363
364 rc = nvram_write_header(new_part);
365 if (rc <= 0) {
366 printk(KERN_ERR "nvram_create_os_partition: nvram_write_header \
367 failed (%d)\n", rc);
368 return rc;
369 }
370
371
372
373 tmp_index = new_part->index + NVRAM_HEADER_LEN;
374 rc = ppc_md.nvram_write((char *)&seq_init, sizeof(seq_init), &tmp_index);
375 if (rc <= 0) {
376 printk(KERN_ERR "nvram_create_os_partition: nvram_write "
377 "failed (%d)\n", rc);
378 return rc;
379 }
380
381 nvram_error_log_index = new_part->index + NVRAM_HEADER_LEN;
382 nvram_error_log_size = ((part->header.length - 1) *
383 NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
384
385 list_add_tail(&new_part->partition, &free_part->partition);
386
387 if (free_part->header.length <= size) {
388 list_del(&free_part->partition);
389 kfree(free_part);
390 return 0;
391 }
392
393
394 free_part->index += size * NVRAM_BLOCK_LEN;
395 free_part->header.length -= size;
396 free_part->header.checksum = nvram_checksum(&free_part->header);
397
398 rc = nvram_write_header(free_part);
399 if (rc <= 0) {
400 printk(KERN_ERR "nvram_create_os_partition: nvram_write_header "
401 "failed (%d)\n", rc);
402 return rc;
403 }
404
405 return 0;
406}
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425static int nvram_setup_partition(void)
426{
427 struct list_head * p;
428 struct nvram_partition * part;
429 int rc;
430
431
432
433
434
435
436 if (machine_is(powermac))
437 return -ENOSPC;
438
439
440
441
442 list_for_each(p, &nvram_part->partition) {
443 part = list_entry(p, struct nvram_partition, partition);
444 if (part->header.signature != NVRAM_SIG_OS)
445 continue;
446
447 if (strcmp(part->header.name, "ppc64,linux"))
448 continue;
449
450 if (part->header.length >= NVRAM_MIN_REQ) {
451
452 nvram_error_log_index = part->index + NVRAM_HEADER_LEN;
453 nvram_error_log_size = ((part->header.length - 1) *
454 NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
455 return 0;
456 }
457 }
458
459
460 rc = nvram_create_os_partition();
461 if (!rc) {
462 return 0;
463 }
464
465
466 rc = nvram_remove_os_partition();
467 if (rc) {
468 return rc;
469 }
470
471
472 rc = nvram_create_os_partition();
473 if (rc) {
474 printk(KERN_ERR "nvram_create_os_partition: Could not find a "
475 "NVRAM partition large enough\n");
476 return rc;
477 }
478
479 return 0;
480}
481
482
483static int nvram_scan_partitions(void)
484{
485 loff_t cur_index = 0;
486 struct nvram_header phead;
487 struct nvram_partition * tmp_part;
488 unsigned char c_sum;
489 char * header;
490 int total_size;
491 int err;
492
493 if (ppc_md.nvram_size == NULL)
494 return -ENODEV;
495 total_size = ppc_md.nvram_size();
496
497 header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL);
498 if (!header) {
499 printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n");
500 return -ENOMEM;
501 }
502
503 while (cur_index < total_size) {
504
505 err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index);
506 if (err != NVRAM_HEADER_LEN) {
507 printk(KERN_ERR "nvram_scan_partitions: Error parsing "
508 "nvram partitions\n");
509 goto out;
510 }
511
512 cur_index -= NVRAM_HEADER_LEN;
513
514 memcpy(&phead, header, NVRAM_HEADER_LEN);
515
516 err = 0;
517 c_sum = nvram_checksum(&phead);
518 if (c_sum != phead.checksum) {
519 printk(KERN_WARNING "WARNING: nvram partition checksum"
520 " was %02x, should be %02x!\n",
521 phead.checksum, c_sum);
522 printk(KERN_WARNING "Terminating nvram partition scan\n");
523 goto out;
524 }
525 if (!phead.length) {
526 printk(KERN_WARNING "WARNING: nvram corruption "
527 "detected: 0-length partition\n");
528 goto out;
529 }
530 tmp_part = (struct nvram_partition *)
531 kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
532 err = -ENOMEM;
533 if (!tmp_part) {
534 printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n");
535 goto out;
536 }
537
538 memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN);
539 tmp_part->index = cur_index;
540 list_add_tail(&tmp_part->partition, &nvram_part->partition);
541
542 cur_index += phead.length * NVRAM_BLOCK_LEN;
543 }
544 err = 0;
545
546 out:
547 kfree(header);
548 return err;
549}
550
551static int __init nvram_init(void)
552{
553 int error;
554 int rc;
555
556 if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
557 return -ENODEV;
558
559 rc = misc_register(&nvram_dev);
560 if (rc != 0) {
561 printk(KERN_ERR "nvram_init: failed to register device\n");
562 return rc;
563 }
564
565
566 nvram_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
567 if (!nvram_part) {
568 printk(KERN_ERR "nvram_init: Failed kmalloc\n");
569 return -ENOMEM;
570 }
571 INIT_LIST_HEAD(&nvram_part->partition);
572
573
574 error = nvram_scan_partitions();
575 if (error) {
576 printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n");
577 return error;
578 }
579
580 if(nvram_setup_partition())
581 printk(KERN_WARNING "nvram_init: Could not find nvram partition"
582 " for nvram buffered error logging.\n");
583
584#ifdef DEBUG_NVRAM
585 nvram_print_partitions("NVRAM Partitions");
586#endif
587
588 return rc;
589}
590
591void __exit nvram_cleanup(void)
592{
593 misc_deregister( &nvram_dev );
594}
595
596
597#ifdef CONFIG_PPC_PSERIES
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628int nvram_write_error_log(char * buff, int length,
629 unsigned int err_type, unsigned int error_log_cnt)
630{
631 int rc;
632 loff_t tmp_index;
633 struct err_log_info info;
634
635 if (nvram_error_log_index == -1) {
636 return -ESPIPE;
637 }
638
639 if (length > nvram_error_log_size) {
640 length = nvram_error_log_size;
641 }
642
643 info.error_type = err_type;
644 info.seq_num = error_log_cnt;
645
646 tmp_index = nvram_error_log_index;
647
648 rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
649 if (rc <= 0) {
650 printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
651 return rc;
652 }
653
654 rc = ppc_md.nvram_write(buff, length, &tmp_index);
655 if (rc <= 0) {
656 printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
657 return rc;
658 }
659
660 return 0;
661}
662
663
664
665
666
667int nvram_read_error_log(char * buff, int length,
668 unsigned int * err_type, unsigned int * error_log_cnt)
669{
670 int rc;
671 loff_t tmp_index;
672 struct err_log_info info;
673
674 if (nvram_error_log_index == -1)
675 return -1;
676
677 if (length > nvram_error_log_size)
678 length = nvram_error_log_size;
679
680 tmp_index = nvram_error_log_index;
681
682 rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
683 if (rc <= 0) {
684 printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
685 return rc;
686 }
687
688 rc = ppc_md.nvram_read(buff, length, &tmp_index);
689 if (rc <= 0) {
690 printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
691 return rc;
692 }
693
694 *error_log_cnt = info.seq_num;
695 *err_type = info.error_type;
696
697 return 0;
698}
699
700
701
702
703int nvram_clear_error_log(void)
704{
705 loff_t tmp_index;
706 int clear_word = ERR_FLAG_ALREADY_LOGGED;
707 int rc;
708
709 tmp_index = nvram_error_log_index;
710
711 rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
712 if (rc <= 0) {
713 printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
714 return rc;
715 }
716
717 return 0;
718}
719
720#endif
721
722module_init(nvram_init);
723module_exit(nvram_cleanup);
724MODULE_LICENSE("GPL");
725