1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#include <linux/capability.h>
40#include <linux/cpu.h>
41#include <linux/types.h>
42#include <linux/proc_fs.h>
43#include <linux/module.h>
44#include <linux/smp.h>
45#include <linux/timer.h>
46#include <linux/vmalloc.h>
47#include <linux/semaphore.h>
48
49#include <asm/sal.h>
50#include <asm/uaccess.h>
51
52MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>");
53MODULE_DESCRIPTION("/proc interface to IA-64 SAL features");
54MODULE_LICENSE("GPL");
55
56static int salinfo_read(char *page, char **start, off_t off, int count, int *eof, void *data);
57
58typedef struct {
59 const char *name;
60 unsigned long feature;
61 struct proc_dir_entry *entry;
62} salinfo_entry_t;
63
64
65
66
67
68static salinfo_entry_t salinfo_entries[]={
69 { "bus_lock", IA64_SAL_PLATFORM_FEATURE_BUS_LOCK, },
70 { "irq_redirection", IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT, },
71 { "ipi_redirection", IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT, },
72 { "itc_drift", IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT, },
73};
74
75#define NR_SALINFO_ENTRIES ARRAY_SIZE(salinfo_entries)
76
77static char *salinfo_log_name[] = {
78 "mca",
79 "init",
80 "cmc",
81 "cpe",
82};
83
84static struct proc_dir_entry *salinfo_proc_entries[
85 ARRAY_SIZE(salinfo_entries) +
86 ARRAY_SIZE(salinfo_log_name) +
87 (2 * ARRAY_SIZE(salinfo_log_name)) +
88 1];
89
90
91
92
93struct salinfo_data_saved {
94 u8* buffer;
95 u64 size;
96 u64 id;
97 int cpu;
98};
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135enum salinfo_state {
136 STATE_NO_DATA,
137 STATE_LOG_RECORD,
138 STATE_OEMDATA,
139};
140
141struct salinfo_data {
142 cpumask_t cpu_event;
143 struct semaphore mutex;
144 u8 *log_buffer;
145 u64 log_size;
146 u8 *oemdata;
147 u64 oemdata_size;
148 int open;
149 u8 type;
150 u8 saved_num;
151 enum salinfo_state state :8;
152 u8 padding;
153 int cpu_check;
154 struct salinfo_data_saved data_saved[5];
155};
156
157static struct salinfo_data salinfo_data[ARRAY_SIZE(salinfo_log_name)];
158
159static DEFINE_SPINLOCK(data_lock);
160static DEFINE_SPINLOCK(data_saved_lock);
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175int (*salinfo_platform_oemdata)(const u8 *sect_header, u8 **oemdata, u64 *oemdata_size);
176
177struct salinfo_platform_oemdata_parms {
178 const u8 *efi_guid;
179 u8 **oemdata;
180 u64 *oemdata_size;
181 int ret;
182};
183
184
185
186
187
188
189
190
191
192static void
193salinfo_work_to_do(struct salinfo_data *data)
194{
195 (void)(down_trylock(&data->mutex) ?: 0);
196 up(&data->mutex);
197}
198
199static void
200salinfo_platform_oemdata_cpu(void *context)
201{
202 struct salinfo_platform_oemdata_parms *parms = context;
203 parms->ret = salinfo_platform_oemdata(parms->efi_guid, parms->oemdata, parms->oemdata_size);
204}
205
206static void
207shift1_data_saved (struct salinfo_data *data, int shift)
208{
209 memcpy(data->data_saved+shift, data->data_saved+shift+1,
210 (ARRAY_SIZE(data->data_saved) - (shift+1)) * sizeof(data->data_saved[0]));
211 memset(data->data_saved + ARRAY_SIZE(data->data_saved) - 1, 0,
212 sizeof(data->data_saved[0]));
213}
214
215
216
217
218
219
220
221
222
223
224
225
226void
227salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe)
228{
229 struct salinfo_data *data = salinfo_data + type;
230 struct salinfo_data_saved *data_saved;
231 unsigned long flags = 0;
232 int i;
233 int saved_size = ARRAY_SIZE(data->data_saved);
234
235 BUG_ON(type >= ARRAY_SIZE(salinfo_log_name));
236
237 if (irqsafe)
238 spin_lock_irqsave(&data_saved_lock, flags);
239 if (buffer) {
240 for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) {
241 if (!data_saved->buffer)
242 break;
243 }
244 if (i == saved_size) {
245 if (!data->saved_num) {
246 shift1_data_saved(data, 0);
247 data_saved = data->data_saved + saved_size - 1;
248 } else
249 data_saved = NULL;
250 }
251 if (data_saved) {
252 data_saved->cpu = smp_processor_id();
253 data_saved->id = ((sal_log_record_header_t *)buffer)->id;
254 data_saved->size = size;
255 data_saved->buffer = buffer;
256 }
257 }
258 cpu_set(smp_processor_id(), data->cpu_event);
259 if (irqsafe) {
260 salinfo_work_to_do(data);
261 spin_unlock_irqrestore(&data_saved_lock, flags);
262 }
263}
264
265
266#define SALINFO_TIMER_DELAY (60*HZ)
267static struct timer_list salinfo_timer;
268extern void ia64_mlogbuf_dump(void);
269
270static void
271salinfo_timeout_check(struct salinfo_data *data)
272{
273 unsigned long flags;
274 if (!data->open)
275 return;
276 if (!cpus_empty(data->cpu_event)) {
277 spin_lock_irqsave(&data_saved_lock, flags);
278 salinfo_work_to_do(data);
279 spin_unlock_irqrestore(&data_saved_lock, flags);
280 }
281}
282
283static void
284salinfo_timeout (unsigned long arg)
285{
286 ia64_mlogbuf_dump();
287 salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA);
288 salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_INIT);
289 salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY;
290 add_timer(&salinfo_timer);
291}
292
293static int
294salinfo_event_open(struct inode *inode, struct file *file)
295{
296 if (!capable(CAP_SYS_ADMIN))
297 return -EPERM;
298 return 0;
299}
300
301static ssize_t
302salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
303{
304 struct inode *inode = file->f_path.dentry->d_inode;
305 struct proc_dir_entry *entry = PDE(inode);
306 struct salinfo_data *data = entry->data;
307 char cmd[32];
308 size_t size;
309 int i, n, cpu = -1;
310
311retry:
312 if (cpus_empty(data->cpu_event) && down_trylock(&data->mutex)) {
313 if (file->f_flags & O_NONBLOCK)
314 return -EAGAIN;
315 if (down_interruptible(&data->mutex))
316 return -EINTR;
317 }
318
319 n = data->cpu_check;
320 for (i = 0; i < nr_cpu_ids; i++) {
321 if (cpu_isset(n, data->cpu_event)) {
322 if (!cpu_online(n)) {
323 cpu_clear(n, data->cpu_event);
324 continue;
325 }
326 cpu = n;
327 break;
328 }
329 if (++n == nr_cpu_ids)
330 n = 0;
331 }
332
333 if (cpu == -1)
334 goto retry;
335
336 ia64_mlogbuf_dump();
337
338
339 data->cpu_check = cpu;
340 if (++data->cpu_check == nr_cpu_ids)
341 data->cpu_check = 0;
342
343 snprintf(cmd, sizeof(cmd), "read %d\n", cpu);
344
345 size = strlen(cmd);
346 if (size > count)
347 size = count;
348 if (copy_to_user(buffer, cmd, size))
349 return -EFAULT;
350
351 return size;
352}
353
354static const struct file_operations salinfo_event_fops = {
355 .open = salinfo_event_open,
356 .read = salinfo_event_read,
357 .llseek = noop_llseek,
358};
359
360static int
361salinfo_log_open(struct inode *inode, struct file *file)
362{
363 struct proc_dir_entry *entry = PDE(inode);
364 struct salinfo_data *data = entry->data;
365
366 if (!capable(CAP_SYS_ADMIN))
367 return -EPERM;
368
369 spin_lock(&data_lock);
370 if (data->open) {
371 spin_unlock(&data_lock);
372 return -EBUSY;
373 }
374 data->open = 1;
375 spin_unlock(&data_lock);
376
377 if (data->state == STATE_NO_DATA &&
378 !(data->log_buffer = vmalloc(ia64_sal_get_state_info_size(data->type)))) {
379 data->open = 0;
380 return -ENOMEM;
381 }
382
383 return 0;
384}
385
386static int
387salinfo_log_release(struct inode *inode, struct file *file)
388{
389 struct proc_dir_entry *entry = PDE(inode);
390 struct salinfo_data *data = entry->data;
391
392 if (data->state == STATE_NO_DATA) {
393 vfree(data->log_buffer);
394 vfree(data->oemdata);
395 data->log_buffer = NULL;
396 data->oemdata = NULL;
397 }
398 spin_lock(&data_lock);
399 data->open = 0;
400 spin_unlock(&data_lock);
401 return 0;
402}
403
404static void
405call_on_cpu(int cpu, void (*fn)(void *), void *arg)
406{
407 cpumask_t save_cpus_allowed = current->cpus_allowed;
408 set_cpus_allowed_ptr(current, cpumask_of(cpu));
409 (*fn)(arg);
410 set_cpus_allowed_ptr(current, &save_cpus_allowed);
411}
412
413static void
414salinfo_log_read_cpu(void *context)
415{
416 struct salinfo_data *data = context;
417 sal_log_record_header_t *rh;
418 data->log_size = ia64_sal_get_state_info(data->type, (u64 *) data->log_buffer);
419 rh = (sal_log_record_header_t *)(data->log_buffer);
420
421 if (rh->severity == sal_log_severity_corrected)
422 ia64_sal_clear_state_info(data->type);
423}
424
425static void
426salinfo_log_new_read(int cpu, struct salinfo_data *data)
427{
428 struct salinfo_data_saved *data_saved;
429 unsigned long flags;
430 int i;
431 int saved_size = ARRAY_SIZE(data->data_saved);
432
433 data->saved_num = 0;
434 spin_lock_irqsave(&data_saved_lock, flags);
435retry:
436 for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) {
437 if (data_saved->buffer && data_saved->cpu == cpu) {
438 sal_log_record_header_t *rh = (sal_log_record_header_t *)(data_saved->buffer);
439 data->log_size = data_saved->size;
440 memcpy(data->log_buffer, rh, data->log_size);
441 barrier();
442 if (rh->id == data_saved->id) {
443 data->saved_num = i+1;
444 break;
445 }
446
447 shift1_data_saved(data, i);
448 goto retry;
449 }
450 }
451 spin_unlock_irqrestore(&data_saved_lock, flags);
452
453 if (!data->saved_num)
454 call_on_cpu(cpu, salinfo_log_read_cpu, data);
455 if (!data->log_size) {
456 data->state = STATE_NO_DATA;
457 cpu_clear(cpu, data->cpu_event);
458 } else {
459 data->state = STATE_LOG_RECORD;
460 }
461}
462
463static ssize_t
464salinfo_log_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
465{
466 struct inode *inode = file->f_path.dentry->d_inode;
467 struct proc_dir_entry *entry = PDE(inode);
468 struct salinfo_data *data = entry->data;
469 u8 *buf;
470 u64 bufsize;
471
472 if (data->state == STATE_LOG_RECORD) {
473 buf = data->log_buffer;
474 bufsize = data->log_size;
475 } else if (data->state == STATE_OEMDATA) {
476 buf = data->oemdata;
477 bufsize = data->oemdata_size;
478 } else {
479 buf = NULL;
480 bufsize = 0;
481 }
482 return simple_read_from_buffer(buffer, count, ppos, buf, bufsize);
483}
484
485static void
486salinfo_log_clear_cpu(void *context)
487{
488 struct salinfo_data *data = context;
489 ia64_sal_clear_state_info(data->type);
490}
491
492static int
493salinfo_log_clear(struct salinfo_data *data, int cpu)
494{
495 sal_log_record_header_t *rh;
496 unsigned long flags;
497 spin_lock_irqsave(&data_saved_lock, flags);
498 data->state = STATE_NO_DATA;
499 if (!cpu_isset(cpu, data->cpu_event)) {
500 spin_unlock_irqrestore(&data_saved_lock, flags);
501 return 0;
502 }
503 cpu_clear(cpu, data->cpu_event);
504 if (data->saved_num) {
505 shift1_data_saved(data, data->saved_num - 1);
506 data->saved_num = 0;
507 }
508 spin_unlock_irqrestore(&data_saved_lock, flags);
509 rh = (sal_log_record_header_t *)(data->log_buffer);
510
511 if (rh->severity != sal_log_severity_corrected)
512 call_on_cpu(cpu, salinfo_log_clear_cpu, data);
513
514 salinfo_log_new_read(cpu, data);
515 if (data->state == STATE_LOG_RECORD) {
516 spin_lock_irqsave(&data_saved_lock, flags);
517 cpu_set(cpu, data->cpu_event);
518 salinfo_work_to_do(data);
519 spin_unlock_irqrestore(&data_saved_lock, flags);
520 }
521 return 0;
522}
523
524static ssize_t
525salinfo_log_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
526{
527 struct inode *inode = file->f_path.dentry->d_inode;
528 struct proc_dir_entry *entry = PDE(inode);
529 struct salinfo_data *data = entry->data;
530 char cmd[32];
531 size_t size;
532 u32 offset;
533 int cpu;
534
535 size = sizeof(cmd);
536 if (count < size)
537 size = count;
538 if (copy_from_user(cmd, buffer, size))
539 return -EFAULT;
540
541 if (sscanf(cmd, "read %d", &cpu) == 1) {
542 salinfo_log_new_read(cpu, data);
543 } else if (sscanf(cmd, "clear %d", &cpu) == 1) {
544 int ret;
545 if ((ret = salinfo_log_clear(data, cpu)))
546 count = ret;
547 } else if (sscanf(cmd, "oemdata %d %d", &cpu, &offset) == 2) {
548 if (data->state != STATE_LOG_RECORD && data->state != STATE_OEMDATA)
549 return -EINVAL;
550 if (offset > data->log_size - sizeof(efi_guid_t))
551 return -EINVAL;
552 data->state = STATE_OEMDATA;
553 if (salinfo_platform_oemdata) {
554 struct salinfo_platform_oemdata_parms parms = {
555 .efi_guid = data->log_buffer + offset,
556 .oemdata = &data->oemdata,
557 .oemdata_size = &data->oemdata_size
558 };
559 call_on_cpu(cpu, salinfo_platform_oemdata_cpu, &parms);
560 if (parms.ret)
561 count = parms.ret;
562 } else
563 data->oemdata_size = 0;
564 } else
565 return -EINVAL;
566
567 return count;
568}
569
570static const struct file_operations salinfo_data_fops = {
571 .open = salinfo_log_open,
572 .release = salinfo_log_release,
573 .read = salinfo_log_read,
574 .write = salinfo_log_write,
575 .llseek = default_llseek,
576};
577
578static int __cpuinit
579salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
580{
581 unsigned int i, cpu = (unsigned long)hcpu;
582 unsigned long flags;
583 struct salinfo_data *data;
584 switch (action) {
585 case CPU_ONLINE:
586 case CPU_ONLINE_FROZEN:
587 spin_lock_irqsave(&data_saved_lock, flags);
588 for (i = 0, data = salinfo_data;
589 i < ARRAY_SIZE(salinfo_data);
590 ++i, ++data) {
591 cpu_set(cpu, data->cpu_event);
592 salinfo_work_to_do(data);
593 }
594 spin_unlock_irqrestore(&data_saved_lock, flags);
595 break;
596 case CPU_DEAD:
597 case CPU_DEAD_FROZEN:
598 spin_lock_irqsave(&data_saved_lock, flags);
599 for (i = 0, data = salinfo_data;
600 i < ARRAY_SIZE(salinfo_data);
601 ++i, ++data) {
602 struct salinfo_data_saved *data_saved;
603 int j;
604 for (j = ARRAY_SIZE(data->data_saved) - 1, data_saved = data->data_saved + j;
605 j >= 0;
606 --j, --data_saved) {
607 if (data_saved->buffer && data_saved->cpu == cpu) {
608 shift1_data_saved(data, j);
609 }
610 }
611 cpu_clear(cpu, data->cpu_event);
612 }
613 spin_unlock_irqrestore(&data_saved_lock, flags);
614 break;
615 }
616 return NOTIFY_OK;
617}
618
619static struct notifier_block salinfo_cpu_notifier __cpuinitdata =
620{
621 .notifier_call = salinfo_cpu_callback,
622 .priority = 0,
623};
624
625static int __init
626salinfo_init(void)
627{
628 struct proc_dir_entry *salinfo_dir;
629 struct proc_dir_entry **sdir = salinfo_proc_entries;
630 struct proc_dir_entry *dir, *entry;
631 struct salinfo_data *data;
632 int i, j;
633
634 salinfo_dir = proc_mkdir("sal", NULL);
635 if (!salinfo_dir)
636 return 0;
637
638 for (i=0; i < NR_SALINFO_ENTRIES; i++) {
639
640 *sdir++ = create_proc_read_entry (salinfo_entries[i].name, 0, salinfo_dir,
641 salinfo_read, (void *)salinfo_entries[i].feature);
642 }
643
644 for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) {
645 data = salinfo_data + i;
646 data->type = i;
647 sema_init(&data->mutex, 1);
648 dir = proc_mkdir(salinfo_log_name[i], salinfo_dir);
649 if (!dir)
650 continue;
651
652 entry = proc_create_data("event", S_IRUSR, dir,
653 &salinfo_event_fops, data);
654 if (!entry)
655 continue;
656 *sdir++ = entry;
657
658 entry = proc_create_data("data", S_IRUSR | S_IWUSR, dir,
659 &salinfo_data_fops, data);
660 if (!entry)
661 continue;
662 *sdir++ = entry;
663
664
665 for_each_online_cpu(j)
666 cpu_set(j, data->cpu_event);
667
668 *sdir++ = dir;
669 }
670
671 *sdir++ = salinfo_dir;
672
673 init_timer(&salinfo_timer);
674 salinfo_timer.expires = jiffies + SALINFO_TIMER_DELAY;
675 salinfo_timer.function = &salinfo_timeout;
676 add_timer(&salinfo_timer);
677
678 register_hotcpu_notifier(&salinfo_cpu_notifier);
679
680 return 0;
681}
682
683
684
685
686
687static int
688salinfo_read(char *page, char **start, off_t off, int count, int *eof, void *data)
689{
690 int len = 0;
691
692 len = sprintf(page, (sal_platform_features & (unsigned long)data) ? "1\n" : "0\n");
693
694 if (len <= off+count) *eof = 1;
695
696 *start = page + off;
697 len -= off;
698
699 if (len>count) len = count;
700 if (len<0) len = 0;
701
702 return len;
703}
704
705module_init(salinfo_init);
706