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