Commit | Line | Data |
---|---|---|
bfc36894 JS |
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 | ||
9b4fffa1 AD |
34 | static struct memcons *opal_memcons = NULL; |
35 | ||
36 | ssize_t opal_msglog_copy(char *to, loff_t pos, size_t count) | |
bfc36894 | 37 | { |
bfc36894 | 38 | const char *conbuf; |
caf69ba6 JS |
39 | ssize_t ret; |
40 | size_t first_read = 0; | |
bfc36894 JS |
41 | uint32_t out_pos, avail; |
42 | ||
9b4fffa1 | 43 | if (!opal_memcons) |
bfc36894 JS |
44 | return -ENODEV; |
45 | ||
9b4fffa1 | 46 | out_pos = be32_to_cpu(ACCESS_ONCE(opal_memcons->out_pos)); |
bfc36894 JS |
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 | ||
9b4fffa1 | 52 | conbuf = phys_to_virt(be64_to_cpu(opal_memcons->obuf_phys)); |
bfc36894 JS |
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; | |
9b4fffa1 | 60 | avail = be32_to_cpu(opal_memcons->obuf_size) - out_pos; |
bfc36894 JS |
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; | |
caf69ba6 JS |
72 | |
73 | if (count <= 0) | |
74 | goto out; | |
bfc36894 JS |
75 | } |
76 | ||
77 | /* Sanity check. The firmware should not do this to us. */ | |
9b4fffa1 | 78 | if (out_pos > be32_to_cpu(opal_memcons->obuf_size)) { |
bfc36894 JS |
79 | pr_err("OPAL: memory console corruption. Aborting read.\n"); |
80 | return -EINVAL; | |
81 | } | |
82 | ||
83 | ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); | |
84 | ||
85 | if (ret < 0) | |
86 | goto out; | |
87 | ||
88 | ret += first_read; | |
89 | out: | |
90 | return ret; | |
91 | } | |
92 | ||
9b4fffa1 AD |
93 | static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, |
94 | struct bin_attribute *bin_attr, char *to, | |
95 | loff_t pos, size_t count) | |
96 | { | |
97 | return opal_msglog_copy(to, pos, count); | |
98 | } | |
99 | ||
bfc36894 JS |
100 | static struct bin_attribute opal_msglog_attr = { |
101 | .attr = {.name = "msglog", .mode = 0444}, | |
102 | .read = opal_msglog_read | |
103 | }; | |
104 | ||
105 | void __init opal_msglog_init(void) | |
106 | { | |
107 | u64 mcaddr; | |
108 | struct memcons *mc; | |
109 | ||
110 | if (of_property_read_u64(opal_node, "ibm,opal-memcons", &mcaddr)) { | |
111 | pr_warn("OPAL: Property ibm,opal-memcons not found, no message log\n"); | |
112 | return; | |
113 | } | |
114 | ||
115 | mc = phys_to_virt(mcaddr); | |
116 | if (!mc) { | |
117 | pr_warn("OPAL: memory console address is invalid\n"); | |
118 | return; | |
119 | } | |
120 | ||
121 | if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { | |
122 | pr_warn("OPAL: memory console version is invalid\n"); | |
123 | return; | |
124 | } | |
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 | } |