blob: 8b6f197e5f8cbce32a307bec1be4d66846b43103 [file] [log] [blame]
Stephen Rothwell8bff05b2006-07-13 18:51:22 +10001/*
2 * iSeries vio driver interface to hvc_console.c
3 *
4 * This code is based heavily on hvc_vio.c and viocons.c
5 *
6 * Copyright (C) 2006 Stephen Rothwell, IBM Corporation
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22#include <stdarg.h>
23#include <linux/types.h>
24#include <linux/init.h>
25#include <linux/kernel.h>
26#include <linux/spinlock.h>
27#include <linux/console.h>
28
29#include <asm/hvconsole.h>
30#include <asm/vio.h>
31#include <asm/prom.h>
32#include <asm/iseries/vio.h>
33#include <asm/iseries/hv_call.h>
34#include <asm/iseries/hv_lp_config.h>
35#include <asm/iseries/hv_lp_event.h>
36
37#include "hvc_console.h"
38
39#define VTTY_PORTS 10
40
41static DEFINE_SPINLOCK(consolelock);
42static DEFINE_SPINLOCK(consoleloglock);
43
44static const char hvc_driver_name[] = "hvc_console";
45
46#define IN_BUF_SIZE 200
47
48/*
49 * Our port information.
50 */
51static struct port_info {
52 HvLpIndex lp;
53 u64 seq; /* sequence number of last HV send */
54 u64 ack; /* last ack from HV */
55 struct hvc_struct *hp;
56 int in_start;
57 int in_end;
58 unsigned char in_buf[IN_BUF_SIZE];
59} port_info[VTTY_PORTS] = {
60 [ 0 ... VTTY_PORTS - 1 ] = {
61 .lp = HvLpIndexInvalid
62 }
63};
64
65#define viochar_is_console(pi) ((pi) == &port_info[0])
66
67static struct vio_device_id hvc_driver_table[] __devinitdata = {
68 {"serial", "IBM,iSeries-vty"},
69 { "", "" }
70};
71MODULE_DEVICE_TABLE(vio, hvc_driver_table);
72
73static void hvlog(char *fmt, ...)
74{
75 int i;
76 unsigned long flags;
77 va_list args;
78 static char buf[256];
79
80 spin_lock_irqsave(&consoleloglock, flags);
81 va_start(args, fmt);
82 i = vscnprintf(buf, sizeof(buf) - 1, fmt, args);
83 va_end(args);
84 buf[i++] = '\r';
85 HvCall_writeLogBuffer(buf, i);
86 spin_unlock_irqrestore(&consoleloglock, flags);
87}
88
89/*
90 * Initialize the common fields in a charLpEvent
91 */
92static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp)
93{
94 struct HvLpEvent *hev = &viochar->event;
95
96 memset(viochar, 0, sizeof(struct viocharlpevent));
97
98 hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK |
99 HV_LP_EVENT_INT;
100 hev->xType = HvLpEvent_Type_VirtualIo;
101 hev->xSubtype = viomajorsubtype_chario | viochardata;
102 hev->xSourceLp = HvLpConfig_getLpIndex();
103 hev->xTargetLp = lp;
104 hev->xSizeMinus1 = sizeof(struct viocharlpevent);
105 hev->xSourceInstanceId = viopath_sourceinst(lp);
106 hev->xTargetInstanceId = viopath_targetinst(lp);
107}
108
109static int get_chars(uint32_t vtermno, char *buf, int count)
110{
111 struct port_info *pi;
112 int n = 0;
113 unsigned long flags;
114
115 if (vtermno >= VTTY_PORTS)
116 return -EINVAL;
117 if (count == 0)
118 return 0;
119
120 pi = &port_info[vtermno];
121 spin_lock_irqsave(&consolelock, flags);
122
123 if (pi->in_end == 0)
124 goto done;
125
126 n = pi->in_end - pi->in_start;
127 if (n > count)
128 n = count;
129 memcpy(buf, &pi->in_buf[pi->in_start], n);
130 pi->in_start += n;
131 if (pi->in_start == pi->in_end) {
132 pi->in_start = 0;
133 pi->in_end = 0;
134 }
135done:
136 spin_unlock_irqrestore(&consolelock, flags);
137 return n;
138}
139
140static int put_chars(uint32_t vtermno, const char *buf, int count)
141{
142 struct viocharlpevent *viochar;
143 struct port_info *pi;
144 HvLpEvent_Rc hvrc;
145 unsigned long flags;
146 int sent = 0;
147
148 if (vtermno >= VTTY_PORTS)
149 return -EINVAL;
150
151 pi = &port_info[vtermno];
152
153 spin_lock_irqsave(&consolelock, flags);
154
155 if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) {
Stephen Rothwell8bff05b2006-07-13 18:51:22 +1000156 HvCall_writeLogBuffer(buf, count);
Stephen Rothwell8bff05b2006-07-13 18:51:22 +1000157 sent = count;
158 goto done;
159 }
160
161 viochar = vio_get_event_buffer(viomajorsubtype_chario);
162 if (viochar == NULL) {
163 hvlog("\n\rviocons: Can't get viochar buffer.");
164 goto done;
165 }
166
167 while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) {
168 int len;
169
170 len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count;
171
Stephen Rothwelld86d9b82006-09-26 11:54:16 +1000172 if (viochar_is_console(pi))
Stephen Rothwell8bff05b2006-07-13 18:51:22 +1000173 HvCall_writeLogBuffer(buf, len);
Stephen Rothwell8bff05b2006-07-13 18:51:22 +1000174
175 init_data_event(viochar, pi->lp);
176
177 viochar->len = len;
178 viochar->event.xCorrelationToken = pi->seq++;
179 viochar->event.xSizeMinus1 =
180 offsetof(struct viocharlpevent, data) + len;
181
182 memcpy(viochar->data, buf, len);
183
184 hvrc = HvCallEvent_signalLpEvent(&viochar->event);
185 if (hvrc)
186 hvlog("\n\rerror sending event! return code %d\n\r",
187 (int)hvrc);
188 sent += len;
189 count -= len;
190 buf += len;
191 }
192
193 vio_free_event_buffer(viomajorsubtype_chario, viochar);
194done:
195 spin_unlock_irqrestore(&consolelock, flags);
196 return sent;
197}
198
199static struct hv_ops hvc_get_put_ops = {
200 .get_chars = get_chars,
201 .put_chars = put_chars,
202};
203
204static int __devinit hvc_vio_probe(struct vio_dev *vdev,
205 const struct vio_device_id *id)
206{
207 struct hvc_struct *hp;
208 struct port_info *pi;
209
210 /* probed with invalid parameters. */
211 if (!vdev || !id)
212 return -EPERM;
213
214 if (vdev->unit_address >= VTTY_PORTS)
215 return -ENODEV;
216
217 pi = &port_info[vdev->unit_address];
218
Stephen Rothwell4e9e95a2006-07-13 18:53:32 +1000219 hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops,
220 VIOCHAR_MAX_DATA);
Stephen Rothwell8bff05b2006-07-13 18:51:22 +1000221 if (IS_ERR(hp))
222 return PTR_ERR(hp);
223 pi->hp = hp;
224 dev_set_drvdata(&vdev->dev, pi);
225
226 return 0;
227}
228
229static int __devexit hvc_vio_remove(struct vio_dev *vdev)
230{
231 struct port_info *pi = dev_get_drvdata(&vdev->dev);
232 struct hvc_struct *hp = pi->hp;
233
234 return hvc_remove(hp);
235}
236
237static struct vio_driver hvc_vio_driver = {
238 .id_table = hvc_driver_table,
239 .probe = hvc_vio_probe,
240 .remove = hvc_vio_remove,
241 .driver = {
242 .name = hvc_driver_name,
243 .owner = THIS_MODULE,
244 }
245};
246
247static void hvc_open_event(struct HvLpEvent *event)
248{
249 unsigned long flags;
250 struct viocharlpevent *cevent = (struct viocharlpevent *)event;
251 u8 port = cevent->virtual_device;
252 struct port_info *pi;
253 int reject = 0;
254
255 if (hvlpevent_is_ack(event)) {
256 if (port >= VTTY_PORTS)
257 return;
258
259 spin_lock_irqsave(&consolelock, flags);
260
261 pi = &port_info[port];
262 if (event->xRc == HvLpEvent_Rc_Good) {
263 pi->seq = pi->ack = 0;
264 /*
265 * This line allows connections from the primary
266 * partition but once one is connected from the
267 * primary partition nothing short of a reboot
268 * of linux will allow access from the hosting
269 * partition again without a required iSeries fix.
270 */
271 pi->lp = event->xTargetLp;
272 }
273
274 spin_unlock_irqrestore(&consolelock, flags);
275 if (event->xRc != HvLpEvent_Rc_Good)
276 printk(KERN_WARNING
277 "hvc: handle_open_event: event->xRc == (%d).\n",
278 event->xRc);
279
280 if (event->xCorrelationToken != 0) {
281 atomic_t *aptr= (atomic_t *)event->xCorrelationToken;
282 atomic_set(aptr, 1);
283 } else
284 printk(KERN_WARNING
285 "hvc: weird...got open ack without atomic\n");
286 return;
287 }
288
289 /* This had better require an ack, otherwise complain */
290 if (!hvlpevent_need_ack(event)) {
291 printk(KERN_WARNING "hvc: viocharopen without ack bit!\n");
292 return;
293 }
294
295 spin_lock_irqsave(&consolelock, flags);
296
297 /* Make sure this is a good virtual tty */
298 if (port >= VTTY_PORTS) {
299 event->xRc = HvLpEvent_Rc_SubtypeError;
300 cevent->subtype_result_code = viorc_openRejected;
301 /*
302 * Flag state here since we can't printk while holding
303 * the consolelock spinlock.
304 */
305 reject = 1;
306 } else {
307 pi = &port_info[port];
308 if ((pi->lp != HvLpIndexInvalid) &&
309 (pi->lp != event->xSourceLp)) {
310 /*
311 * If this is tty is already connected to a different
312 * partition, fail.
313 */
314 event->xRc = HvLpEvent_Rc_SubtypeError;
315 cevent->subtype_result_code = viorc_openRejected;
316 reject = 2;
317 } else {
318 pi->lp = event->xSourceLp;
319 event->xRc = HvLpEvent_Rc_Good;
320 cevent->subtype_result_code = viorc_good;
321 pi->seq = pi->ack = 0;
322 }
323 }
324
325 spin_unlock_irqrestore(&consolelock, flags);
326
327 if (reject == 1)
328 printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n");
329 else if (reject == 2)
330 printk(KERN_WARNING "hvc: open rejected: console in exclusive "
331 "use by another partition.\n");
332
333 /* Return the acknowledgement */
334 HvCallEvent_ackLpEvent(event);
335}
336
337/*
338 * Handle a close charLpEvent. This should ONLY be an Interrupt because the
339 * virtual console should never actually issue a close event to the hypervisor
340 * because the virtual console never goes away. A close event coming from the
341 * hypervisor simply means that there are no client consoles connected to the
342 * virtual console.
343 */
344static void hvc_close_event(struct HvLpEvent *event)
345{
346 unsigned long flags;
347 struct viocharlpevent *cevent = (struct viocharlpevent *)event;
348 u8 port = cevent->virtual_device;
349
350 if (!hvlpevent_is_int(event)) {
351 printk(KERN_WARNING
352 "hvc: got unexpected close acknowlegement\n");
353 return;
354 }
355
356 if (port >= VTTY_PORTS) {
357 printk(KERN_WARNING
358 "hvc: close message from invalid virtual device.\n");
359 return;
360 }
361
362 /* For closes, just mark the console partition invalid */
363 spin_lock_irqsave(&consolelock, flags);
364
365 if (port_info[port].lp == event->xSourceLp)
366 port_info[port].lp = HvLpIndexInvalid;
367
368 spin_unlock_irqrestore(&consolelock, flags);
369}
370
371static void hvc_data_event(struct HvLpEvent *event)
372{
373 unsigned long flags;
374 struct viocharlpevent *cevent = (struct viocharlpevent *)event;
375 struct port_info *pi;
376 int n;
377 u8 port = cevent->virtual_device;
378
379 if (port >= VTTY_PORTS) {
380 printk(KERN_WARNING "hvc: data on invalid virtual device %d\n",
381 port);
382 return;
383 }
384 if (cevent->len == 0)
385 return;
386
387 /*
388 * Change 05/01/2003 - Ryan Arnold: If a partition other than
389 * the current exclusive partition tries to send us data
390 * events then just drop them on the floor because we don't
391 * want his stinking data. He isn't authorized to receive
392 * data because he wasn't the first one to get the console,
393 * therefore he shouldn't be allowed to send data either.
394 * This will work without an iSeries fix.
395 */
396 pi = &port_info[port];
397 if (pi->lp != event->xSourceLp)
398 return;
399
400 spin_lock_irqsave(&consolelock, flags);
401
402 n = IN_BUF_SIZE - pi->in_end;
403 if (n > cevent->len)
404 n = cevent->len;
405 if (n > 0) {
406 memcpy(&pi->in_buf[pi->in_end], cevent->data, n);
407 pi->in_end += n;
408 }
409 spin_unlock_irqrestore(&consolelock, flags);
410 if (n == 0)
411 printk(KERN_WARNING "hvc: input buffer overflow\n");
412}
413
414static void hvc_ack_event(struct HvLpEvent *event)
415{
416 struct viocharlpevent *cevent = (struct viocharlpevent *)event;
417 unsigned long flags;
418 u8 port = cevent->virtual_device;
419
420 if (port >= VTTY_PORTS) {
421 printk(KERN_WARNING "hvc: data on invalid virtual device\n");
422 return;
423 }
424
425 spin_lock_irqsave(&consolelock, flags);
426 port_info[port].ack = event->xCorrelationToken;
427 spin_unlock_irqrestore(&consolelock, flags);
428}
429
430static void hvc_config_event(struct HvLpEvent *event)
431{
432 struct viocharlpevent *cevent = (struct viocharlpevent *)event;
433
434 if (cevent->data[0] == 0x01)
435 printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n",
436 cevent->data[1], cevent->data[2],
437 cevent->data[3], cevent->data[4]);
438 else
439 printk(KERN_WARNING "hvc: unknown config event\n");
440}
441
442static void hvc_handle_event(struct HvLpEvent *event)
443{
444 int charminor;
445
446 if (event == NULL)
447 return;
448
449 charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK;
450 switch (charminor) {
451 case viocharopen:
452 hvc_open_event(event);
453 break;
454 case viocharclose:
455 hvc_close_event(event);
456 break;
457 case viochardata:
458 hvc_data_event(event);
459 break;
460 case viocharack:
461 hvc_ack_event(event);
462 break;
463 case viocharconfig:
464 hvc_config_event(event);
465 break;
466 default:
467 if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {
468 event->xRc = HvLpEvent_Rc_InvalidSubtype;
469 HvCallEvent_ackLpEvent(event);
470 }
471 }
472}
473
474static int send_open(HvLpIndex remoteLp, void *sem)
475{
476 return HvCallEvent_signalLpEventFast(remoteLp,
477 HvLpEvent_Type_VirtualIo,
478 viomajorsubtype_chario | viocharopen,
479 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
480 viopath_sourceinst(remoteLp),
481 viopath_targetinst(remoteLp),
482 (u64)(unsigned long)sem, VIOVERSION << 16,
483 0, 0, 0, 0);
484}
485
486static int hvc_vio_init(void)
487{
488 atomic_t wait_flag;
489 int rc;
490
491 /* +2 for fudge */
492 rc = viopath_open(HvLpConfig_getPrimaryLpIndex(),
493 viomajorsubtype_chario, VIOCHAR_WINDOW + 2);
494 if (rc)
495 printk(KERN_WARNING "hvc: error opening to primary %d\n", rc);
496
497 if (viopath_hostLp == HvLpIndexInvalid)
498 vio_set_hostlp();
499
500 /*
501 * And if the primary is not the same as the hosting LP, open to the
502 * hosting lp
503 */
504 if ((viopath_hostLp != HvLpIndexInvalid) &&
505 (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) {
506 printk(KERN_INFO "hvc: open path to hosting (%d)\n",
507 viopath_hostLp);
508 rc = viopath_open(viopath_hostLp, viomajorsubtype_chario,
509 VIOCHAR_WINDOW + 2); /* +2 for fudge */
510 if (rc)
511 printk(KERN_WARNING
512 "error opening to partition %d: %d\n",
513 viopath_hostLp, rc);
514 }
515
516 if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0)
517 printk(KERN_WARNING
518 "hvc: error seting handler for console events!\n");
519
520 /*
521 * First, try to open the console to the hosting lp.
522 * Wait on a semaphore for the response.
523 */
524 atomic_set(&wait_flag, 0);
525 if ((viopath_isactive(viopath_hostLp)) &&
526 (send_open(viopath_hostLp, &wait_flag) == 0)) {
527 printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp);
528 while (atomic_read(&wait_flag) == 0)
529 mb();
530 atomic_set(&wait_flag, 0);
531 }
532
533 /*
534 * If we don't have an active console, try the primary
535 */
536 if ((!viopath_isactive(port_info[0].lp)) &&
537 (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) &&
538 (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) {
539 printk(KERN_INFO "hvc: opening console to primary partition\n");
540 while (atomic_read(&wait_flag) == 0)
541 mb();
542 }
543
544 /* Register as a vio device to receive callbacks */
545 rc = vio_register_driver(&hvc_vio_driver);
546
547 return rc;
548}
549module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */
550
551static void hvc_vio_exit(void)
552{
553 vio_unregister_driver(&hvc_vio_driver);
554}
555module_exit(hvc_vio_exit);
556
557/* the device tree order defines our numbering */
558static int hvc_find_vtys(void)
559{
560 struct device_node *vty;
561 int num_found = 0;
562
563 for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL;
564 vty = of_find_node_by_name(vty, "vty")) {
565 uint32_t *vtermno;
566
567 /* We have statically defined space for only a certain number
568 * of console adapters.
569 */
570 if ((num_found >= MAX_NR_HVC_CONSOLES) ||
571 (num_found >= VTTY_PORTS))
572 break;
573
574 vtermno = (uint32_t *)get_property(vty, "reg", NULL);
575 if (!vtermno)
576 continue;
577
578 if (!device_is_compatible(vty, "IBM,iSeries-vty"))
579 continue;
580
581 if (num_found == 0)
582 add_preferred_console("hvc", 0, NULL);
583 hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops);
584 ++num_found;
585 }
586
587 return num_found;
588}
589console_initcall(hvc_find_vtys);