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