]>
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 | ||
15 | /* OPAL in-memory console. Defined in OPAL source at core/console.c */ | |
16 | struct memcons { | |
17 | __be64 magic; | |
18 | #define MEMCONS_MAGIC 0x6630696567726173L | |
19 | __be64 obuf_phys; | |
20 | __be64 ibuf_phys; | |
21 | __be32 obuf_size; | |
22 | __be32 ibuf_size; | |
23 | __be32 out_pos; | |
24 | #define MEMCONS_OUT_POS_WRAP 0x80000000u | |
25 | #define MEMCONS_OUT_POS_MASK 0x00ffffffu | |
26 | __be32 in_prod; | |
27 | __be32 in_cons; | |
28 | }; | |
29 | ||
9b4fffa1 AD |
30 | static struct memcons *opal_memcons = NULL; |
31 | ||
32 | ssize_t opal_msglog_copy(char *to, loff_t pos, size_t count) | |
bfc36894 | 33 | { |
bfc36894 | 34 | const char *conbuf; |
caf69ba6 JS |
35 | ssize_t ret; |
36 | size_t first_read = 0; | |
bfc36894 JS |
37 | uint32_t out_pos, avail; |
38 | ||
9b4fffa1 | 39 | if (!opal_memcons) |
bfc36894 JS |
40 | return -ENODEV; |
41 | ||
6aa7de05 | 42 | out_pos = be32_to_cpu(READ_ONCE(opal_memcons->out_pos)); |
bfc36894 JS |
43 | |
44 | /* Now we've read out_pos, put a barrier in before reading the new | |
45 | * data it points to in conbuf. */ | |
46 | smp_rmb(); | |
47 | ||
9b4fffa1 | 48 | conbuf = phys_to_virt(be64_to_cpu(opal_memcons->obuf_phys)); |
bfc36894 JS |
49 | |
50 | /* When the buffer has wrapped, read from the out_pos marker to the end | |
51 | * of the buffer, and then read the remaining data as in the un-wrapped | |
52 | * case. */ | |
53 | if (out_pos & MEMCONS_OUT_POS_WRAP) { | |
54 | ||
55 | out_pos &= MEMCONS_OUT_POS_MASK; | |
9b4fffa1 | 56 | avail = be32_to_cpu(opal_memcons->obuf_size) - out_pos; |
bfc36894 JS |
57 | |
58 | ret = memory_read_from_buffer(to, count, &pos, | |
59 | conbuf + out_pos, avail); | |
60 | ||
61 | if (ret < 0) | |
62 | goto out; | |
63 | ||
64 | first_read = ret; | |
65 | to += first_read; | |
66 | count -= first_read; | |
67 | pos -= avail; | |
caf69ba6 JS |
68 | |
69 | if (count <= 0) | |
70 | goto out; | |
bfc36894 JS |
71 | } |
72 | ||
73 | /* Sanity check. The firmware should not do this to us. */ | |
9b4fffa1 | 74 | if (out_pos > be32_to_cpu(opal_memcons->obuf_size)) { |
bfc36894 JS |
75 | pr_err("OPAL: memory console corruption. Aborting read.\n"); |
76 | return -EINVAL; | |
77 | } | |
78 | ||
79 | ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); | |
80 | ||
81 | if (ret < 0) | |
82 | goto out; | |
83 | ||
84 | ret += first_read; | |
85 | out: | |
86 | return ret; | |
87 | } | |
88 | ||
9b4fffa1 AD |
89 | static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, |
90 | struct bin_attribute *bin_attr, char *to, | |
91 | loff_t pos, size_t count) | |
92 | { | |
93 | return opal_msglog_copy(to, pos, count); | |
94 | } | |
95 | ||
bfc36894 | 96 | static struct bin_attribute opal_msglog_attr = { |
7b62f9bd | 97 | .attr = {.name = "msglog", .mode = 0400}, |
bfc36894 JS |
98 | .read = opal_msglog_read |
99 | }; | |
100 | ||
101 | void __init opal_msglog_init(void) | |
102 | { | |
103 | u64 mcaddr; | |
104 | struct memcons *mc; | |
105 | ||
106 | if (of_property_read_u64(opal_node, "ibm,opal-memcons", &mcaddr)) { | |
107 | pr_warn("OPAL: Property ibm,opal-memcons not found, no message log\n"); | |
108 | return; | |
109 | } | |
110 | ||
111 | mc = phys_to_virt(mcaddr); | |
112 | if (!mc) { | |
113 | pr_warn("OPAL: memory console address is invalid\n"); | |
114 | return; | |
115 | } | |
116 | ||
117 | if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { | |
118 | pr_warn("OPAL: memory console version is invalid\n"); | |
119 | return; | |
120 | } | |
121 | ||
14a41d6b JS |
122 | /* Report maximum size */ |
123 | opal_msglog_attr.size = be32_to_cpu(mc->ibuf_size) + | |
124 | be32_to_cpu(mc->obuf_size); | |
125 | ||
9b4fffa1 AD |
126 | opal_memcons = mc; |
127 | } | |
bfc36894 | 128 | |
9b4fffa1 AD |
129 | void __init opal_msglog_sysfs_init(void) |
130 | { | |
88409d0c AD |
131 | if (!opal_memcons) { |
132 | pr_warn("OPAL: message log initialisation failed, not creating sysfs entry\n"); | |
133 | return; | |
134 | } | |
135 | ||
bfc36894 JS |
136 | if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0) |
137 | pr_warn("OPAL: sysfs file creation failed\n"); | |
138 | } |