blob: 4edace1d9e4badeb45b37def1db23e46ab2aeb82 [file] [log] [blame]
Jag Raman55bd2132017-08-15 17:02:57 -04001/* vcc.c: sun4v virtual channel concentrator
2 *
3 * Copyright (C) 2017 Oracle. All rights reserved.
4 */
5
Jag Raman5d171052017-08-15 17:03:00 -04006#include <linux/delay.h>
7#include <linux/interrupt.h>
Jag Raman55bd2132017-08-15 17:02:57 -04008#include <linux/module.h>
Jag Raman5d171052017-08-15 17:03:00 -04009#include <linux/slab.h>
Jag Ramance808b72017-08-15 17:02:59 -040010#include <linux/tty.h>
Jag Raman5d171052017-08-15 17:03:00 -040011#include <asm/vio.h>
12#include <asm/ldc.h>
Jag Raman55bd2132017-08-15 17:02:57 -040013
Jag Ramanf283ebd2017-08-15 17:02:58 -040014#define DRV_MODULE_NAME "vcc"
15#define DRV_MODULE_VERSION "1.1"
16#define DRV_MODULE_RELDATE "July 1, 2017"
17
Jag Ramance808b72017-08-15 17:02:59 -040018static char version[] =
19 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")";
20
Jag Ramanf283ebd2017-08-15 17:02:58 -040021MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver");
22MODULE_LICENSE("GPL");
23MODULE_VERSION(DRV_MODULE_VERSION);
24
Jag Raman5d171052017-08-15 17:03:00 -040025struct vcc_port {
26 struct vio_driver_state vio;
27
28 spinlock_t lock;
29 char *domain;
30 struct tty_struct *tty; /* only populated while dev is open */
31 unsigned long index; /* index into the vcc_table */
32
33 u64 refcnt;
34 bool excl_locked;
35
36 bool removed;
37
38 /* This buffer is required to support the tty write_room interface
39 * and guarantee that any characters that the driver accepts will
40 * be eventually sent, either immediately or later.
41 */
42 int chars_in_buffer;
43 struct vio_vcc buffer;
44
45 struct timer_list rx_timer;
46 struct timer_list tx_timer;
47};
48
49/* Microseconds that thread will delay waiting for a vcc port ref */
50#define VCC_REF_DELAY 100
51
Jag Ramance808b72017-08-15 17:02:59 -040052#define VCC_MAX_PORTS 1024
53#define VCC_MINOR_START 0 /* must be zero */
54
55static const char vcc_driver_name[] = "vcc";
56static const char vcc_device_node[] = "vcc";
57static struct tty_driver *vcc_tty_driver;
58
Jag Raman5d171052017-08-15 17:03:00 -040059static struct vcc_port *vcc_table[VCC_MAX_PORTS];
60static DEFINE_SPINLOCK(vcc_table_lock);
61
Jag Ramanf283ebd2017-08-15 17:02:58 -040062int vcc_dbg;
63int vcc_dbg_ldc;
64int vcc_dbg_vio;
65
66module_param(vcc_dbg, uint, 0664);
67module_param(vcc_dbg_ldc, uint, 0664);
68module_param(vcc_dbg_vio, uint, 0664);
69
70#define VCC_DBG_DRV 0x1
71#define VCC_DBG_LDC 0x2
72#define VCC_DBG_PKT 0x4
73
74#define vccdbg(f, a...) \
75 do { \
76 if (vcc_dbg & VCC_DBG_DRV) \
77 pr_info(f, ## a); \
78 } while (0) \
79
80#define vccdbgl(l) \
81 do { \
82 if (vcc_dbg & VCC_DBG_LDC) \
83 ldc_print(l); \
84 } while (0) \
85
86#define vccdbgp(pkt) \
87 do { \
88 if (vcc_dbg & VCC_DBG_PKT) { \
89 int i; \
90 for (i = 0; i < pkt.tag.stype; i++) \
91 pr_info("[%c]", pkt.data[i]); \
92 } \
93 } while (0) \
94
Jag Ramance808b72017-08-15 17:02:59 -040095/* Note: Be careful when adding flags to this line discipline. Don't
96 * add anything that will cause echoing or we'll go into recursive
97 * loop echoing chars back and forth with the console drivers.
98 */
99static struct ktermios vcc_tty_termios = {
100 .c_iflag = IGNBRK | IGNPAR,
101 .c_oflag = OPOST,
102 .c_cflag = B38400 | CS8 | CREAD | HUPCL,
103 .c_cc = INIT_C_CC,
104 .c_ispeed = 38400,
105 .c_ospeed = 38400
106};
107
Jag Raman5d171052017-08-15 17:03:00 -0400108/**
109 * vcc_table_add() - Add VCC port to the VCC table
110 * @port: pointer to the VCC port
111 *
112 * Return: index of the port in the VCC table on success,
113 * -1 on failure
114 */
115static int vcc_table_add(struct vcc_port *port)
116{
117 unsigned long flags;
118 int i;
119
120 spin_lock_irqsave(&vcc_table_lock, flags);
121 for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) {
122 if (!vcc_table[i]) {
123 vcc_table[i] = port;
124 break;
125 }
126 }
127 spin_unlock_irqrestore(&vcc_table_lock, flags);
128
129 if (i < VCC_MAX_PORTS)
130 return i;
131 else
132 return -1;
133}
134
135/**
136 * vcc_table_remove() - Removes a VCC port from the VCC table
137 * @index: Index into the VCC table
138 */
139static void vcc_table_remove(unsigned long index)
140{
141 unsigned long flags;
142
143 if (WARN_ON(index >= VCC_MAX_PORTS))
144 return;
145
146 spin_lock_irqsave(&vcc_table_lock, flags);
147 vcc_table[index] = NULL;
148 spin_unlock_irqrestore(&vcc_table_lock, flags);
149}
150
151/**
152 * vcc_get() - Gets a reference to VCC port
153 * @index: Index into the VCC table
154 * @excl: Indicates if an exclusive access is requested
155 *
156 * Return: reference to the VCC port, if found
157 * NULL, if port not found
158 */
159static struct vcc_port *vcc_get(unsigned long index, bool excl)
160{
161 struct vcc_port *port;
162 unsigned long flags;
163
164try_again:
165 spin_lock_irqsave(&vcc_table_lock, flags);
166
167 port = vcc_table[index];
168 if (!port) {
169 spin_unlock_irqrestore(&vcc_table_lock, flags);
170 return NULL;
171 }
172
173 if (!excl) {
174 if (port->excl_locked) {
175 spin_unlock_irqrestore(&vcc_table_lock, flags);
176 udelay(VCC_REF_DELAY);
177 goto try_again;
178 }
179 port->refcnt++;
180 spin_unlock_irqrestore(&vcc_table_lock, flags);
181 return port;
182 }
183
184 if (port->refcnt) {
185 spin_unlock_irqrestore(&vcc_table_lock, flags);
186 /* Threads wanting exclusive access will wait half the time,
187 * probably giving them higher priority in the case of
188 * multiple waiters.
189 */
190 udelay(VCC_REF_DELAY/2);
191 goto try_again;
192 }
193
194 port->refcnt++;
195 port->excl_locked = true;
196 spin_unlock_irqrestore(&vcc_table_lock, flags);
197
198 return port;
199}
200
201/**
202 * vcc_put() - Returns a reference to VCC port
203 * @port: pointer to VCC port
204 * @excl: Indicates if the returned reference is an exclusive reference
205 *
206 * Note: It's the caller's responsibility to ensure the correct value
207 * for the excl flag
208 */
209static void vcc_put(struct vcc_port *port, bool excl)
210{
211 unsigned long flags;
212
213 if (!port)
214 return;
215
216 spin_lock_irqsave(&vcc_table_lock, flags);
217
218 /* check if caller attempted to put with the wrong flags */
219 if (WARN_ON((excl && !port->excl_locked) ||
220 (!excl && port->excl_locked)))
221 goto done;
222
223 port->refcnt--;
224
225 if (excl)
226 port->excl_locked = false;
227
228done:
229 spin_unlock_irqrestore(&vcc_table_lock, flags);
230}
231
232/**
233 * vcc_get_ne() - Get a non-exclusive reference to VCC port
234 * @index: Index into the VCC table
235 *
236 * Gets a non-exclusive reference to VCC port, if it's not removed
237 *
238 * Return: pointer to the VCC port, if found
239 * NULL, if port not found
240 */
241static struct vcc_port *vcc_get_ne(unsigned long index)
242{
243 struct vcc_port *port;
244
245 port = vcc_get(index, false);
246
247 if (port && port->removed) {
248 vcc_put(port, false);
249 return NULL;
250 }
251
252 return port;
253}
254
255static void vcc_event(void *arg, int event)
256{
257}
258
259static struct ldc_channel_config vcc_ldc_cfg = {
260 .event = vcc_event,
261 .mtu = VIO_VCC_MTU_SIZE,
262 .mode = LDC_MODE_RAW,
263 .debug = 0,
264};
265
266/* Ordered from largest major to lowest */
267static struct vio_version vcc_versions[] = {
268 { .major = 1, .minor = 0 },
269};
270
271/**
272 * vcc_probe() - Initialize VCC port
273 * @vdev: Pointer to VIO device of the new VCC port
274 * @id: VIO device ID
275 *
276 * Initializes a VCC port to receive serial console data from
277 * the guest domain. Sets up a TTY end point on the control
278 * domain. Sets up VIO/LDC link between the guest & control
279 * domain endpoints.
280 *
281 * Return: status of the probe
282 */
283static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
284{
285 struct mdesc_handle *hp;
286 struct vcc_port *port;
287 struct device *dev;
288 const char *domain;
289 char *name;
290 u64 node;
291 int rv;
292
293 vccdbg("VCC: name=%s\n", dev_name(&vdev->dev));
294
295 if (!vcc_tty_driver) {
296 pr_err("VCC: TTY driver not registered\n");
297 return -ENODEV;
298 }
299
300 port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL);
301 if (!port)
302 return -ENOMEM;
303
304 name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL);
305
306 rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions,
307 ARRAY_SIZE(vcc_versions), NULL, name);
308 if (rv)
309 goto free_port;
310
311 port->vio.debug = vcc_dbg_vio;
312 vcc_ldc_cfg.debug = vcc_dbg_ldc;
313
314 rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port);
315 if (rv)
316 goto free_port;
317
318 spin_lock_init(&port->lock);
319
320 port->index = vcc_table_add(port);
321 if (port->index == -1) {
322 pr_err("VCC: no more TTY indices left for allocation\n");
323 goto free_ldc;
324 }
325
326 /* Register the device using VCC table index as TTY index */
327 dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev);
328 if (IS_ERR(dev)) {
329 rv = PTR_ERR(dev);
330 goto free_table;
331 }
332
333 hp = mdesc_grab();
334
335 node = vio_vdev_node(hp, vdev);
336 if (node == MDESC_NODE_NULL) {
337 rv = -ENXIO;
338 mdesc_release(hp);
339 goto unreg_tty;
340 }
341
342 domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL);
343 if (!domain) {
344 rv = -ENXIO;
345 mdesc_release(hp);
346 goto unreg_tty;
347 }
348 port->domain = kstrdup(domain, GFP_KERNEL);
349
350 mdesc_release(hp);
351
352 dev_set_drvdata(&vdev->dev, port);
353
354 /* It's possible to receive IRQs in the middle of vio_port_up. Disable
355 * IRQs until the port is up.
356 */
357 disable_irq_nosync(vdev->rx_irq);
358 vio_port_up(&port->vio);
359 enable_irq(vdev->rx_irq);
360
361 return 0;
362
363unreg_tty:
364 tty_unregister_device(vcc_tty_driver, port->index);
365free_table:
366 vcc_table_remove(port->index);
367free_ldc:
368 vio_ldc_free(&port->vio);
369free_port:
370 kfree(name);
371 kfree(port);
372
373 return rv;
374}
375
376/**
377 * vcc_remove() - Terminate a VCC port
378 * @vdev: Pointer to VIO device of the VCC port
379 *
380 * Terminates a VCC port. Sets up the teardown of TTY and
381 * VIO/LDC link between guest and primary domains.
382 *
383 * Return: status of removal
384 */
385static int vcc_remove(struct vio_dev *vdev)
386{
387 struct vcc_port *port = dev_get_drvdata(&vdev->dev);
388
389 if (!port)
390 return -ENODEV;
391
392 /* If there's a process with the device open, do a synchronous
393 * hangup of the TTY. This *may* cause the process to call close
394 * asynchronously, but it's not guaranteed.
395 */
396 if (port->tty)
397 tty_vhangup(port->tty);
398
399 /* Get exclusive reference to VCC, ensures that there are no other
400 * clients to this port
401 */
402 port = vcc_get(port->index, true);
403
404 if (WARN_ON(!port))
405 return -ENODEV;
406
407 tty_unregister_device(vcc_tty_driver, port->index);
408
409 del_timer_sync(&port->vio.timer);
410 vio_ldc_free(&port->vio);
411 dev_set_drvdata(&vdev->dev, NULL);
412
413 if (port->tty) {
414 port->removed = true;
415 vcc_put(port, true);
416 } else {
417 vcc_table_remove(port->index);
418
419 kfree(port->vio.name);
420 kfree(port->domain);
421 kfree(port);
422 }
423
424 return 0;
425}
426
427static const struct vio_device_id vcc_match[] = {
428 {
429 .type = "vcc-port",
430 },
431 {},
432};
433MODULE_DEVICE_TABLE(vio, vcc_match);
434
435static struct vio_driver vcc_driver = {
436 .id_table = vcc_match,
437 .probe = vcc_probe,
438 .remove = vcc_remove,
439 .name = "vcc",
440};
441
Jag Ramance808b72017-08-15 17:02:59 -0400442static const struct tty_operations vcc_ops;
443
444#define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW)
445
446static int vcc_tty_init(void)
447{
448 int rv;
449
450 pr_info("VCC: %s\n", version);
451
452 vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS);
453 if (!vcc_tty_driver) {
454 pr_err("VCC: TTY driver alloc failed\n");
455 return -ENOMEM;
456 }
457
458 vcc_tty_driver->driver_name = vcc_driver_name;
459 vcc_tty_driver->name = vcc_device_node;
460
461 vcc_tty_driver->minor_start = VCC_MINOR_START;
462 vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
463 vcc_tty_driver->init_termios = vcc_tty_termios;
464
465 tty_set_operations(vcc_tty_driver, &vcc_ops);
466
467 rv = tty_register_driver(vcc_tty_driver);
468 if (rv) {
469 pr_err("VCC: TTY driver registration failed\n");
470 put_tty_driver(vcc_tty_driver);
471 vcc_tty_driver = NULL;
472 return rv;
473 }
474
475 vccdbg("VCC: TTY driver registered\n");
476
477 return 0;
478}
479
480static void vcc_tty_exit(void)
481{
482 tty_unregister_driver(vcc_tty_driver);
483 put_tty_driver(vcc_tty_driver);
484 vccdbg("VCC: TTY driver unregistered\n");
485
486 vcc_tty_driver = NULL;
487}
488
Jag Raman55bd2132017-08-15 17:02:57 -0400489static int __init vcc_init(void)
490{
Jag Ramance808b72017-08-15 17:02:59 -0400491 int rv;
492
493 rv = vcc_tty_init();
494 if (rv) {
495 pr_err("VCC: TTY init failed\n");
496 return rv;
497 }
498
Jag Raman5d171052017-08-15 17:03:00 -0400499 rv = vio_register_driver(&vcc_driver);
500 if (rv) {
501 pr_err("VCC: VIO driver registration failed\n");
502 vcc_tty_exit();
503 } else {
504 vccdbg("VCC: VIO driver registered successfully\n");
505 }
506
Jag Ramance808b72017-08-15 17:02:59 -0400507 return rv;
Jag Raman55bd2132017-08-15 17:02:57 -0400508}
509
510static void __exit vcc_exit(void)
511{
Jag Raman5d171052017-08-15 17:03:00 -0400512 vio_unregister_driver(&vcc_driver);
513 vccdbg("VCC: VIO driver unregistered\n");
Jag Ramance808b72017-08-15 17:02:59 -0400514 vcc_tty_exit();
515 vccdbg("VCC: TTY driver unregistered\n");
Jag Raman55bd2132017-08-15 17:02:57 -0400516}
517
518module_init(vcc_init);
519module_exit(vcc_exit);