1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2010, 2014, 2022 The Linux Foundation. All rights reserved. */ |
3 | |
4 | #include <linux/console.h> |
5 | #include <linux/cpu.h> |
6 | #include <linux/cpumask.h> |
7 | #include <linux/init.h> |
8 | #include <linux/kfifo.h> |
9 | #include <linux/serial.h> |
10 | #include <linux/serial_core.h> |
11 | #include <linux/smp.h> |
12 | #include <linux/spinlock.h> |
13 | |
14 | #include <asm/dcc.h> |
15 | #include <asm/processor.h> |
16 | |
17 | #include "hvc_console.h" |
18 | |
19 | /* DCC Status Bits */ |
20 | #define DCC_STATUS_RX (1 << 30) |
21 | #define DCC_STATUS_TX (1 << 29) |
22 | |
23 | #define DCC_INBUF_SIZE 128 |
24 | #define DCC_OUTBUF_SIZE 1024 |
25 | |
26 | /* Lock to serialize access to DCC fifo */ |
27 | static DEFINE_SPINLOCK(dcc_lock); |
28 | |
29 | static DEFINE_KFIFO(inbuf, u8, DCC_INBUF_SIZE); |
30 | static DEFINE_KFIFO(outbuf, u8, DCC_OUTBUF_SIZE); |
31 | |
32 | static void dcc_uart_console_putchar(struct uart_port *port, u8 ch) |
33 | { |
34 | while (__dcc_getstatus() & DCC_STATUS_TX) |
35 | cpu_relax(); |
36 | |
37 | __dcc_putchar(ch); |
38 | } |
39 | |
40 | static void dcc_early_write(struct console *con, const char *s, unsigned n) |
41 | { |
42 | struct earlycon_device *dev = con->data; |
43 | |
44 | uart_console_write(port: &dev->port, s, count: n, putchar: dcc_uart_console_putchar); |
45 | } |
46 | |
47 | static int __init dcc_early_console_setup(struct earlycon_device *device, |
48 | const char *opt) |
49 | { |
50 | unsigned int count = 0x4000000; |
51 | |
52 | while (--count && (__dcc_getstatus() & DCC_STATUS_TX)) |
53 | cpu_relax(); |
54 | |
55 | if (__dcc_getstatus() & DCC_STATUS_TX) |
56 | return -ENODEV; |
57 | |
58 | device->con->write = dcc_early_write; |
59 | |
60 | return 0; |
61 | } |
62 | |
63 | EARLYCON_DECLARE(dcc, dcc_early_console_setup); |
64 | |
65 | static ssize_t hvc_dcc_put_chars(uint32_t vt, const u8 *buf, size_t count) |
66 | { |
67 | size_t i; |
68 | |
69 | for (i = 0; i < count; i++) { |
70 | while (__dcc_getstatus() & DCC_STATUS_TX) |
71 | cpu_relax(); |
72 | |
73 | __dcc_putchar(buf[i]); |
74 | } |
75 | |
76 | return count; |
77 | } |
78 | |
79 | static ssize_t hvc_dcc_get_chars(uint32_t vt, u8 *buf, size_t count) |
80 | { |
81 | size_t i; |
82 | |
83 | for (i = 0; i < count; ++i) |
84 | if (__dcc_getstatus() & DCC_STATUS_RX) |
85 | buf[i] = __dcc_getchar(); |
86 | else |
87 | break; |
88 | |
89 | return i; |
90 | } |
91 | |
92 | /* |
93 | * Check if the DCC is enabled. If CONFIG_HVC_DCC_SERIALIZE_SMP is enabled, |
94 | * then we assume then this function will be called first on core0. That way, |
95 | * dcc_core0_available will be true only if it's available on core0. |
96 | */ |
97 | static bool hvc_dcc_check(void) |
98 | { |
99 | unsigned long time = jiffies + (HZ / 10); |
100 | static bool dcc_core0_available; |
101 | |
102 | /* |
103 | * If we're not on core 0, but we previously confirmed that DCC is |
104 | * active, then just return true. |
105 | */ |
106 | int cpu = get_cpu(); |
107 | |
108 | if (IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP) && cpu && dcc_core0_available) { |
109 | put_cpu(); |
110 | return true; |
111 | } |
112 | |
113 | put_cpu(); |
114 | |
115 | /* Write a test character to check if it is handled */ |
116 | __dcc_putchar('\n'); |
117 | |
118 | while (time_is_after_jiffies(time)) { |
119 | if (!(__dcc_getstatus() & DCC_STATUS_TX)) { |
120 | dcc_core0_available = true; |
121 | return true; |
122 | } |
123 | } |
124 | |
125 | return false; |
126 | } |
127 | |
128 | /* |
129 | * Workqueue function that writes the output FIFO to the DCC on core 0. |
130 | */ |
131 | static void dcc_put_work(struct work_struct *work) |
132 | { |
133 | unsigned char ch; |
134 | unsigned long irqflags; |
135 | |
136 | spin_lock_irqsave(&dcc_lock, irqflags); |
137 | |
138 | /* While there's data in the output FIFO, write it to the DCC */ |
139 | while (kfifo_get(&outbuf, &ch)) |
140 | hvc_dcc_put_chars(vt: 0, buf: &ch, count: 1); |
141 | |
142 | /* While we're at it, check for any input characters */ |
143 | while (!kfifo_is_full(&inbuf)) { |
144 | if (!hvc_dcc_get_chars(vt: 0, buf: &ch, count: 1)) |
145 | break; |
146 | kfifo_put(&inbuf, ch); |
147 | } |
148 | |
149 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
150 | } |
151 | |
152 | static DECLARE_WORK(dcc_pwork, dcc_put_work); |
153 | |
154 | /* |
155 | * Workqueue function that reads characters from DCC and puts them into the |
156 | * input FIFO. |
157 | */ |
158 | static void dcc_get_work(struct work_struct *work) |
159 | { |
160 | unsigned long irqflags; |
161 | u8 ch; |
162 | |
163 | /* |
164 | * Read characters from DCC and put them into the input FIFO, as |
165 | * long as there is room and we have characters to read. |
166 | */ |
167 | spin_lock_irqsave(&dcc_lock, irqflags); |
168 | |
169 | while (!kfifo_is_full(&inbuf)) { |
170 | if (!hvc_dcc_get_chars(vt: 0, buf: &ch, count: 1)) |
171 | break; |
172 | kfifo_put(&inbuf, ch); |
173 | } |
174 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
175 | } |
176 | |
177 | static DECLARE_WORK(dcc_gwork, dcc_get_work); |
178 | |
179 | /* |
180 | * Write characters directly to the DCC if we're on core 0 and the FIFO |
181 | * is empty, or write them to the FIFO if we're not. |
182 | */ |
183 | static ssize_t hvc_dcc0_put_chars(u32 vt, const u8 *buf, size_t count) |
184 | { |
185 | unsigned long irqflags; |
186 | ssize_t len; |
187 | |
188 | if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) |
189 | return hvc_dcc_put_chars(vt, buf, count); |
190 | |
191 | spin_lock_irqsave(&dcc_lock, irqflags); |
192 | if (smp_processor_id() || (!kfifo_is_empty(&outbuf))) { |
193 | len = kfifo_in(&outbuf, buf, count); |
194 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
195 | |
196 | /* |
197 | * We just push data to the output FIFO, so schedule the |
198 | * workqueue that will actually write that data to DCC. |
199 | * CPU hotplug is disabled in dcc_init so CPU0 cannot be |
200 | * offlined after the cpu online check. |
201 | */ |
202 | if (cpu_online(cpu: 0)) |
203 | schedule_work_on(cpu: 0, work: &dcc_pwork); |
204 | |
205 | return len; |
206 | } |
207 | |
208 | /* |
209 | * If we're already on core 0, and the FIFO is empty, then just |
210 | * write the data to DCC. |
211 | */ |
212 | len = hvc_dcc_put_chars(vt, buf, count); |
213 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
214 | |
215 | return len; |
216 | } |
217 | |
218 | /* |
219 | * Read characters directly from the DCC if we're on core 0 and the FIFO |
220 | * is empty, or read them from the FIFO if we're not. |
221 | */ |
222 | static ssize_t hvc_dcc0_get_chars(u32 vt, u8 *buf, size_t count) |
223 | { |
224 | unsigned long irqflags; |
225 | ssize_t len; |
226 | |
227 | if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) |
228 | return hvc_dcc_get_chars(vt, buf, count); |
229 | |
230 | spin_lock_irqsave(&dcc_lock, irqflags); |
231 | |
232 | if (smp_processor_id() || (!kfifo_is_empty(&inbuf))) { |
233 | len = kfifo_out(&inbuf, buf, count); |
234 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
235 | |
236 | /* |
237 | * If the FIFO was empty, there may be characters in the DCC |
238 | * that we haven't read yet. Schedule a workqueue to fill |
239 | * the input FIFO, so that the next time this function is |
240 | * called, we'll have data. CPU hotplug is disabled in dcc_init |
241 | * so CPU0 cannot be offlined after the cpu online check. |
242 | */ |
243 | if (!len && cpu_online(cpu: 0)) |
244 | schedule_work_on(cpu: 0, work: &dcc_gwork); |
245 | |
246 | return len; |
247 | } |
248 | |
249 | /* |
250 | * If we're already on core 0, and the FIFO is empty, then just |
251 | * read the data from DCC. |
252 | */ |
253 | len = hvc_dcc_get_chars(vt, buf, count); |
254 | spin_unlock_irqrestore(lock: &dcc_lock, flags: irqflags); |
255 | |
256 | return len; |
257 | } |
258 | |
259 | static const struct hv_ops hvc_dcc_get_put_ops = { |
260 | .get_chars = hvc_dcc0_get_chars, |
261 | .put_chars = hvc_dcc0_put_chars, |
262 | }; |
263 | |
264 | static int __init hvc_dcc_console_init(void) |
265 | { |
266 | int ret; |
267 | |
268 | if (!hvc_dcc_check()) |
269 | return -ENODEV; |
270 | |
271 | /* Returns -1 if error */ |
272 | ret = hvc_instantiate(vtermno: 0, index: 0, ops: &hvc_dcc_get_put_ops); |
273 | |
274 | return ret < 0 ? -ENODEV : 0; |
275 | } |
276 | console_initcall(hvc_dcc_console_init); |
277 | |
278 | static int __init hvc_dcc_init(void) |
279 | { |
280 | struct hvc_struct *p; |
281 | |
282 | if (!hvc_dcc_check()) |
283 | return -ENODEV; |
284 | |
285 | if (IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) { |
286 | pr_warn("\n" ); |
287 | pr_warn("********************************************************************\n" ); |
288 | pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n" ); |
289 | pr_warn("** **\n" ); |
290 | pr_warn("** HVC_DCC_SERIALIZE_SMP SUPPORT HAS BEEN ENABLED IN THIS KERNEL **\n" ); |
291 | pr_warn("** **\n" ); |
292 | pr_warn("** This means that this is a DEBUG kernel and unsafe for **\n" ); |
293 | pr_warn("** production use and has important feature like CPU hotplug **\n" ); |
294 | pr_warn("** disabled. **\n" ); |
295 | pr_warn("** **\n" ); |
296 | pr_warn("** If you see this message and you are not debugging the **\n" ); |
297 | pr_warn("** kernel, report this immediately to your vendor! **\n" ); |
298 | pr_warn("** **\n" ); |
299 | pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n" ); |
300 | pr_warn("********************************************************************\n" ); |
301 | |
302 | cpu_hotplug_disable(); |
303 | } |
304 | |
305 | p = hvc_alloc(vtermno: 0, data: 0, ops: &hvc_dcc_get_put_ops, outbuf_size: 128); |
306 | |
307 | return PTR_ERR_OR_ZERO(ptr: p); |
308 | } |
309 | device_initcall(hvc_dcc_init); |
310 | |