diff options
author | Joel Stanley <joel@jms.id.au> | 2014-03-31 23:58:19 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-04-08 22:53:19 -0400 |
commit | bfc36894a48b996eba7e02d8e43093a289c1fb91 (patch) | |
tree | a2429363b192b759a0d4d27c8403f062a6fbbaf3 | |
parent | 6e556b471036b751aaa1a1b5a189eff76b1a2d0b (diff) |
powerpc/powernv: Add OPAL message log interface
OPAL provides an in-memory circular buffer containing a message log
populated with various runtime messages produced by the firmware.
Provide a sysfs interface /sys/firmware/opal/msglog for userspace to
view the messages.
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/include/asm/opal.h | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-msglog.c | 120 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 4 |
4 files changed, 128 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index a13ab397edda..05f9455615d6 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h | |||
@@ -730,6 +730,9 @@ typedef struct oppanel_line { | |||
730 | /* /sys/firmware/opal */ | 730 | /* /sys/firmware/opal */ |
731 | extern struct kobject *opal_kobj; | 731 | extern struct kobject *opal_kobj; |
732 | 732 | ||
733 | /* /ibm,opal */ | ||
734 | extern struct device_node *opal_node; | ||
735 | |||
733 | /* API functions */ | 736 | /* API functions */ |
734 | int64_t opal_console_write(int64_t term_number, __be64 *length, | 737 | int64_t opal_console_write(int64_t term_number, __be64 *length, |
735 | const uint8_t *buffer); | 738 | const uint8_t *buffer); |
@@ -920,6 +923,7 @@ extern void opal_flash_init(void); | |||
920 | extern int opal_elog_init(void); | 923 | extern int opal_elog_init(void); |
921 | extern void opal_platform_dump_init(void); | 924 | extern void opal_platform_dump_init(void); |
922 | extern void opal_sys_param_init(void); | 925 | extern void opal_sys_param_init(void); |
926 | extern void opal_msglog_init(void); | ||
923 | 927 | ||
924 | extern int opal_machine_check(struct pt_regs *regs); | 928 | extern int opal_machine_check(struct pt_regs *regs); |
925 | extern bool opal_mce_check_early_recovery(struct pt_regs *regs); | 929 | extern bool opal_mce_check_early_recovery(struct pt_regs *regs); |
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index f324ea099503..63cebb9b4d45 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o | 1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o |
2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o | 2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o |
3 | obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o | 3 | obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o |
4 | obj-y += opal-msglog.o | ||
4 | 5 | ||
5 | obj-$(CONFIG_SMP) += smp.o | 6 | obj-$(CONFIG_SMP) += smp.o |
6 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o | 7 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o |
diff --git a/arch/powerpc/platforms/powernv/opal-msglog.c b/arch/powerpc/platforms/powernv/opal-msglog.c new file mode 100644 index 000000000000..1bb25b952504 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-msglog.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * PowerNV OPAL in-memory console interface | ||
3 | * | ||
4 | * Copyright 2014 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <asm/io.h> | ||
13 | #include <asm/opal.h> | ||
14 | #include <linux/debugfs.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <asm/barrier.h> | ||
18 | |||
19 | /* OPAL in-memory console. Defined in OPAL source at core/console.c */ | ||
20 | struct memcons { | ||
21 | __be64 magic; | ||
22 | #define MEMCONS_MAGIC 0x6630696567726173L | ||
23 | __be64 obuf_phys; | ||
24 | __be64 ibuf_phys; | ||
25 | __be32 obuf_size; | ||
26 | __be32 ibuf_size; | ||
27 | __be32 out_pos; | ||
28 | #define MEMCONS_OUT_POS_WRAP 0x80000000u | ||
29 | #define MEMCONS_OUT_POS_MASK 0x00ffffffu | ||
30 | __be32 in_prod; | ||
31 | __be32 in_cons; | ||
32 | }; | ||
33 | |||
34 | static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, | ||
35 | struct bin_attribute *bin_attr, char *to, | ||
36 | loff_t pos, size_t count) | ||
37 | { | ||
38 | struct memcons *mc = bin_attr->private; | ||
39 | const char *conbuf; | ||
40 | size_t ret, first_read = 0; | ||
41 | uint32_t out_pos, avail; | ||
42 | |||
43 | if (!mc) | ||
44 | return -ENODEV; | ||
45 | |||
46 | out_pos = be32_to_cpu(ACCESS_ONCE(mc->out_pos)); | ||
47 | |||
48 | /* Now we've read out_pos, put a barrier in before reading the new | ||
49 | * data it points to in conbuf. */ | ||
50 | smp_rmb(); | ||
51 | |||
52 | conbuf = phys_to_virt(be64_to_cpu(mc->obuf_phys)); | ||
53 | |||
54 | /* When the buffer has wrapped, read from the out_pos marker to the end | ||
55 | * of the buffer, and then read the remaining data as in the un-wrapped | ||
56 | * case. */ | ||
57 | if (out_pos & MEMCONS_OUT_POS_WRAP) { | ||
58 | |||
59 | out_pos &= MEMCONS_OUT_POS_MASK; | ||
60 | avail = be32_to_cpu(mc->obuf_size) - out_pos; | ||
61 | |||
62 | ret = memory_read_from_buffer(to, count, &pos, | ||
63 | conbuf + out_pos, avail); | ||
64 | |||
65 | if (ret < 0) | ||
66 | goto out; | ||
67 | |||
68 | first_read = ret; | ||
69 | to += first_read; | ||
70 | count -= first_read; | ||
71 | pos -= avail; | ||
72 | } | ||
73 | |||
74 | /* Sanity check. The firmware should not do this to us. */ | ||
75 | if (out_pos > be32_to_cpu(mc->obuf_size)) { | ||
76 | pr_err("OPAL: memory console corruption. Aborting read.\n"); | ||
77 | return -EINVAL; | ||
78 | } | ||
79 | |||
80 | ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); | ||
81 | |||
82 | if (ret < 0) | ||
83 | goto out; | ||
84 | |||
85 | ret += first_read; | ||
86 | out: | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static struct bin_attribute opal_msglog_attr = { | ||
91 | .attr = {.name = "msglog", .mode = 0444}, | ||
92 | .read = opal_msglog_read | ||
93 | }; | ||
94 | |||
95 | void __init opal_msglog_init(void) | ||
96 | { | ||
97 | u64 mcaddr; | ||
98 | struct memcons *mc; | ||
99 | |||
100 | if (of_property_read_u64(opal_node, "ibm,opal-memcons", &mcaddr)) { | ||
101 | pr_warn("OPAL: Property ibm,opal-memcons not found, no message log\n"); | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | mc = phys_to_virt(mcaddr); | ||
106 | if (!mc) { | ||
107 | pr_warn("OPAL: memory console address is invalid\n"); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { | ||
112 | pr_warn("OPAL: memory console version is invalid\n"); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | opal_msglog_attr.private = mc; | ||
117 | |||
118 | if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0) | ||
119 | pr_warn("OPAL: sysfs file creation failed\n"); | ||
120 | } | ||
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 3697772e3759..99e9c2887e21 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c | |||
@@ -46,7 +46,7 @@ struct mcheck_recoverable_range { | |||
46 | static struct mcheck_recoverable_range *mc_recoverable_range; | 46 | static struct mcheck_recoverable_range *mc_recoverable_range; |
47 | static int mc_recoverable_range_len; | 47 | static int mc_recoverable_range_len; |
48 | 48 | ||
49 | static struct device_node *opal_node; | 49 | struct device_node *opal_node; |
50 | static DEFINE_SPINLOCK(opal_write_lock); | 50 | static DEFINE_SPINLOCK(opal_write_lock); |
51 | extern u64 opal_mc_secondary_handler[]; | 51 | extern u64 opal_mc_secondary_handler[]; |
52 | static unsigned int *opal_irqs; | 52 | static unsigned int *opal_irqs; |
@@ -602,6 +602,8 @@ static int __init opal_init(void) | |||
602 | opal_platform_dump_init(); | 602 | opal_platform_dump_init(); |
603 | /* Setup system parameters interface */ | 603 | /* Setup system parameters interface */ |
604 | opal_sys_param_init(); | 604 | opal_sys_param_init(); |
605 | /* Setup message log interface. */ | ||
606 | opal_msglog_init(); | ||
605 | } | 607 | } |
606 | 608 | ||
607 | return 0; | 609 | return 0; |