1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * Host to PCI-Express local bus driver 32 */ 33 34 #include <sys/conf.h> 35 #include <sys/modctl.h> 36 #include <sys/pcie.h> 37 #include <sys/pci_impl.h> 38 #include <sys/sysmacros.h> 39 #include <sys/ddi_intr.h> 40 #include <sys/sunndi.h> 41 #include <sys/hotplug/pci/pcihp.h> 42 #include <io/pci/pci_common.h> 43 #include <io/pci/pci_tools_ext.h> 44 #include <io/pci/pci_var.h> 45 #include <io/pciex/pcie_error.h> 46 47 /* 48 * Bus Operation functions 49 */ 50 static int npe_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, 51 off_t, off_t, caddr_t *); 52 static int npe_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, 53 void *, void *); 54 static int npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 55 ddi_intr_handle_impl_t *, void *); 56 57 struct bus_ops npe_bus_ops = { 58 BUSO_REV, 59 npe_bus_map, 60 NULL, 61 NULL, 62 NULL, 63 i_ddi_map_fault, 64 ddi_dma_map, 65 ddi_dma_allochdl, 66 ddi_dma_freehdl, 67 ddi_dma_bindhdl, 68 ddi_dma_unbindhdl, 69 ddi_dma_flush, 70 ddi_dma_win, 71 ddi_dma_mctl, 72 npe_ctlops, 73 ddi_bus_prop_op, 74 0, /* (*bus_get_eventcookie)(); */ 75 0, /* (*bus_add_eventcall)(); */ 76 0, /* (*bus_remove_eventcall)(); */ 77 0, /* (*bus_post_event)(); */ 78 0, /* (*bus_intr_ctl)(); */ 79 0, /* (*bus_config)(); */ 80 0, /* (*bus_unconfig)(); */ 81 NULL, /* (*bus_fm_init)(); */ 82 NULL, /* (*bus_fm_fini)(); */ 83 NULL, /* (*bus_fm_access_enter)(); */ 84 NULL, /* (*bus_fm_access_exit)(); */ 85 NULL, /* (*bus_power)(); */ 86 npe_intr_ops /* (*bus_intr_op)(); */ 87 }; 88 89 /* 90 * One goal here is to leverage off of the pcihp.c source without making 91 * changes to it. Call into it's cb_ops directly if needed, piggybacking 92 * anything else needed by the pci_tools.c module. Only pci_tools and pcihp 93 * will be using the PCI devctl node. 94 */ 95 static int npe_open(dev_t *, int, int, cred_t *); 96 static int npe_close(dev_t, int, int, cred_t *); 97 static int npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 98 static int npe_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, 99 caddr_t, int *); 100 static int npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 101 102 struct cb_ops npe_cb_ops = { 103 npe_open, /* open */ 104 npe_close, /* close */ 105 nodev, /* strategy */ 106 nodev, /* print */ 107 nodev, /* dump */ 108 nodev, /* read */ 109 nodev, /* write */ 110 npe_ioctl, /* ioctl */ 111 nodev, /* devmap */ 112 nodev, /* mmap */ 113 nodev, /* segmap */ 114 nochpoll, /* poll */ 115 npe_prop_op, /* cb_prop_op */ 116 NULL, /* streamtab */ 117 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 118 CB_REV, /* rev */ 119 nodev, /* int (*cb_aread)() */ 120 nodev /* int (*cb_awrite)() */ 121 }; 122 123 124 /* 125 * Device Node Operation functions 126 */ 127 static int npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 128 static int npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 129 130 struct dev_ops npe_ops = { 131 DEVO_REV, /* devo_rev */ 132 0, /* refcnt */ 133 pcihp_info, /* info */ 134 nulldev, /* identify */ 135 nulldev, /* probe */ 136 npe_attach, /* attach */ 137 npe_detach, /* detach */ 138 nulldev, /* reset */ 139 &npe_cb_ops, /* driver operations */ 140 &npe_bus_ops /* bus operations */ 141 }; 142 143 /* 144 * Internal routines in support of particular npe_ctlops. 145 */ 146 static int npe_removechild(dev_info_t *child); 147 static int npe_initchild(dev_info_t *child); 148 149 /* 150 * External support routine 151 */ 152 extern void npe_query_acpi_mcfg(dev_info_t *dip); 153 extern void npe_ck804_fix_aer_ptr(dev_info_t *child); 154 155 /* 156 * Module linkage information for the kernel. 157 */ 158 static struct modldrv modldrv = { 159 &mod_driverops, /* Type of module */ 160 "Host to PCIe nexus driver %I%", 161 &npe_ops, /* driver ops */ 162 }; 163 164 static struct modlinkage modlinkage = { 165 MODREV_1, 166 (void *)&modldrv, 167 NULL 168 }; 169 170 /* Save minimal state. */ 171 void *npe_statep; 172 173 174 int 175 _init(void) 176 { 177 int e; 178 179 /* 180 * Initialize per-pci bus soft state pointer. 181 */ 182 e = ddi_soft_state_init(&npe_statep, sizeof (pci_state_t), 1); 183 if (e != 0) 184 return (e); 185 186 if ((e = mod_install(&modlinkage)) != 0) 187 ddi_soft_state_fini(&npe_statep); 188 189 return (e); 190 } 191 192 193 int 194 _fini(void) 195 { 196 int rc; 197 198 rc = mod_remove(&modlinkage); 199 if (rc != 0) 200 return (rc); 201 202 ddi_soft_state_fini(&npe_statep); 203 return (rc); 204 } 205 206 207 int 208 _info(struct modinfo *modinfop) 209 { 210 return (mod_info(&modlinkage, modinfop)); 211 } 212 213 214 /*ARGSUSED*/ 215 static int 216 npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 217 { 218 /* 219 * Use the minor number as constructed by pcihp, as the index value to 220 * ddi_soft_state_zalloc. 221 */ 222 int instance = ddi_get_instance(devi); 223 pci_state_t *pcip = NULL; 224 225 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type", 226 "pciex") != DDI_PROP_SUCCESS) { 227 cmn_err(CE_WARN, "npe: 'device_type' prop create failed"); 228 } 229 230 if (ddi_soft_state_zalloc(npe_statep, instance) == DDI_SUCCESS) 231 pcip = ddi_get_soft_state(npe_statep, instance); 232 233 if (pcip == NULL) 234 return (DDI_FAILURE); 235 236 pcip->pci_dip = devi; 237 238 /* 239 * Initialize hotplug support on this bus. At minimum 240 * (for non hotplug bus) this would create ":devctl" minor 241 * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls 242 * to this bus. 243 */ 244 if (pcihp_init(devi) != DDI_SUCCESS) { 245 cmn_err(CE_WARN, "npe: Failed to setup hotplug framework"); 246 ddi_soft_state_free(npe_statep, instance); 247 return (DDI_FAILURE); 248 } 249 250 if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) { 251 (void) pcihp_uninit(devi); 252 ddi_soft_state_free(npe_statep, instance); 253 return (DDI_FAILURE); 254 } 255 256 npe_query_acpi_mcfg(devi); 257 ddi_report_dev(devi); 258 return (DDI_SUCCESS); 259 260 } 261 262 263 /*ARGSUSED*/ 264 static int 265 npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 266 { 267 int instance = ddi_get_instance(devi); 268 269 /* Uninitialize pcitool support. */ 270 pcitool_uninit(devi); 271 272 /* 273 * Uninitialize hotplug support on this bus. 274 */ 275 (void) pcihp_uninit(devi); 276 ddi_soft_state_free(npe_statep, instance); 277 return (DDI_SUCCESS); 278 } 279 280 281 static int 282 npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 283 off_t offset, off_t len, caddr_t *vaddrp) 284 { 285 int rnumber; 286 int length; 287 int space; 288 ddi_acc_hdl_t *hp; 289 ddi_map_req_t mr; 290 pci_regspec_t pci_reg; 291 pci_regspec_t *pci_rp; 292 struct regspec reg; 293 pci_acc_cfblk_t *cfp; 294 295 296 mr = *mp; /* Get private copy of request */ 297 mp = &mr; 298 299 /* 300 * check for register number 301 */ 302 switch (mp->map_type) { 303 case DDI_MT_REGSPEC: 304 pci_reg = *(pci_regspec_t *)(mp->map_obj.rp); 305 pci_rp = &pci_reg; 306 if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS) 307 return (DDI_FAILURE); 308 break; 309 case DDI_MT_RNUMBER: 310 rnumber = mp->map_obj.rnumber; 311 /* 312 * get ALL "reg" properties for dip, select the one of 313 * of interest. In x86, "assigned-addresses" property 314 * is identical to the "reg" property, so there is no 315 * need to cross check the two to determine the physical 316 * address of the registers. 317 * This routine still performs some validity checks to 318 * make sure that everything is okay. 319 */ 320 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 321 DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, 322 (uint_t *)&length) != DDI_PROP_SUCCESS) 323 return (DDI_FAILURE); 324 325 /* 326 * validate the register number. 327 */ 328 length /= (sizeof (pci_regspec_t) / sizeof (int)); 329 if (rnumber >= length) { 330 ddi_prop_free(pci_rp); 331 return (DDI_FAILURE); 332 } 333 334 /* 335 * copy the required entry. 336 */ 337 pci_reg = pci_rp[rnumber]; 338 339 /* 340 * free the memory allocated by ddi_prop_lookup_int_array 341 */ 342 ddi_prop_free(pci_rp); 343 344 pci_rp = &pci_reg; 345 if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS) 346 return (DDI_FAILURE); 347 mp->map_type = DDI_MT_REGSPEC; 348 break; 349 default: 350 return (DDI_ME_INVAL); 351 } 352 353 space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M; 354 355 /* 356 * check for unmap and unlock of address space 357 */ 358 if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) { 359 /* 360 * Adjust offset and length 361 * A non-zero length means override the one in the regspec. 362 */ 363 pci_rp->pci_phys_low += (uint_t)offset; 364 if (len != 0) 365 pci_rp->pci_size_low = len; 366 367 switch (space) { 368 case PCI_ADDR_IO: 369 reg.regspec_bustype = 1; 370 break; 371 372 case PCI_ADDR_CONFIG: 373 /* Just fall through */ 374 case PCI_ADDR_MEM64: 375 /* 376 * MEM64 requires special treatment on map, to check 377 * that the device is below 4G. On unmap, however, 378 * we can assume that everything is OK... the map 379 * must have succeeded. 380 */ 381 /* FALLTHROUGH */ 382 case PCI_ADDR_MEM32: 383 reg.regspec_bustype = 0; 384 break; 385 386 default: 387 return (DDI_FAILURE); 388 } 389 reg.regspec_addr = pci_rp->pci_phys_low; 390 reg.regspec_size = pci_rp->pci_size_low; 391 392 mp->map_obj.rp = ® 393 return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp)); 394 395 } 396 397 /* check for user mapping request - not legal for Config */ 398 if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) { 399 cmn_err(CE_NOTE, "npe: Config mapping request from user\n"); 400 return (DDI_FAILURE); 401 } 402 403 404 if (space == PCI_ADDR_CONFIG) { 405 /* Can't map config space without a handle */ 406 hp = (ddi_acc_hdl_t *)mp->map_handlep; 407 if (hp == NULL) 408 return (DDI_FAILURE); 409 410 pci_rp->pci_phys_low = ddi_prop_get_int64(DDI_DEV_T_ANY, 411 rdip, 0, "ecfga-base-address", 0); 412 413 /* record the device address for future reference */ 414 cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private; 415 cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi); 416 cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 417 cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 418 419 pci_rp->pci_phys_low += ((cfp->c_busnum << 20) | 420 (cfp->c_devnum) << 15 | (cfp->c_funcnum << 12)); 421 422 pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE; 423 } 424 425 length = pci_rp->pci_size_low; 426 427 /* 428 * range check 429 */ 430 if ((offset >= length) || (len > length) || (offset + len > length)) 431 return (DDI_FAILURE); 432 433 /* 434 * Adjust offset and length 435 * A non-zero length means override the one in the regspec. 436 */ 437 pci_rp->pci_phys_low += (uint_t)offset; 438 if (len != 0) 439 pci_rp->pci_size_low = len; 440 441 /* 442 * convert the pci regsec into the generic regspec used by the 443 * parent root nexus driver. 444 */ 445 switch (space) { 446 case PCI_ADDR_IO: 447 reg.regspec_bustype = 1; 448 break; 449 case PCI_ADDR_CONFIG: 450 case PCI_ADDR_MEM64: 451 /* 452 * We can't handle 64-bit devices that are mapped above 453 * 4G or that are larger than 4G. 454 */ 455 if (pci_rp->pci_phys_mid != 0 || pci_rp->pci_size_hi != 0) 456 return (DDI_FAILURE); 457 /* 458 * Other than that, we can treat them as 32-bit mappings 459 */ 460 /* FALLTHROUGH */ 461 case PCI_ADDR_MEM32: 462 reg.regspec_bustype = 0; 463 break; 464 default: 465 return (DDI_FAILURE); 466 } 467 468 reg.regspec_addr = pci_rp->pci_phys_low; 469 reg.regspec_size = pci_rp->pci_size_low; 470 471 mp->map_obj.rp = ® 472 return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp)); 473 } 474 475 476 /*ARGSUSED*/ 477 static int 478 npe_ctlops(dev_info_t *dip, dev_info_t *rdip, 479 ddi_ctl_enum_t ctlop, void *arg, void *result) 480 { 481 int rn; 482 int totreg; 483 uint_t reglen; 484 pci_regspec_t *drv_regp; 485 486 switch (ctlop) { 487 case DDI_CTLOPS_REPORTDEV: 488 if (rdip == (dev_info_t *)0) 489 return (DDI_FAILURE); 490 cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 491 ddi_node_name(rdip), ddi_get_name_addr(rdip), 492 ddi_driver_name(rdip), ddi_get_instance(rdip)); 493 return (DDI_SUCCESS); 494 495 case DDI_CTLOPS_INITCHILD: 496 return (npe_initchild((dev_info_t *)arg)); 497 498 case DDI_CTLOPS_UNINITCHILD: 499 return (npe_removechild((dev_info_t *)arg)); 500 501 case DDI_CTLOPS_SIDDEV: 502 return (DDI_SUCCESS); 503 504 case DDI_CTLOPS_REGSIZE: 505 case DDI_CTLOPS_NREGS: 506 if (rdip == (dev_info_t *)0) 507 return (DDI_FAILURE); 508 509 *(int *)result = 0; 510 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 511 DDI_PROP_DONTPASS, "reg", (int **)&drv_regp, 512 ®len) != DDI_PROP_SUCCESS) { 513 return (DDI_FAILURE); 514 } 515 516 totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t); 517 if (ctlop == DDI_CTLOPS_NREGS) 518 *(int *)result = totreg; 519 else if (ctlop == DDI_CTLOPS_REGSIZE) { 520 rn = *(int *)arg; 521 if (rn >= totreg) { 522 ddi_prop_free(drv_regp); 523 return (DDI_FAILURE); 524 } 525 *(off_t *)result = drv_regp[rn].pci_size_low; 526 } 527 ddi_prop_free(drv_regp); 528 529 return (DDI_SUCCESS); 530 531 case DDI_CTLOPS_POWER: 532 { 533 power_req_t *reqp = (power_req_t *)arg; 534 /* 535 * We currently understand reporting of PCI_PM_IDLESPEED 536 * capability. Everything else is passed up. 537 */ 538 if ((reqp->request_type == PMR_REPORT_PMCAP) && 539 (reqp->req.report_pmcap_req.cap == PCI_PM_IDLESPEED)) 540 return (DDI_SUCCESS); 541 542 break; 543 } 544 545 default: 546 break; 547 } 548 549 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 550 551 } 552 553 554 /* 555 * npe_intr_ops 556 */ 557 static int 558 npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 559 ddi_intr_handle_impl_t *hdlp, void *result) 560 { 561 return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result)); 562 } 563 564 565 static int 566 npe_initchild(dev_info_t *child) 567 { 568 char name[80]; 569 570 if (pci_common_name_child(child, name, 80) != DDI_SUCCESS) 571 return (DDI_FAILURE); 572 573 ddi_set_name_addr(child, name); 574 575 /* 576 * Pseudo nodes indicate a prototype node with per-instance 577 * properties to be merged into the real h/w device node. 578 * The interpretation of the unit-address is DD[,F] 579 * where DD is the device id and F is the function. 580 */ 581 if (ndi_dev_is_persistent_node(child) == 0) { 582 extern int pci_allow_pseudo_children; 583 584 ddi_set_parent_data(child, NULL); 585 586 /* 587 * Try to merge the properties from this prototype 588 * node into real h/w nodes. 589 */ 590 if (ndi_merge_node(child, pci_common_name_child) == 591 DDI_SUCCESS) { 592 /* 593 * Merged ok - return failure to remove the node. 594 */ 595 ddi_set_name_addr(child, NULL); 596 return (DDI_FAILURE); 597 } 598 599 /* workaround for DDIVS to run under PCI Express */ 600 if (pci_allow_pseudo_children) { 601 /* 602 * If the "interrupts" property doesn't exist, 603 * this must be the ddivs no-intr case, and it returns 604 * DDI_SUCCESS instead of DDI_FAILURE. 605 */ 606 if (ddi_prop_get_int(DDI_DEV_T_ANY, child, 607 DDI_PROP_DONTPASS, "interrupts", -1) == -1) 608 return (DDI_SUCCESS); 609 /* 610 * Create the ddi_parent_private_data for a pseudo 611 * child. 612 */ 613 pci_common_set_parent_private_data(child); 614 return (DDI_SUCCESS); 615 } 616 617 /* 618 * The child was not merged into a h/w node, 619 * but there's not much we can do with it other 620 * than return failure to cause the node to be removed. 621 */ 622 cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 623 ddi_get_name(child), ddi_get_name_addr(child), 624 ddi_get_name(child)); 625 ddi_set_name_addr(child, NULL); 626 return (DDI_NOT_WELL_FORMED); 627 } 628 629 if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 630 "interrupts", -1) != -1) 631 pci_common_set_parent_private_data(child); 632 else 633 ddi_set_parent_data(child, NULL); 634 635 /* 636 * Enable AER next pointer being displayed 637 */ 638 npe_ck804_fix_aer_ptr(child); 639 640 (void) pcie_error_init(child); 641 642 return (DDI_SUCCESS); 643 } 644 645 646 static int 647 npe_removechild(dev_info_t *dip) 648 { 649 struct ddi_parent_private_data *pdptr; 650 651 /* 652 * Do it way early. 653 * Otherwise ddi_map() call form pcie_error_fini crashes 654 */ 655 pcie_error_fini(dip); 656 657 if ((pdptr = ddi_get_parent_data(dip)) != NULL) { 658 kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); 659 ddi_set_parent_data(dip, NULL); 660 } 661 ddi_set_name_addr(dip, NULL); 662 663 /* 664 * Strip the node to properly convert it back to prototype form 665 */ 666 ddi_remove_minor_node(dip, NULL); 667 668 ddi_prop_remove_all(dip); 669 670 return (DDI_SUCCESS); 671 } 672 673 674 /* 675 * When retrofitting this module for pci_tools, functions such as open, close, 676 * and ioctl are now pulled into this module. Before this, the functions in 677 * the pcihp module were referenced directly. Now they are called or 678 * referenced through the pcihp cb_ops structure from functions in this module. 679 */ 680 static int 681 npe_open(dev_t *devp, int flags, int otyp, cred_t *credp) 682 { 683 return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp)); 684 } 685 686 static int 687 npe_close(dev_t dev, int flags, int otyp, cred_t *credp) 688 { 689 return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp)); 690 } 691 692 static int 693 npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 694 { 695 minor_t minor = getminor(dev); 696 int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 697 pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance); 698 dev_info_t *dip; 699 700 if (pci_p == NULL) 701 return (ENXIO); 702 703 dip = pci_p->pci_dip; 704 return (pci_common_ioctl(dip, dev, cmd, arg, mode, credp, rvalp)); 705 } 706 707 static int 708 npe_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, 709 int flags, char *name, caddr_t valuep, int *lengthp) 710 { 711 return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags, 712 name, valuep, lengthp)); 713 } 714 715 static int 716 npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 717 { 718 return (pcihp_info(dip, cmd, arg, result)); 719 } 720