Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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