Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
bfc36894 JS |
2 | /* |
3 | * PowerNV OPAL in-memory console interface | |
4 | * | |
5 | * Copyright 2014 IBM Corp. | |
bfc36894 JS |
6 | */ |
7 | ||
8 | #include <asm/io.h> | |
9 | #include <asm/opal.h> | |
10 | #include <linux/debugfs.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/types.h> | |
13 | #include <asm/barrier.h> | |
14 | ||
8471c1dd OH |
15 | #include "powernv.h" |
16 | ||
bfc36894 JS |
17 | /* OPAL in-memory console. Defined in OPAL source at core/console.c */ |
18 | struct memcons { | |
19 | __be64 magic; | |
20 | #define MEMCONS_MAGIC 0x6630696567726173L | |
21 | __be64 obuf_phys; | |
22 | __be64 ibuf_phys; | |
23 | __be32 obuf_size; | |
24 | __be32 ibuf_size; | |
25 | __be32 out_pos; | |
26 | #define MEMCONS_OUT_POS_WRAP 0x80000000u | |
27 | #define MEMCONS_OUT_POS_MASK 0x00ffffffu | |
28 | __be32 in_prod; | |
29 | __be32 in_cons; | |
30 | }; | |
31 | ||
9b4fffa1 AD |
32 | static struct memcons *opal_memcons = NULL; |
33 | ||
dea45ea7 | 34 | ssize_t memcons_copy(struct memcons *mc, char *to, loff_t pos, size_t count) |
bfc36894 | 35 | { |
bfc36894 | 36 | const char *conbuf; |
caf69ba6 JS |
37 | ssize_t ret; |
38 | size_t first_read = 0; | |
bfc36894 JS |
39 | uint32_t out_pos, avail; |
40 | ||
dea45ea7 | 41 | if (!mc) |
bfc36894 JS |
42 | return -ENODEV; |
43 | ||
dea45ea7 | 44 | out_pos = be32_to_cpu(READ_ONCE(mc->out_pos)); |
bfc36894 JS |
45 | |
46 | /* Now we've read out_pos, put a barrier in before reading the new | |
47 | * data it points to in conbuf. */ | |
48 | smp_rmb(); | |
49 | ||
dea45ea7 | 50 | conbuf = phys_to_virt(be64_to_cpu(mc->obuf_phys)); |
bfc36894 JS |
51 | |
52 | /* When the buffer has wrapped, read from the out_pos marker to the end | |
53 | * of the buffer, and then read the remaining data as in the un-wrapped | |
54 | * case. */ | |
55 | if (out_pos & MEMCONS_OUT_POS_WRAP) { | |
56 | ||
57 | out_pos &= MEMCONS_OUT_POS_MASK; | |
dea45ea7 | 58 | avail = be32_to_cpu(mc->obuf_size) - out_pos; |
bfc36894 JS |
59 | |
60 | ret = memory_read_from_buffer(to, count, &pos, | |
61 | conbuf + out_pos, avail); | |
62 | ||
63 | if (ret < 0) | |
64 | goto out; | |
65 | ||
66 | first_read = ret; | |
67 | to += first_read; | |
68 | count -= first_read; | |
69 | pos -= avail; | |
caf69ba6 JS |
70 | |
71 | if (count <= 0) | |
72 | goto out; | |
bfc36894 JS |
73 | } |
74 | ||
75 | /* Sanity check. The firmware should not do this to us. */ | |
dea45ea7 | 76 | if (out_pos > be32_to_cpu(mc->obuf_size)) { |
bfc36894 JS |
77 | pr_err("OPAL: memory console corruption. Aborting read.\n"); |
78 | return -EINVAL; | |
79 | } | |
80 | ||
81 | ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); | |
82 | ||
83 | if (ret < 0) | |
84 | goto out; | |
85 | ||
86 | ret += first_read; | |
87 | out: | |
88 | return ret; | |
89 | } | |
90 | ||
dea45ea7 CC |
91 | ssize_t opal_msglog_copy(char *to, loff_t pos, size_t count) |
92 | { | |
93 | return memcons_copy(opal_memcons, to, pos, count); | |
94 | } | |
95 | ||
9b4fffa1 AD |
96 | static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, |
97 | struct bin_attribute *bin_attr, char *to, | |
98 | loff_t pos, size_t count) | |
99 | { | |
100 | return opal_msglog_copy(to, pos, count); | |
101 | } | |
102 | ||
bfc36894 | 103 | static struct bin_attribute opal_msglog_attr = { |
7b62f9bd | 104 | .attr = {.name = "msglog", .mode = 0400}, |
bfc36894 JS |
105 | .read = opal_msglog_read |
106 | }; | |
107 | ||
e5913db1 | 108 | struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name) |
bfc36894 JS |
109 | { |
110 | u64 mcaddr; | |
111 | struct memcons *mc; | |
112 | ||
dea45ea7 CC |
113 | if (of_property_read_u64(node, mc_prop_name, &mcaddr)) { |
114 | pr_warn("%s property not found, no message log\n", | |
115 | mc_prop_name); | |
116 | goto out_err; | |
bfc36894 JS |
117 | } |
118 | ||
119 | mc = phys_to_virt(mcaddr); | |
120 | if (!mc) { | |
dea45ea7 CC |
121 | pr_warn("memory console address is invalid\n"); |
122 | goto out_err; | |
bfc36894 JS |
123 | } |
124 | ||
125 | if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { | |
dea45ea7 CC |
126 | pr_warn("memory console version is invalid\n"); |
127 | goto out_err; | |
bfc36894 JS |
128 | } |
129 | ||
dea45ea7 CC |
130 | return mc; |
131 | ||
132 | out_err: | |
133 | return NULL; | |
134 | } | |
135 | ||
e5913db1 | 136 | u32 __init memcons_get_size(struct memcons *mc) |
dea45ea7 CC |
137 | { |
138 | return be32_to_cpu(mc->ibuf_size) + be32_to_cpu(mc->obuf_size); | |
139 | } | |
140 | ||
141 | void __init opal_msglog_init(void) | |
142 | { | |
143 | opal_memcons = memcons_init(opal_node, "ibm,opal-memcons"); | |
144 | if (!opal_memcons) { | |
145 | pr_warn("OPAL: memcons failed to load from ibm,opal-memcons\n"); | |
146 | return; | |
147 | } | |
14a41d6b | 148 | |
dea45ea7 | 149 | opal_msglog_attr.size = memcons_get_size(opal_memcons); |
9b4fffa1 | 150 | } |
bfc36894 | 151 | |
9b4fffa1 AD |
152 | void __init opal_msglog_sysfs_init(void) |
153 | { | |
88409d0c AD |
154 | if (!opal_memcons) { |
155 | pr_warn("OPAL: message log initialisation failed, not creating sysfs entry\n"); | |
156 | return; | |
157 | } | |
158 | ||
bfc36894 JS |
159 | if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0) |
160 | pr_warn("OPAL: sysfs file creation failed\n"); | |
161 | } |