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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2023 Oxide Computer Company 29 */ 30 31 #include <sys/modctl.h> 32 #include <sys/prom_plat.h> 33 #include <sys/ddi.h> 34 #include <sys/sunddi.h> 35 #include <sys/sunndi.h> 36 #include <sys/ndi_impldefs.h> 37 #include <sys/ddi_impldefs.h> 38 #include <sys/ethernet.h> 39 #include <sys/machsystm.h> 40 #include <sys/hypervisor_api.h> 41 #include <sys/mach_descrip.h> 42 #include <sys/drctl.h> 43 #include <sys/dr_util.h> 44 #include <sys/mac.h> 45 #include <sys/vnet.h> 46 #include <sys/vnet_mailbox.h> 47 #include <sys/vnet_common.h> 48 #include <sys/hsvc.h> 49 50 51 #define VDDS_MAX_RANGES 6 /* 6 possible VRs */ 52 #define VDDS_MAX_VRINTRS 8 /* limited to 8 intrs/VR */ 53 #define VDDS_MAX_INTR_NUM 64 /* 0-63 or valid */ 54 55 #define VDDS_INO_RANGE_START(x) (x * VDDS_MAX_VRINTRS) 56 #define HVCOOKIE(c) ((c) & 0xFFFFFFFFF) 57 #define NIUCFGHDL(c) ((c) >> 32) 58 59 60 /* For "ranges" property */ 61 typedef struct vdds_ranges { 62 uint32_t child_hi; 63 uint32_t child_lo; 64 uint32_t parent_hi; 65 uint32_t parent_lo; 66 uint32_t size_hi; 67 uint32_t size_lo; 68 } vdds_ranges_t; 69 70 /* For "reg" property */ 71 typedef struct vdds_reg { 72 uint32_t addr_hi; 73 uint32_t addr_lo; 74 uint32_t size_hi; 75 uint32_t size_lo; 76 } vdds_reg_t; 77 78 /* For ddi callback argument */ 79 typedef struct vdds_cb_arg { 80 dev_info_t *dip; 81 uint64_t cookie; 82 uint64_t macaddr; 83 uint32_t max_frame_size; 84 } vdds_cb_arg_t; 85 86 87 /* Functions exported to other files */ 88 void vdds_mod_init(void); 89 void vdds_mod_fini(void); 90 int vdds_init(vnet_t *vnetp); 91 void vdds_cleanup(vnet_t *vnetp); 92 void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg); 93 void vdds_cleanup_hybrid_res(void *arg); 94 void vdds_cleanup_hio(vnet_t *vnetp); 95 96 /* Support functions to create/destory Hybrid device */ 97 static dev_info_t *vdds_create_niu_node(uint64_t cookie, 98 uint64_t macaddr, uint32_t max_frame_size); 99 static int vdds_destroy_niu_node(dev_info_t *niu_dip, uint64_t cookie); 100 static dev_info_t *vdds_create_new_node(vdds_cb_arg_t *cba, 101 dev_info_t *pdip, int (*new_node_func)(dev_info_t *dip, 102 void *arg, uint_t flags)); 103 static int vdds_new_nexus_node(dev_info_t *dip, void *arg, uint_t flags); 104 static int vdds_new_niu_node(dev_info_t *dip, void *arg, uint_t flags); 105 static dev_info_t *vdds_find_node(uint64_t cookie, dev_info_t *sdip, 106 int (*match_func)(dev_info_t *dip, void *arg)); 107 static int vdds_match_niu_nexus(dev_info_t *dip, void *arg); 108 static int vdds_match_niu_node(dev_info_t *dip, void *arg); 109 static int vdds_get_interrupts(uint64_t cookie, int ino_range, 110 int *intrs, int *nintr); 111 112 /* DDS message processing related functions */ 113 static void vdds_process_dds_msg_task(void *arg); 114 static int vdds_send_dds_resp_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg, int ack); 115 static int vdds_send_dds_rel_msg(vnet_t *vnetp); 116 static void vdds_release_range_prop(dev_info_t *nexus_dip, uint64_t cookie); 117 118 /* Functions imported from other files */ 119 extern int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg); 120 extern int vnet_hio_mac_init(vnet_t *vnetp, char *ifname); 121 extern void vnet_hio_mac_cleanup(vnet_t *vnetp); 122 123 /* HV functions that are used in this file */ 124 extern uint64_t vdds_hv_niu_vr_getinfo(uint32_t hvcookie, 125 uint64_t *real_start, uint64_t *size); 126 extern uint64_t vdds_hv_niu_vr_get_txmap(uint32_t hvcookie, uint64_t *dma_map); 127 extern uint64_t vdds_hv_niu_vr_get_rxmap(uint32_t hvcookie, uint64_t *dma_map); 128 extern uint64_t vdds_hv_niu_vrtx_set_ino(uint32_t cookie, uint64_t vch_idx, 129 uint32_t ino); 130 extern uint64_t vdds_hv_niu_vrrx_set_ino(uint32_t cookie, uint64_t vch_idx, 131 uint32_t ino); 132 133 134 #ifdef DEBUG 135 136 #define DEBUG_PRINTF debug_printf 137 138 extern int vnet_dbglevel; 139 140 static void 141 debug_printf(const char *fname, void *arg, const char *fmt, ...) 142 { 143 char buf[512]; 144 va_list ap; 145 char *bufp = buf; 146 vnet_dds_info_t *vdds = arg; 147 148 if (vdds != NULL) { 149 (void) sprintf(bufp, "vnet%d: %s: ", 150 vdds->vnetp->instance, fname); 151 } else { 152 (void) sprintf(bufp, "%s: ", fname); 153 } 154 bufp += strlen(bufp); 155 va_start(ap, fmt); 156 (void) vsprintf(bufp, fmt, ap); 157 va_end(ap); 158 cmn_err(CE_CONT, "%s\n", buf); 159 } 160 #endif 161 162 /* 163 * Hypervisor N2/NIU services information: 164 * 165 * The list of HV versions that support NIU HybridIO. Note, 166 * the order is higher version to a lower version, as the 167 * registration is attempted in this order. 168 */ 169 static hsvc_info_t niu_hsvc[] = { 170 {HSVC_REV_1, NULL, HSVC_GROUP_NIU, 2, 0, "vnet_dds"}, 171 {HSVC_REV_1, NULL, HSVC_GROUP_NIU, 1, 1, "vnet_dds"} 172 }; 173 174 /* 175 * Index that points to the successful HV version that 176 * is registered. 177 */ 178 static int niu_hsvc_index = -1; 179 180 /* 181 * Lock to serialize the NIU device node related operations. 182 */ 183 kmutex_t vdds_dev_lock; 184 185 boolean_t vdds_hv_hio_capable = B_FALSE; 186 187 /* 188 * vdds_mod_init -- one time initialization. 189 */ 190 void 191 vdds_mod_init(void) 192 { 193 int i; 194 int rv; 195 uint64_t minor = 0; 196 197 /* 198 * Try register one by one from niu_hsvc. 199 */ 200 for (i = 0; i < (sizeof (niu_hsvc) / sizeof (hsvc_info_t)); i++) { 201 rv = hsvc_register(&niu_hsvc[i], &minor); 202 if (rv == 0) { 203 if (minor == niu_hsvc[i].hsvc_minor) { 204 vdds_hv_hio_capable = B_TRUE; 205 niu_hsvc_index = i; 206 break; 207 } else { 208 (void) hsvc_unregister(&niu_hsvc[i]); 209 } 210 } 211 } 212 mutex_init(&vdds_dev_lock, NULL, MUTEX_DRIVER, NULL); 213 DBG2(NULL, "HV HIO capable=%d ver(%ld.%ld)", vdds_hv_hio_capable, 214 (niu_hsvc_index == -1) ? 0 : niu_hsvc[niu_hsvc_index].hsvc_major, 215 minor); 216 } 217 218 /* 219 * vdds_mod_fini -- one time cleanup. 220 */ 221 void 222 vdds_mod_fini(void) 223 { 224 if (niu_hsvc_index != -1) { 225 (void) hsvc_unregister(&niu_hsvc[niu_hsvc_index]); 226 } 227 mutex_destroy(&vdds_dev_lock); 228 } 229 230 /* 231 * vdds_init -- vnet instance related DDS related initialization. 232 */ 233 int 234 vdds_init(vnet_t *vnetp) 235 { 236 vnet_dds_info_t *vdds = &vnetp->vdds_info; 237 char qname[TASKQ_NAMELEN]; 238 239 vdds->vnetp = vnetp; 240 DBG1(vdds, "Initializing.."); 241 (void) snprintf(qname, TASKQ_NAMELEN, "vdds_taskq%d", vnetp->instance); 242 if ((vdds->dds_taskqp = ddi_taskq_create(vnetp->dip, qname, 1, 243 TASKQ_DEFAULTPRI, 0)) == NULL) { 244 cmn_err(CE_WARN, "!vnet%d: Unable to create DDS task queue", 245 vnetp->instance); 246 return (ENOMEM); 247 } 248 mutex_init(&vdds->lock, NULL, MUTEX_DRIVER, NULL); 249 return (0); 250 } 251 252 /* 253 * vdds_cleanup -- vnet instance related cleanup. 254 */ 255 void 256 vdds_cleanup(vnet_t *vnetp) 257 { 258 vnet_dds_info_t *vdds = &vnetp->vdds_info; 259 260 DBG1(vdds, "Cleanup..."); 261 /* Cleanup/destroy any hybrid resouce that exists */ 262 vdds_cleanup_hybrid_res(vnetp); 263 264 /* taskq_destroy will wait for all taskqs to complete */ 265 ddi_taskq_destroy(vdds->dds_taskqp); 266 vdds->dds_taskqp = NULL; 267 mutex_destroy(&vdds->lock); 268 DBG1(vdds, "Cleanup complete"); 269 } 270 271 /* 272 * vdds_cleanup_hybrid_res -- Cleanup Hybrid resource. 273 */ 274 void 275 vdds_cleanup_hybrid_res(void *arg) 276 { 277 vnet_t *vnetp = arg; 278 vnet_dds_info_t *vdds = &vnetp->vdds_info; 279 280 DBG1(vdds, "Hybrid device cleanup..."); 281 mutex_enter(&vdds->lock); 282 if (vdds->task_flags == VNET_DDS_TASK_ADD_SHARE) { 283 /* 284 * Task for ADD_SHARE is pending, simply 285 * cleanup the flags, the task will quit without 286 * any changes. 287 */ 288 vdds->task_flags = 0; 289 DBG2(vdds, "Task for ADD is pending, clean flags only"); 290 } else if ((vdds->hio_dip != NULL) && (vdds->task_flags == 0)) { 291 /* 292 * There is no task pending and a hybrid device 293 * is present, so dispatch a task to release the share. 294 */ 295 vdds->task_flags = VNET_DDS_TASK_REL_SHARE; 296 (void) ddi_taskq_dispatch(vdds->dds_taskqp, 297 vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP); 298 DBG2(vdds, "Dispatched a task to destroy HIO device"); 299 } 300 /* 301 * Other possible cases include either DEL_SHARE or 302 * REL_SHARE as pending. In that case, there is nothing 303 * to do as a task is already pending to do the cleanup. 304 */ 305 mutex_exit(&vdds->lock); 306 DBG1(vdds, "Hybrid device cleanup complete"); 307 } 308 309 /* 310 * vdds_cleanup_hio -- An interface to cleanup the hio resources before 311 * resetting the vswitch port. 312 */ 313 void 314 vdds_cleanup_hio(vnet_t *vnetp) 315 { 316 vnet_dds_info_t *vdds = &vnetp->vdds_info; 317 318 /* Wait for any pending vdds tasks to complete */ 319 ddi_taskq_wait(vdds->dds_taskqp); 320 vdds_cleanup_hybrid_res(vnetp); 321 /* Wait for the cleanup task to complete */ 322 ddi_taskq_wait(vdds->dds_taskqp); 323 } 324 325 /* 326 * vdds_process_dds_msg -- Process a DDS message. 327 */ 328 void 329 vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg) 330 { 331 vnet_dds_info_t *vdds = &vnetp->vdds_info; 332 int rv; 333 334 DBG1(vdds, "DDS message received..."); 335 336 if (dmsg->dds_class != DDS_VNET_NIU) { 337 DBG2(vdds, "Invalid class send NACK"); 338 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 339 return; 340 } 341 mutex_enter(&vdds->lock); 342 switch (dmsg->dds_subclass) { 343 case DDS_VNET_ADD_SHARE: 344 DBG2(vdds, "DDS_VNET_ADD_SHARE message..."); 345 if ((vdds->task_flags != 0) || (vdds->hio_dip != NULL)) { 346 /* 347 * Either a task is already pending or 348 * a hybrid device already exists. 349 */ 350 DWARN(vdds, "NACK: Already pending DDS task"); 351 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 352 mutex_exit(&vdds->lock); 353 return; 354 } 355 vdds->task_flags = VNET_DDS_TASK_ADD_SHARE; 356 bcopy(dmsg, &vnetp->vdds_info.dmsg, sizeof (vio_dds_msg_t)); 357 DBG2(vdds, "Dispatching task for ADD_SHARE"); 358 rv = ddi_taskq_dispatch(vdds->dds_taskqp, 359 vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP); 360 if (rv != 0) { 361 /* Send NACK */ 362 DBG2(vdds, "NACK: Failed to dispatch task"); 363 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 364 vdds->task_flags = 0; 365 } 366 break; 367 368 case DDS_VNET_DEL_SHARE: 369 DBG2(vdds, "DDS_VNET_DEL_SHARE message..."); 370 if (vdds->task_flags == VNET_DDS_TASK_ADD_SHARE) { 371 /* 372 * ADD_SHARE task still pending, simply clear 373 * task falgs and ACK. 374 */ 375 DBG2(vdds, "ACK:ADD_SHARE task still pending"); 376 vdds->task_flags = 0; 377 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_TRUE); 378 mutex_exit(&vdds->lock); 379 return; 380 } 381 if ((vdds->task_flags == 0) && (vdds->hio_dip == NULL)) { 382 /* Send NACK */ 383 DBG2(vdds, "NACK:No HIO device exists"); 384 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 385 mutex_exit(&vdds->lock); 386 return; 387 } 388 vdds->task_flags = VNET_DDS_TASK_DEL_SHARE; 389 bcopy(dmsg, &vdds->dmsg, sizeof (vio_dds_msg_t)); 390 DBG2(vdds, "Dispatching DEL_SHARE task"); 391 rv = ddi_taskq_dispatch(vdds->dds_taskqp, 392 vdds_process_dds_msg_task, vnetp, DDI_NOSLEEP); 393 if (rv != 0) { 394 /* Send NACK */ 395 DBG2(vdds, "NACK: failed to dispatch task"); 396 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 397 vdds->task_flags = 0; 398 } 399 break; 400 case DDS_VNET_REL_SHARE: 401 DBG2(vdds, "Reply for REL_SHARE reply=%d", 402 dmsg->tag.vio_subtype); 403 break; 404 default: 405 DWARN(vdds, "Discarding Unknown DDS message"); 406 break; 407 } 408 mutex_exit(&vdds->lock); 409 } 410 411 /* 412 * vdds_process_dds_msg_task -- Called from a taskq to process the 413 * DDS message. 414 */ 415 static void 416 vdds_process_dds_msg_task(void *arg) 417 { 418 vnet_t *vnetp = arg; 419 vnet_dds_info_t *vdds = &vnetp->vdds_info; 420 vio_dds_msg_t *dmsg = &vdds->dmsg; 421 dev_info_t *dip; 422 uint32_t max_frame_size; 423 uint64_t hio_cookie; 424 int rv; 425 426 DBG1(vdds, "DDS task started..."); 427 mutex_enter(&vdds->lock); 428 switch (vdds->task_flags) { 429 case VNET_DDS_TASK_ADD_SHARE: 430 DBG2(vdds, "ADD_SHARE task..."); 431 hio_cookie = dmsg->msg.share_msg.cookie; 432 /* 433 * max-frame-size value need to be set to 434 * the full ethernet frame size. That is, 435 * header + payload + checksum. 436 */ 437 max_frame_size = vnetp->mtu + 438 sizeof (struct ether_vlan_header) + ETHERFCSL; 439 dip = vdds_create_niu_node(hio_cookie, 440 dmsg->msg.share_msg.macaddr, max_frame_size); 441 if (dip == NULL) { 442 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 443 DERR(vdds, "Failed to create HIO node"); 444 } else { 445 vdds->hio_dip = dip; 446 vdds->hio_cookie = hio_cookie; 447 (void) snprintf(vdds->hio_ifname, 448 sizeof (vdds->hio_ifname), "%s%d", 449 ddi_driver_name(dip), ddi_get_instance(dip)); 450 451 rv = vnet_hio_mac_init(vnetp, vdds->hio_ifname); 452 if (rv != 0) { 453 /* failed - cleanup, send failed DDS message */ 454 DERR(vdds, "HIO mac init failed, cleaning up"); 455 rv = vdds_destroy_niu_node(dip, hio_cookie); 456 if (rv == 0) { 457 /* use DERR to print by default */ 458 DERR(vdds, "Successfully destroyed" 459 " Hybrid node"); 460 } else { 461 cmn_err(CE_WARN, "vnet%d:Failed to " 462 "destroy Hybrid node", 463 vnetp->instance); 464 } 465 vdds->hio_dip = NULL; 466 vdds->hio_cookie = 0; 467 (void) vdds_send_dds_resp_msg(vnetp, 468 dmsg, B_FALSE); 469 } else { 470 (void) vdds_send_dds_resp_msg(vnetp, 471 dmsg, B_TRUE); 472 } 473 /* DERR used only print by default */ 474 DERR(vdds, "Successfully created HIO node"); 475 } 476 break; 477 478 case VNET_DDS_TASK_DEL_SHARE: 479 DBG2(vdds, "DEL_SHARE task..."); 480 if (vnetp->vdds_info.hio_dip == NULL) { 481 DBG2(vdds, "NACK: No HIO device destroy"); 482 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_FALSE); 483 } else { 484 vnet_hio_mac_cleanup(vnetp); 485 rv = vdds_destroy_niu_node(vnetp->vdds_info.hio_dip, 486 vdds->hio_cookie); 487 if (rv == 0) { 488 /* use DERR to print by default */ 489 DERR(vdds, "Successfully destroyed" 490 " Hybrid node"); 491 } else { 492 cmn_err(CE_WARN, "vnet%d:Failed to " 493 "destroy Hybrid node", vnetp->instance); 494 } 495 /* TODO: send ACK even for failure? */ 496 DBG2(vdds, "ACK: HIO device destroyed"); 497 (void) vdds_send_dds_resp_msg(vnetp, dmsg, B_TRUE); 498 vdds->hio_dip = 0; 499 vdds->hio_cookie = 0; 500 } 501 break; 502 case VNET_DDS_TASK_REL_SHARE: 503 DBG2(vdds, "REL_SHARE task..."); 504 if (vnetp->vdds_info.hio_dip != NULL) { 505 vnet_hio_mac_cleanup(vnetp); 506 rv = vdds_destroy_niu_node(vnetp->vdds_info.hio_dip, 507 vdds->hio_cookie); 508 if (rv == 0) { 509 DERR(vdds, "Successfully destroyed " 510 "Hybrid node"); 511 } else { 512 cmn_err(CE_WARN, "vnet%d:Failed to " 513 "destroy HIO node", vnetp->instance); 514 } 515 /* TODO: failure case */ 516 (void) vdds_send_dds_rel_msg(vnetp); 517 vdds->hio_dip = 0; 518 vdds->hio_cookie = 0; 519 } 520 break; 521 default: 522 break; 523 } 524 vdds->task_flags = 0; 525 mutex_exit(&vdds->lock); 526 } 527 528 /* 529 * vdds_send_dds_rel_msg -- Send a DDS_REL_SHARE message. 530 */ 531 static int 532 vdds_send_dds_rel_msg(vnet_t *vnetp) 533 { 534 vnet_dds_info_t *vdds = &vnetp->vdds_info; 535 vio_dds_msg_t vmsg; 536 dds_share_msg_t *smsg = &vmsg.msg.share_msg; 537 int rv; 538 539 DBG1(vdds, "Sending DDS_VNET_REL_SHARE message"); 540 vmsg.tag.vio_msgtype = VIO_TYPE_CTRL; 541 vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO; 542 vmsg.tag.vio_subtype_env = VIO_DDS_INFO; 543 /* vio_sid filled by the LDC module */ 544 vmsg.dds_class = DDS_VNET_NIU; 545 vmsg.dds_subclass = DDS_VNET_REL_SHARE; 546 vmsg.dds_req_id = (++vdds->dds_req_id); 547 smsg->macaddr = vnet_macaddr_strtoul(vnetp->curr_macaddr); 548 smsg->cookie = vdds->hio_cookie; 549 rv = vnet_send_dds_msg(vnetp, &vmsg); 550 return (rv); 551 } 552 553 /* 554 * vdds_send_dds_resp_msg -- Send a DDS response message. 555 */ 556 static int 557 vdds_send_dds_resp_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg, int ack) 558 { 559 vnet_dds_info_t *vdds = &vnetp->vdds_info; 560 int rv; 561 562 DBG1(vdds, "Sending a response mesage=%d", ack); 563 if (ack == B_TRUE) { 564 dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK; 565 dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS; 566 } else { 567 dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK; 568 dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL; 569 } 570 rv = vnet_send_dds_msg(vnetp, dmsg); 571 return (rv); 572 } 573 574 /* 575 * vdds_create_niu_node -- Create NIU Hybrid node. The NIU nexus 576 * node also created if it doesn't exist already. 577 */ 578 dev_info_t * 579 vdds_create_niu_node(uint64_t cookie, uint64_t macaddr, uint32_t max_frame_size) 580 { 581 dev_info_t *nexus_dip; 582 dev_info_t *niu_dip; 583 vdds_cb_arg_t cba; 584 585 DBG1(NULL, "Called"); 586 587 if (vdds_hv_hio_capable == B_FALSE) { 588 return (NULL); 589 } 590 mutex_enter(&vdds_dev_lock); 591 /* Check if the nexus node exists already */ 592 nexus_dip = vdds_find_node(cookie, ddi_root_node(), 593 vdds_match_niu_nexus); 594 if (nexus_dip == NULL) { 595 /* 596 * NIU nexus node not found, so create it now. 597 */ 598 cba.dip = NULL; 599 cba.cookie = cookie; 600 cba.macaddr = macaddr; 601 cba.max_frame_size = max_frame_size; 602 nexus_dip = vdds_create_new_node(&cba, NULL, 603 vdds_new_nexus_node); 604 if (nexus_dip == NULL) { 605 mutex_exit(&vdds_dev_lock); 606 return (NULL); 607 } 608 } 609 DBG2(NULL, "nexus_dip = 0x%p", nexus_dip); 610 611 /* Check if NIU node exists already before creating one */ 612 niu_dip = vdds_find_node(cookie, nexus_dip, 613 vdds_match_niu_node); 614 if (niu_dip == NULL) { 615 cba.dip = NULL; 616 cba.cookie = cookie; 617 cba.macaddr = macaddr; 618 cba.max_frame_size = max_frame_size; 619 niu_dip = vdds_create_new_node(&cba, nexus_dip, 620 vdds_new_niu_node); 621 /* 622 * Hold the niu_dip to prevent it from 623 * detaching. 624 */ 625 if (niu_dip != NULL) { 626 e_ddi_hold_devi(niu_dip); 627 } else { 628 DWARN(NULL, "niumx/network node creation failed"); 629 } 630 } else { 631 DWARN(NULL, "niumx/network node already exists(dip=0x%p)", 632 niu_dip); 633 } 634 /* release the hold that was done in find/create */ 635 if ((niu_dip != NULL) && (e_ddi_branch_held(niu_dip))) 636 e_ddi_branch_rele(niu_dip); 637 if (e_ddi_branch_held(nexus_dip)) 638 e_ddi_branch_rele(nexus_dip); 639 mutex_exit(&vdds_dev_lock); 640 DBG1(NULL, "returning niu_dip=0x%p", niu_dip); 641 return (niu_dip); 642 } 643 644 /* 645 * vdds_destroy_niu_node -- Destroy the NIU node. 646 */ 647 int 648 vdds_destroy_niu_node(dev_info_t *niu_dip, uint64_t cookie) 649 { 650 int rv; 651 dev_info_t *fdip = NULL; 652 dev_info_t *nexus_dip = ddi_get_parent(niu_dip); 653 654 655 DBG1(NULL, "Called"); 656 ASSERT(nexus_dip != NULL); 657 mutex_enter(&vdds_dev_lock); 658 659 if (!e_ddi_branch_held(niu_dip)) 660 e_ddi_branch_hold(niu_dip); 661 /* 662 * As we are destroying now, release the 663 * hold that was done in during the creation. 664 */ 665 ddi_release_devi(niu_dip); 666 rv = e_ddi_branch_destroy(niu_dip, &fdip, 0); 667 if (rv != 0) { 668 DERR(NULL, "Failed to destroy niumx/network node dip=0x%p", 669 niu_dip); 670 if (fdip != NULL) { 671 ddi_release_devi(fdip); 672 } 673 rv = EBUSY; 674 goto dest_exit; 675 } 676 /* 677 * Cleanup the parent's ranges property set 678 * for this Hybrid device. 679 */ 680 vdds_release_range_prop(nexus_dip, cookie); 681 682 dest_exit: 683 mutex_exit(&vdds_dev_lock); 684 DBG1(NULL, "returning rv=%d", rv); 685 return (rv); 686 } 687 688 /* 689 * vdds_match_niu_nexus -- callback function to verify a node is the 690 * NIU nexus node. 691 */ 692 static int 693 vdds_match_niu_nexus(dev_info_t *dip, void *arg) 694 { 695 vdds_cb_arg_t *warg = (vdds_cb_arg_t *)arg; 696 vdds_reg_t *reg_p; 697 char *name; 698 uint64_t hdl; 699 uint_t reglen; 700 int rv; 701 702 if (dip == ddi_root_node()) { 703 return (DDI_WALK_CONTINUE); 704 } 705 706 name = ddi_node_name(dip); 707 if (strcmp(name, "niu") != 0) { 708 return (DDI_WALK_CONTINUE); 709 } 710 rv = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 711 DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len); 712 if (rv != DDI_PROP_SUCCESS) { 713 DWARN(NULL, "Failed to get reg property dip=0x%p", dip); 714 return (DDI_WALK_CONTINUE); 715 } 716 717 hdl = reg_p->addr_hi & 0x0FFFFFFF; 718 ddi_prop_free(reg_p); 719 720 DBG2(NULL, "Handle = 0x%lx dip=0x%p", hdl, dip); 721 if (hdl == NIUCFGHDL(warg->cookie)) { 722 /* Hold before returning */ 723 if (!e_ddi_branch_held(dip)) 724 e_ddi_branch_hold(dip); 725 warg->dip = dip; 726 DBG2(NULL, "Found dip = 0x%p", dip); 727 return (DDI_WALK_TERMINATE); 728 } 729 return (DDI_WALK_CONTINUE); 730 } 731 732 /* 733 * vdds_match_niu_node -- callback function to verify a node is the 734 * NIU Hybrid node. 735 */ 736 static int 737 vdds_match_niu_node(dev_info_t *dip, void *arg) 738 { 739 vdds_cb_arg_t *warg = (vdds_cb_arg_t *)arg; 740 char *name; 741 vdds_reg_t *reg_p; 742 uint_t reglen; 743 int rv; 744 uint32_t addr_hi; 745 746 name = ddi_node_name(dip); 747 if (strcmp(name, "network") != 0) { 748 return (DDI_WALK_CONTINUE); 749 } 750 rv = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 751 DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len); 752 if (rv != DDI_PROP_SUCCESS) { 753 DWARN(NULL, "Failed to get reg property dip=0x%p", dip); 754 return (DDI_WALK_CONTINUE); 755 } 756 757 addr_hi = reg_p->addr_hi; 758 DBG1(NULL, "addr_hi = 0x%x dip=0x%p", addr_hi, dip); 759 ddi_prop_free(reg_p); 760 if (addr_hi == HVCOOKIE(warg->cookie)) { 761 warg->dip = dip; 762 if (!e_ddi_branch_held(dip)) 763 e_ddi_branch_hold(dip); 764 DBG1(NULL, "Found dip = 0x%p", dip); 765 return (DDI_WALK_TERMINATE); 766 } 767 return (DDI_WALK_CONTINUE); 768 } 769 770 /* 771 * vdds_new_nexus_node -- callback function to set all the properties 772 * a new NIU nexus node. 773 */ 774 static int 775 vdds_new_nexus_node(dev_info_t *dip, void *arg, uint_t flags) 776 { 777 vdds_cb_arg_t *cba = (vdds_cb_arg_t *)arg; 778 char *compat[] = { "SUNW,niumx" }; 779 vdds_ranges_t *rangesp; 780 vdds_reg_t reg; 781 uint64_t nranges; 782 int n; 783 784 DBG1(NULL, "Called dip=0x%p, flags=0x%X", dip, flags); 785 786 /* create "niu" property */ 787 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "name", "niu") != 788 DDI_SUCCESS) { 789 DERR(NULL, "Failed to create name property(dip=0x%p)", dip); 790 return (DDI_WALK_ERROR); 791 } 792 793 /* create "compatible" property */ 794 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible", 795 compat, 1) != DDI_SUCCESS) { 796 DERR(NULL, "Failed to create compatible property(dip=0x%p)", 797 dip); 798 return (DDI_WALK_ERROR); 799 } 800 801 /* create "device_type" property */ 802 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, 803 "device_type", "sun4v") != DDI_SUCCESS) { 804 DERR(NULL, "Failed to create device_type property(dip=0x%p)", 805 dip); 806 return (DDI_WALK_ERROR); 807 } 808 809 /* 810 * create "reg" property. The first 28 bits of 811 * 'addr_hi' are NIU cfg_handle, the 0xc in 28-31 bits 812 * indicates non-cacheable config. 813 */ 814 reg.addr_hi = 0xc0000000 | NIUCFGHDL(cba->cookie); 815 reg.addr_lo = 0; 816 reg.size_hi = 0; 817 reg.size_lo = 0; 818 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, 819 "reg", (int *)®, sizeof (reg)/sizeof (int)) != DDI_SUCCESS) { 820 DERR(NULL, "Failed to create reg property(dip=0x%p)", dip); 821 return (DDI_WALK_ERROR); 822 } 823 824 /* 825 * Create VDDS_MAX_RANGES so that they are already in place 826 * before the children are created. While creating the child 827 * we just modify one of this ranges entries. 828 */ 829 nranges = VDDS_MAX_RANGES; /* One range for each VR */ 830 rangesp = (vdds_ranges_t *)kmem_zalloc( 831 (sizeof (vdds_ranges_t) * nranges), KM_SLEEP); 832 833 for (n = 0; n < nranges; n++) { 834 /* zero all child_hi/lo */ 835 rangesp[n].child_hi = 0; 836 rangesp[n].child_lo = 0; 837 } 838 839 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 840 (int *)rangesp, (nranges * 6)) != DDI_SUCCESS) { 841 DERR(NULL, "Failed to create ranges property(dip=0x%p)", dip); 842 kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges)); 843 return (DDI_WALK_ERROR); 844 } 845 846 /* create "#size-cells" property */ 847 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, 848 "#size-cells", 2) != DDI_SUCCESS) { 849 DERR(NULL, "Failed to create #size-cells property(dip=0x%p)", 850 dip); 851 kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges)); 852 return (DDI_WALK_ERROR); 853 } 854 855 /* create "#address-cells" property */ 856 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, 857 "#address-cells", 2) != DDI_SUCCESS) { 858 DERR(NULL, "Failed to create #address-cells prop(dip=0x%p)", 859 dip); 860 kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges)); 861 return (DDI_WALK_ERROR); 862 } 863 864 kmem_free(rangesp, (sizeof (vdds_ranges_t) * nranges)); 865 cba->dip = dip; 866 DBG1(NULL, "Returning (dip=0x%p)", dip); 867 return (DDI_WALK_TERMINATE); 868 } 869 870 /* 871 * vdds_new_niu_node -- callback function to create a new NIU Hybrid node. 872 */ 873 static int 874 vdds_new_niu_node(dev_info_t *dip, void *arg, uint_t flags) 875 { 876 vdds_cb_arg_t *cba = (vdds_cb_arg_t *)arg; 877 char *compat[] = { "SUNW,niusl" }; 878 uint8_t macaddrbytes[ETHERADDRL]; 879 int interrupts[VDDS_MAX_VRINTRS]; 880 vdds_ranges_t *prng; 881 vdds_ranges_t *prp; 882 vdds_reg_t reg; 883 dev_info_t *pdip; 884 uint64_t start; 885 uint64_t size; 886 int prnglen; 887 int nintr = 0; 888 int nrng; 889 int rnum; 890 int rv; 891 892 DBG1(NULL, "Called dip=0x%p flags=0x%X", dip, flags); 893 pdip = ddi_get_parent(dip); 894 895 if (pdip == NULL) { 896 DWARN(NULL, "Failed to get parent dip(dip=0x%p)", dip); 897 return (DDI_WALK_ERROR); 898 } 899 900 /* create "network" property */ 901 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "name", "network") != 902 DDI_SUCCESS) { 903 DERR(NULL, "Failed to create name property(dip=0x%p)", dip); 904 return (DDI_WALK_ERROR); 905 } 906 907 /* 908 * create "niutype" property, it is set to n2niu to 909 * indicate NIU Hybrid node. 910 */ 911 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, "niutype", 912 "n2niu") != DDI_SUCCESS) { 913 DERR(NULL, "Failed to create niuopmode property(dip=0x%p)", 914 dip); 915 return (DDI_WALK_ERROR); 916 } 917 918 /* create "compatible" property */ 919 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible", 920 compat, 1) != DDI_SUCCESS) { 921 DERR(NULL, "Failed to create compatible property(dip=0x%p)", 922 dip); 923 return (DDI_WALK_ERROR); 924 } 925 926 /* create "device_type" property */ 927 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, 928 "device_type", "network") != DDI_SUCCESS) { 929 DERR(NULL, "Failed to create device_type property(dip=0x%p)", 930 dip); 931 return (DDI_WALK_ERROR); 932 } 933 934 /* create "reg" property */ 935 if (vdds_hv_niu_vr_getinfo(HVCOOKIE(cba->cookie), 936 &start, &size) != H_EOK) { 937 DERR(NULL, "Failed to get vrinfo for cookie(0x%lX)", 938 cba->cookie); 939 return (DDI_WALK_ERROR); 940 } 941 reg.addr_hi = HVCOOKIE(cba->cookie); 942 reg.addr_lo = 0; 943 reg.size_hi = 0; 944 reg.size_lo = size; 945 946 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg", 947 (int *)®, sizeof (reg) / sizeof (int)) != DDI_SUCCESS) { 948 DERR(NULL, "Failed to create reg property(dip=0x%p)", dip); 949 return (DDI_WALK_ERROR); 950 } 951 952 /* 953 * Modify the parent's ranges property to map the "reg" property 954 * of the new child. 955 */ 956 if ((rv = ddi_getlongprop(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 957 "ranges", (caddr_t)&prng, &prnglen)) != DDI_SUCCESS) { 958 DERR(NULL, 959 "Failed to get parent's ranges property(pdip=0x%p) rv=%d", 960 pdip, rv); 961 return (DDI_WALK_ERROR); 962 } 963 nrng = prnglen/(sizeof (vdds_ranges_t)); 964 /* 965 * First scan all ranges to see if a range corresponding 966 * to this virtual NIU exists already. 967 */ 968 for (rnum = 0; rnum < nrng; rnum++) { 969 prp = &prng[rnum]; 970 if (prp->child_hi == HVCOOKIE(cba->cookie)) { 971 break; 972 } 973 } 974 if (rnum == nrng) { 975 /* Now to try to find an empty range */ 976 for (rnum = 0; rnum < nrng; rnum++) { 977 prp = &prng[rnum]; 978 if (prp->child_hi == 0) { 979 break; 980 } 981 } 982 } 983 if (rnum == nrng) { 984 DERR(NULL, "No free ranges entry found"); 985 return (DDI_WALK_ERROR); 986 } 987 988 /* 989 * child_hi will have HV cookie as HV cookie is more like 990 * a port in the HybridIO. 991 */ 992 prp->child_hi = HVCOOKIE(cba->cookie); 993 prp->child_lo = 0; 994 prp->parent_hi = 0x80000000 | (start >> 32); 995 prp->parent_lo = start & 0x00000000FFFFFFFF; 996 prp->size_hi = (size >> 32); 997 prp->size_lo = size & 0x00000000FFFFFFFF; 998 999 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, pdip, "ranges", 1000 (int *)prng, (nrng * 6)) != DDI_SUCCESS) { 1001 DERR(NULL, "Failed to update parent ranges prop(pdip=0x%p)", 1002 pdip); 1003 return (DDI_WALK_ERROR); 1004 } 1005 kmem_free((void *)prng, prnglen); 1006 1007 vnet_macaddr_ultostr(cba->macaddr, macaddrbytes); 1008 1009 /* 1010 * create "local-mac-address" property, this will be same as 1011 * the vnet's mac-address. 1012 */ 1013 if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, "local-mac-address", 1014 macaddrbytes, ETHERADDRL) != DDI_SUCCESS) { 1015 DERR(NULL, "Failed to update mac-addresses property(dip=0x%p)", 1016 dip); 1017 return (DDI_WALK_ERROR); 1018 } 1019 1020 rv = vdds_get_interrupts(cba->cookie, rnum, interrupts, &nintr); 1021 if (rv != 0) { 1022 DERR(NULL, "Failed to get interrupts for cookie=0x%lx", 1023 cba->cookie); 1024 return (DDI_WALK_ERROR); 1025 } 1026 1027 /* create "interrupts" property */ 1028 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts", 1029 interrupts, nintr) != DDI_SUCCESS) { 1030 DERR(NULL, "Failed to update interrupts property(dip=0x%p)", 1031 dip); 1032 return (DDI_WALK_ERROR); 1033 } 1034 1035 1036 /* create "max_frame_size" property */ 1037 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "max-frame-size", 1038 cba->max_frame_size) != DDI_SUCCESS) { 1039 DERR(NULL, "Failed to update max-frame-size property(dip=0x%p)", 1040 dip); 1041 return (DDI_WALK_ERROR); 1042 } 1043 1044 cba->dip = dip; 1045 DBG1(NULL, "Returning dip=0x%p", dip); 1046 return (DDI_WALK_TERMINATE); 1047 } 1048 1049 1050 /* 1051 * vdds_find_node -- A common function to find a NIU nexus or NIU node. 1052 */ 1053 static dev_info_t * 1054 vdds_find_node(uint64_t cookie, dev_info_t *sdip, 1055 int (*match_func)(dev_info_t *dip, void *arg)) 1056 { 1057 vdds_cb_arg_t arg; 1058 dev_info_t *pdip; 1059 1060 DBG1(NULL, "Called cookie=%lx\n", cookie); 1061 1062 arg.dip = NULL; 1063 arg.cookie = cookie; 1064 1065 if (pdip = ddi_get_parent(sdip)) { 1066 ndi_devi_enter(pdip); 1067 } 1068 1069 ddi_walk_devs(sdip, match_func, (void *)&arg); 1070 if (pdip != NULL) { 1071 ndi_devi_exit(pdip); 1072 } 1073 1074 DBG1(NULL, "Returning dip=0x%p", arg.dip); 1075 return (arg.dip); 1076 } 1077 1078 /* 1079 * vdds_create_new_node -- A common function to create NIU nexus/NIU node. 1080 */ 1081 static dev_info_t * 1082 vdds_create_new_node(vdds_cb_arg_t *cbap, dev_info_t *pdip, 1083 int (*new_node_func)(dev_info_t *dip, void *arg, uint_t flags)) 1084 { 1085 devi_branch_t br; 1086 int rv; 1087 1088 DBG1(NULL, "Called cookie=0x%lx", cbap->cookie); 1089 1090 br.arg = (void *)cbap; 1091 br.type = DEVI_BRANCH_SID; 1092 br.create.sid_branch_create = new_node_func; 1093 br.devi_branch_callback = NULL; 1094 1095 if (pdip == NULL) { 1096 pdip = ddi_root_node(); 1097 } 1098 DBG1(NULL, "calling e_ddi_branch_create"); 1099 if ((rv = e_ddi_branch_create(pdip, &br, NULL, 1100 DEVI_BRANCH_CHILD | DEVI_BRANCH_CONFIGURE))) { 1101 DERR(NULL, "e_ddi_branch_create failed=%d", rv); 1102 return (NULL); 1103 } 1104 DBG1(NULL, "Returning(dip=0x%p", cbap->dip); 1105 return (cbap->dip); 1106 } 1107 1108 /* 1109 * vdds_get_interrupts -- A function that binds ino's to channels and 1110 * then provides them to create interrupts property. 1111 */ 1112 static int 1113 vdds_get_interrupts(uint64_t cookie, int ino_range, int *intrs, int *nintr) 1114 { 1115 uint32_t hvcookie = HVCOOKIE(cookie); 1116 uint64_t txmap; 1117 uint64_t rxmap; 1118 uint32_t ino = VDDS_INO_RANGE_START(ino_range); 1119 int rv; 1120 uint64_t i; 1121 1122 *nintr = 0; 1123 rv = vdds_hv_niu_vr_get_txmap(hvcookie, &txmap); 1124 if (rv != H_EOK) { 1125 DWARN(NULL, "Failed to get txmap for hvcookie=0x%X rv=%d\n", 1126 hvcookie, rv); 1127 return (EIO); 1128 } 1129 rv = vdds_hv_niu_vr_get_rxmap(hvcookie, &rxmap); 1130 if (rv != H_EOK) { 1131 DWARN(NULL, "Failed to get rxmap for hvcookie=0x%X, rv=%d\n", 1132 hvcookie, rv); 1133 return (EIO); 1134 } 1135 /* Check if the number of total channels to be more than 8 */ 1136 for (i = 0; i < 4; i++) { 1137 if (rxmap & (((uint64_t)0x1) << i)) { 1138 rv = vdds_hv_niu_vrrx_set_ino(hvcookie, i, ino); 1139 if (rv != H_EOK) { 1140 DWARN(NULL, "Failed to get Rx ino for " 1141 "hvcookie=0x%X vch_idx=0x%lx rv=%d\n", 1142 hvcookie, i, rv); 1143 return (EIO); 1144 } 1145 DWARN(NULL, 1146 "hvcookie=0x%X RX vch_idx=0x%lx ino=0x%X\n", 1147 hvcookie, i, ino); 1148 *intrs = ino; 1149 ino++; 1150 } else { 1151 *intrs = VDDS_MAX_INTR_NUM; 1152 } 1153 intrs++; 1154 *nintr += 1; 1155 } 1156 for (i = 0; i < 4; i++) { 1157 if (txmap & (((uint64_t)0x1) << i)) { 1158 rv = vdds_hv_niu_vrtx_set_ino(hvcookie, i, ino); 1159 if (rv != H_EOK) { 1160 DWARN(NULL, "Failed to get Tx ino for " 1161 "hvcookie=0x%X vch_idx=0x%lx rv=%d\n", 1162 hvcookie, i, rv); 1163 return (EIO); 1164 } 1165 DWARN(NULL, "hvcookie=0x%X TX vch_idx=0x%lx ino=0x%X\n", 1166 hvcookie, i, ino); 1167 *intrs = ino; 1168 ino++; 1169 } else { 1170 *intrs = VDDS_MAX_INTR_NUM; 1171 } 1172 intrs++; 1173 *nintr += 1; 1174 } 1175 return (0); 1176 } 1177 1178 /* 1179 * vdds_release_range_prop -- cleanups an entry in the ranges property 1180 * corresponding to a cookie. 1181 */ 1182 static void 1183 vdds_release_range_prop(dev_info_t *nexus_dip, uint64_t cookie) 1184 { 1185 vdds_ranges_t *prng; 1186 vdds_ranges_t *prp; 1187 int prnglen; 1188 int nrng; 1189 int rnum; 1190 boolean_t success = B_FALSE; 1191 int rv; 1192 1193 if ((rv = ddi_getlongprop(DDI_DEV_T_ANY, nexus_dip, DDI_PROP_DONTPASS, 1194 "ranges", (caddr_t)&prng, &prnglen)) != DDI_SUCCESS) { 1195 DERR(NULL, 1196 "Failed to get nexus ranges property(dip=0x%p) rv=%d", 1197 nexus_dip, rv); 1198 return; 1199 } 1200 nrng = prnglen/(sizeof (vdds_ranges_t)); 1201 for (rnum = 0; rnum < nrng; rnum++) { 1202 prp = &prng[rnum]; 1203 if (prp->child_hi == HVCOOKIE(cookie)) { 1204 prp->child_hi = 0; 1205 success = B_TRUE; 1206 break; 1207 } 1208 } 1209 if (success) { 1210 if (ndi_prop_update_int_array(DDI_DEV_T_NONE, nexus_dip, 1211 "ranges", (int *)prng, (nrng * 6)) != DDI_SUCCESS) { 1212 DERR(NULL, 1213 "Failed to update nexus ranges prop(dip=0x%p)", 1214 nexus_dip); 1215 } 1216 } 1217 } 1218