xref: /illumos-gate/usr/src/uts/common/io/nxge/nxge_hio.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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  * nxge_hio.c
29  *
30  * This file manages the virtualization resources for Neptune
31  * devices.  That is, it implements a hybrid I/O (HIO) approach in the
32  * Solaris kernel, whereby a guest domain on an LDOMs server may
33  * request & use hardware resources from the service domain.
34  *
35  */
36 
37 #include <sys/mac_provider.h>
38 #include <sys/nxge/nxge_impl.h>
39 #include <sys/nxge/nxge_fzc.h>
40 #include <sys/nxge/nxge_rxdma.h>
41 #include <sys/nxge/nxge_txdma.h>
42 #include <sys/nxge/nxge_hio.h>
43 
44 /*
45  * External prototypes
46  */
47 extern npi_status_t npi_rxdma_dump_rdc_table(npi_handle_t, uint8_t);
48 
49 /* The following function may be found in nxge_main.c */
50 extern int nxge_m_mmac_remove(void *arg, int slot);
51 extern int nxge_m_mmac_add_g(void *arg, const uint8_t *maddr, int rdctbl,
52 	boolean_t usetbl);
53 extern int nxge_rx_ring_start(mac_ring_driver_t rdriver, uint64_t mr_gen_num);
54 
55 /* The following function may be found in nxge_[t|r]xdma.c */
56 extern npi_status_t nxge_txdma_channel_disable(nxge_t *, int);
57 extern nxge_status_t nxge_disable_rxdma_channel(nxge_t *, uint16_t);
58 
59 /*
60  * Local prototypes
61  */
62 static void nxge_grp_dc_append(nxge_t *, nxge_grp_t *, nxge_hio_dc_t *);
63 static nxge_hio_dc_t *nxge_grp_dc_unlink(nxge_t *, nxge_grp_t *, int);
64 static void nxge_grp_dc_map(nxge_grp_t *group);
65 
66 /*
67  * These functions are used by both service & guest domains to
68  * decide whether they're running in an LDOMs/XEN environment
69  * or not.  If so, then the Hybrid I/O (HIO) module is initialized.
70  */
71 
72 /*
73  * nxge_get_environs
74  *
75  *	Figure out if we are in a guest domain or not.
76  *
77  * Arguments:
78  * 	nxge
79  *
80  * Notes:
81  *
82  * Context:
83  *	Any domain
84  */
85 void
86 nxge_get_environs(
87 	nxge_t *nxge)
88 {
89 	char *string;
90 
91 	/*
92 	 * In the beginning, assume that we are running sans LDOMs/XEN.
93 	 */
94 	nxge->environs = SOLARIS_DOMAIN;
95 
96 	/*
97 	 * Are we a hybrid I/O (HIO) guest domain driver?
98 	 */
99 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, nxge->dip,
100 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
101 	    "niutype", &string)) == DDI_PROP_SUCCESS) {
102 		if (strcmp(string, "n2niu") == 0) {
103 			nxge->environs = SOLARIS_GUEST_DOMAIN;
104 			/* So we can allocate properly-aligned memory. */
105 			nxge->niu_type = N2_NIU;
106 			NXGE_DEBUG_MSG((nxge, HIO_CTL,
107 			    "Hybrid IO-capable guest domain"));
108 		}
109 		ddi_prop_free(string);
110 	}
111 }
112 
113 #if !defined(sun4v)
114 
115 /*
116  * nxge_hio_init
117  *
118  *	Initialize the HIO module of the NXGE driver.
119  *
120  * Arguments:
121  * 	nxge
122  *
123  * Notes:
124  *	This is the non-hybrid I/O version of this function.
125  *
126  * Context:
127  *	Any domain
128  */
129 int
130 nxge_hio_init(nxge_t *nxge)
131 {
132 	nxge_hio_data_t *nhd;
133 	int i;
134 
135 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
136 	if (nhd == NULL) {
137 		nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
138 		MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
139 		nhd->type = NXGE_HIO_TYPE_SERVICE;
140 		nxge->nxge_hw_p->hio = (uintptr_t)nhd;
141 	}
142 
143 	/*
144 	 * Initialize share and ring group structures.
145 	 */
146 	for (i = 0; i < NXGE_MAX_TDCS; i++)
147 		nxge->tdc_is_shared[i] = B_FALSE;
148 
149 	for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
150 		nxge->tx_hio_groups[i].ghandle = NULL;
151 		nxge->tx_hio_groups[i].nxgep = nxge;
152 		nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
153 		nxge->tx_hio_groups[i].gindex = 0;
154 		nxge->tx_hio_groups[i].sindex = 0;
155 	}
156 
157 	for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
158 		nxge->rx_hio_groups[i].ghandle = NULL;
159 		nxge->rx_hio_groups[i].nxgep = nxge;
160 		nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
161 		nxge->rx_hio_groups[i].gindex = 0;
162 		nxge->rx_hio_groups[i].sindex = 0;
163 		nxge->rx_hio_groups[i].started = B_FALSE;
164 		nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
165 		nxge->rx_hio_groups[i].rdctbl = -1;
166 		nxge->rx_hio_groups[i].n_mac_addrs = 0;
167 	}
168 
169 	nhd->hio.ldoms = B_FALSE;
170 
171 	return (NXGE_OK);
172 }
173 
174 #endif
175 
176 void
177 nxge_hio_uninit(nxge_t *nxge)
178 {
179 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
180 
181 	ASSERT(nxge->nxge_hw_p->ndevs == 0);
182 
183 	if (nhd != NULL) {
184 		MUTEX_DESTROY(&nhd->lock);
185 		KMEM_FREE(nhd, sizeof (*nhd));
186 		nxge->nxge_hw_p->hio = 0;
187 	}
188 }
189 
190 /*
191  * nxge_dci_map
192  *
193  *	Map a DMA channel index to a channel number.
194  *
195  * Arguments:
196  * 	instance	The instance number of the driver.
197  * 	type		The type of channel this is: Tx or Rx.
198  * 	index		The index to convert to a channel number
199  *
200  * Notes:
201  *	This function is called by nxge_ndd.c:nxge_param_set_port_rdc()
202  *
203  * Context:
204  *	Any domain
205  */
206 int
207 nxge_dci_map(
208 	nxge_t *nxge,
209 	vpc_type_t type,
210 	int index)
211 {
212 	nxge_grp_set_t *set;
213 	int dc;
214 
215 	switch (type) {
216 	case VP_BOUND_TX:
217 		set = &nxge->tx_set;
218 		break;
219 	case VP_BOUND_RX:
220 		set = &nxge->rx_set;
221 		break;
222 	}
223 
224 	for (dc = 0; dc < NXGE_MAX_TDCS; dc++) {
225 		if ((1 << dc) & set->owned.map) {
226 			if (index == 0)
227 				return (dc);
228 			else
229 				index--;
230 		}
231 	}
232 
233 	return (-1);
234 }
235 
236 /*
237  * ---------------------------------------------------------------------
238  * These are the general-purpose DMA channel group functions.  That is,
239  * these functions are used to manage groups of TDCs or RDCs in an HIO
240  * environment.
241  *
242  * But is also expected that in the future they will be able to manage
243  * Crossbow groups.
244  * ---------------------------------------------------------------------
245  */
246 
247 /*
248  * nxge_grp_cleanup(p_nxge_t nxge)
249  *
250  *	Remove all outstanding groups.
251  *
252  * Arguments:
253  *	nxge
254  */
255 void
256 nxge_grp_cleanup(p_nxge_t nxge)
257 {
258 	nxge_grp_set_t *set;
259 	int i;
260 
261 	MUTEX_ENTER(&nxge->group_lock);
262 
263 	/*
264 	 * Find RX groups that need to be cleaned up.
265 	 */
266 	set = &nxge->rx_set;
267 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
268 		if (set->group[i] != NULL) {
269 			KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
270 			set->group[i] = NULL;
271 		}
272 	}
273 
274 	/*
275 	 * Find TX groups that need to be cleaned up.
276 	 */
277 	set = &nxge->tx_set;
278 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
279 		if (set->group[i] != NULL) {
280 			KMEM_FREE(set->group[i], sizeof (nxge_grp_t));
281 			set->group[i] = NULL;
282 		}
283 	}
284 	MUTEX_EXIT(&nxge->group_lock);
285 }
286 
287 
288 /*
289  * nxge_grp_add
290  *
291  *	Add a group to an instance of NXGE.
292  *
293  * Arguments:
294  * 	nxge
295  * 	type	Tx or Rx
296  *
297  * Notes:
298  *
299  * Context:
300  *	Any domain
301  */
302 nxge_grp_t *
303 nxge_grp_add(
304 	nxge_t *nxge,
305 	nxge_grp_type_t type)
306 {
307 	nxge_grp_set_t *set;
308 	nxge_grp_t *group;
309 	int i;
310 
311 	group = KMEM_ZALLOC(sizeof (*group), KM_SLEEP);
312 	group->nxge = nxge;
313 
314 	MUTEX_ENTER(&nxge->group_lock);
315 	switch (type) {
316 	case NXGE_TRANSMIT_GROUP:
317 	case EXT_TRANSMIT_GROUP:
318 		set = &nxge->tx_set;
319 		break;
320 	default:
321 		set = &nxge->rx_set;
322 		break;
323 	}
324 
325 	group->type = type;
326 	group->active = B_TRUE;
327 	group->sequence = set->sequence++;
328 
329 	/* Find an empty slot for this logical group. */
330 	for (i = 0; i < NXGE_LOGICAL_GROUP_MAX; i++) {
331 		if (set->group[i] == 0) {
332 			group->index = i;
333 			set->group[i] = group;
334 			NXGE_DC_SET(set->lg.map, i);
335 			set->lg.count++;
336 			break;
337 		}
338 	}
339 	MUTEX_EXIT(&nxge->group_lock);
340 
341 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
342 	    "nxge_grp_add: %cgroup = %d.%d",
343 	    type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
344 	    nxge->mac.portnum, group->sequence));
345 
346 	return (group);
347 }
348 
349 void
350 nxge_grp_remove(
351 	nxge_t *nxge,
352 	nxge_grp_t *group)	/* The group to remove. */
353 {
354 	nxge_grp_set_t *set;
355 	vpc_type_t type;
356 
357 	if (group == NULL)
358 		return;
359 
360 	MUTEX_ENTER(&nxge->group_lock);
361 	switch (group->type) {
362 	case NXGE_TRANSMIT_GROUP:
363 	case EXT_TRANSMIT_GROUP:
364 		set = &nxge->tx_set;
365 		break;
366 	default:
367 		set = &nxge->rx_set;
368 		break;
369 	}
370 
371 	if (set->group[group->index] != group) {
372 		MUTEX_EXIT(&nxge->group_lock);
373 		return;
374 	}
375 
376 	set->group[group->index] = 0;
377 	NXGE_DC_RESET(set->lg.map, group->index);
378 	set->lg.count--;
379 
380 	/* While inside the mutex, deactivate <group>. */
381 	group->active = B_FALSE;
382 
383 	MUTEX_EXIT(&nxge->group_lock);
384 
385 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
386 	    "nxge_grp_remove(%c.%d.%d) called",
387 	    group->type == NXGE_TRANSMIT_GROUP ? 't' : 'r',
388 	    nxge->mac.portnum, group->sequence));
389 
390 	/* Now, remove any DCs which are still active. */
391 	switch (group->type) {
392 	default:
393 		type = VP_BOUND_TX;
394 		break;
395 	case NXGE_RECEIVE_GROUP:
396 	case EXT_RECEIVE_GROUP:
397 		type = VP_BOUND_RX;
398 	}
399 
400 	while (group->dc) {
401 		nxge_grp_dc_remove(nxge, type, group->dc->channel);
402 	}
403 
404 	KMEM_FREE(group, sizeof (*group));
405 }
406 
407 /*
408  * nxge_grp_dc_add
409  *
410  *	Add a DMA channel to a VR/Group.
411  *
412  * Arguments:
413  * 	nxge
414  * 	channel	The channel to add.
415  * Notes:
416  *
417  * Context:
418  *	Any domain
419  */
420 /* ARGSUSED */
421 int
422 nxge_grp_dc_add(
423 	nxge_t *nxge,
424 	nxge_grp_t *group,	/* The group to add <channel> to. */
425 	vpc_type_t type,	/* Rx or Tx */
426 	int channel)		/* A physical/logical channel number */
427 {
428 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
429 	nxge_hio_dc_t *dc;
430 	nxge_grp_set_t *set;
431 	nxge_status_t status = NXGE_OK;
432 	int error = 0;
433 
434 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_add"));
435 
436 	if (group == 0)
437 		return (0);
438 
439 	switch (type) {
440 	case VP_BOUND_TX:
441 		set = &nxge->tx_set;
442 		if (channel > NXGE_MAX_TDCS) {
443 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
444 			    "nxge_grp_dc_add: TDC = %d", channel));
445 			return (NXGE_ERROR);
446 		}
447 		break;
448 	case VP_BOUND_RX:
449 		set = &nxge->rx_set;
450 		if (channel > NXGE_MAX_RDCS) {
451 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
452 			    "nxge_grp_dc_add: RDC = %d", channel));
453 			return (NXGE_ERROR);
454 		}
455 		break;
456 
457 	default:
458 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
459 		    "nxge_grp_dc_add: unknown type channel(%d)", channel));
460 	}
461 
462 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
463 	    "nxge_grp_dc_add: %cgroup = %d.%d.%d, channel = %d",
464 	    type == VP_BOUND_TX ? 't' : 'r',
465 	    nxge->mac.portnum, group->sequence, group->count, channel));
466 
467 	MUTEX_ENTER(&nxge->group_lock);
468 	if (group->active != B_TRUE) {
469 		/* We may be in the process of removing this group. */
470 		MUTEX_EXIT(&nxge->group_lock);
471 		return (NXGE_ERROR);
472 	}
473 	MUTEX_EXIT(&nxge->group_lock);
474 
475 	if (!(dc = nxge_grp_dc_find(nxge, type, channel))) {
476 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
477 		    "nxge_grp_dc_add(%d): DC FIND failed", channel));
478 		return (NXGE_ERROR);
479 	}
480 
481 	MUTEX_ENTER(&nhd->lock);
482 
483 	if (dc->group) {
484 		MUTEX_EXIT(&nhd->lock);
485 		/* This channel is already in use! */
486 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
487 		    "nxge_grp_dc_add(%d): channel already in group", channel));
488 		return (NXGE_ERROR);
489 	}
490 
491 	dc->next = 0;
492 	dc->page = channel;
493 	dc->channel = (nxge_channel_t)channel;
494 
495 	dc->type = type;
496 	if (type == VP_BOUND_RX) {
497 		dc->init = nxge_init_rxdma_channel;
498 		dc->uninit = nxge_uninit_rxdma_channel;
499 	} else {
500 		dc->init = nxge_init_txdma_channel;
501 		dc->uninit = nxge_uninit_txdma_channel;
502 	}
503 
504 	dc->group = group;
505 
506 	if (isLDOMguest(nxge)) {
507 		error = nxge_hio_ldsv_add(nxge, dc);
508 		if (error != 0) {
509 			MUTEX_EXIT(&nhd->lock);
510 			return (NXGE_ERROR);
511 		}
512 	}
513 
514 	NXGE_DC_SET(set->owned.map, channel);
515 	set->owned.count++;
516 
517 	MUTEX_EXIT(&nhd->lock);
518 
519 	if ((status = (*dc->init)(nxge, channel)) != NXGE_OK) {
520 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
521 		    "nxge_grp_dc_add(%d): channel init failed", channel));
522 		MUTEX_ENTER(&nhd->lock);
523 		(void) memset(dc, 0, sizeof (*dc));
524 		NXGE_DC_RESET(set->owned.map, channel);
525 		set->owned.count--;
526 		MUTEX_EXIT(&nhd->lock);
527 		return (NXGE_ERROR);
528 	}
529 
530 	nxge_grp_dc_append(nxge, group, dc);
531 
532 	if (type == VP_BOUND_TX) {
533 		MUTEX_ENTER(&nhd->lock);
534 		nxge->tdc_is_shared[channel] = B_FALSE;
535 		MUTEX_EXIT(&nhd->lock);
536 	}
537 
538 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_add"));
539 
540 	return ((int)status);
541 }
542 
543 void
544 nxge_grp_dc_remove(
545 	nxge_t *nxge,
546 	vpc_type_t type,
547 	int channel)
548 {
549 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
550 	nxge_hio_dc_t *dc;
551 	nxge_grp_set_t *set;
552 	nxge_grp_t *group;
553 
554 	dc_uninit_t uninit;
555 
556 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_grp_dc_remove"));
557 
558 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0)
559 		goto nxge_grp_dc_remove_exit;
560 
561 	if ((dc->group == NULL) && (dc->next == 0) &&
562 	    (dc->channel == 0) && (dc->page == 0) && (dc->type == 0)) {
563 		goto nxge_grp_dc_remove_exit;
564 	}
565 
566 	group = (nxge_grp_t *)dc->group;
567 
568 	if (isLDOMguest(nxge)) {
569 		(void) nxge_hio_intr_remove(nxge, type, channel);
570 	}
571 
572 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
573 	    "DC remove: group = %d.%d.%d, %cdc %d",
574 	    nxge->mac.portnum, group->sequence, group->count,
575 	    type == VP_BOUND_TX ? 't' : 'r', dc->channel));
576 
577 	MUTEX_ENTER(&nhd->lock);
578 
579 	set = dc->type == VP_BOUND_TX ? &nxge->tx_set : &nxge->rx_set;
580 
581 	/* Remove the DC from its group. */
582 	if (nxge_grp_dc_unlink(nxge, group, channel) != dc) {
583 		MUTEX_EXIT(&nhd->lock);
584 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
585 		    "nxge_grp_dc_remove(%d) failed", channel));
586 		goto nxge_grp_dc_remove_exit;
587 	}
588 
589 	uninit = dc->uninit;
590 	channel = dc->channel;
591 
592 	NXGE_DC_RESET(set->owned.map, channel);
593 	set->owned.count--;
594 
595 	(void) memset(dc, 0, sizeof (*dc));
596 
597 	MUTEX_EXIT(&nhd->lock);
598 
599 	(*uninit)(nxge, channel);
600 
601 nxge_grp_dc_remove_exit:
602 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_grp_dc_remove"));
603 }
604 
605 nxge_hio_dc_t *
606 nxge_grp_dc_find(
607 	nxge_t *nxge,
608 	vpc_type_t type,	/* Rx or Tx */
609 	int channel)
610 {
611 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
612 	nxge_hio_dc_t *current;
613 
614 	current = (type == VP_BOUND_TX) ? &nhd->tdc[0] : &nhd->rdc[0];
615 
616 	if (!isLDOMguest(nxge)) {
617 		return (&current[channel]);
618 	} else {
619 		/* We're in a guest domain. */
620 		int i, limit = (type == VP_BOUND_TX) ?
621 		    NXGE_MAX_TDCS : NXGE_MAX_RDCS;
622 
623 		MUTEX_ENTER(&nhd->lock);
624 		for (i = 0; i < limit; i++, current++) {
625 			if (current->channel == channel) {
626 				if (current->vr && current->vr->nxge ==
627 				    (uintptr_t)nxge) {
628 					MUTEX_EXIT(&nhd->lock);
629 					return (current);
630 				}
631 			}
632 		}
633 		MUTEX_EXIT(&nhd->lock);
634 	}
635 
636 	return (0);
637 }
638 
639 /*
640  * nxge_grp_dc_append
641  *
642  *	Append a DMA channel to a group.
643  *
644  * Arguments:
645  * 	nxge
646  * 	group	The group to append to
647  * 	dc	The DMA channel to append
648  *
649  * Notes:
650  *
651  * Context:
652  *	Any domain
653  */
654 static
655 void
656 nxge_grp_dc_append(
657 	nxge_t *nxge,
658 	nxge_grp_t *group,
659 	nxge_hio_dc_t *dc)
660 {
661 	MUTEX_ENTER(&nxge->group_lock);
662 
663 	if (group->dc == 0) {
664 		group->dc = dc;
665 	} else {
666 		nxge_hio_dc_t *current = group->dc;
667 		do {
668 			if (current->next == 0) {
669 				current->next = dc;
670 				break;
671 			}
672 			current = current->next;
673 		} while (current);
674 	}
675 
676 	NXGE_DC_SET(group->map, dc->channel);
677 
678 	nxge_grp_dc_map(group);
679 	group->count++;
680 
681 	MUTEX_EXIT(&nxge->group_lock);
682 }
683 
684 /*
685  * nxge_grp_dc_unlink
686  *
687  *	Unlink a DMA channel fromits linked list (group).
688  *
689  * Arguments:
690  * 	nxge
691  * 	group	The group (linked list) to unlink from
692  * 	dc	The DMA channel to append
693  *
694  * Notes:
695  *
696  * Context:
697  *	Any domain
698  */
699 nxge_hio_dc_t *
700 nxge_grp_dc_unlink(
701 	nxge_t *nxge,
702 	nxge_grp_t *group,
703 	int channel)
704 {
705 	nxge_hio_dc_t *current, *previous;
706 
707 	MUTEX_ENTER(&nxge->group_lock);
708 
709 	if (group == NULL) {
710 		MUTEX_EXIT(&nxge->group_lock);
711 		return (0);
712 	}
713 
714 	if ((current = group->dc) == 0) {
715 		MUTEX_EXIT(&nxge->group_lock);
716 		return (0);
717 	}
718 
719 	previous = 0;
720 	do {
721 		if (current->channel == channel) {
722 			if (previous)
723 				previous->next = current->next;
724 			else
725 				group->dc = current->next;
726 			break;
727 		}
728 		previous = current;
729 		current = current->next;
730 	} while (current);
731 
732 	if (current == 0) {
733 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
734 		    "DC unlink: DC %d not found", channel));
735 	} else {
736 		current->next = 0;
737 		current->group = 0;
738 
739 		NXGE_DC_RESET(group->map, channel);
740 		group->count--;
741 	}
742 
743 	nxge_grp_dc_map(group);
744 
745 	MUTEX_EXIT(&nxge->group_lock);
746 
747 	return (current);
748 }
749 
750 /*
751  * nxge_grp_dc_map
752  *
753  *	Map a linked list to an array of channel numbers.
754  *
755  * Arguments:
756  * 	nxge
757  * 	group	The group to remap.
758  *
759  * Notes:
760  *	It is expected that the caller will hold the correct mutex.
761  *
762  * Context:
763  *	Service domain
764  */
765 void
766 nxge_grp_dc_map(
767 	nxge_grp_t *group)
768 {
769 	nxge_channel_t *legend;
770 	nxge_hio_dc_t *dc;
771 
772 	(void) memset(group->legend, 0, sizeof (group->legend));
773 
774 	legend = group->legend;
775 	dc = group->dc;
776 	while (dc) {
777 		*legend = dc->channel;
778 		legend++;
779 		dc = dc->next;
780 	}
781 }
782 
783 /*
784  * ---------------------------------------------------------------------
785  * These are HIO debugging functions.
786  * ---------------------------------------------------------------------
787  */
788 
789 /*
790  * nxge_delay
791  *
792  *	Delay <seconds> number of seconds.
793  *
794  * Arguments:
795  * 	nxge
796  * 	group	The group to append to
797  * 	dc	The DMA channel to append
798  *
799  * Notes:
800  *	This is a developer-only function.
801  *
802  * Context:
803  *	Any domain
804  */
805 void
806 nxge_delay(
807 	int seconds)
808 {
809 	delay(drv_usectohz(seconds * 1000000));
810 }
811 
812 static dmc_reg_name_t rx_names[] = {
813 	{ "RXDMA_CFIG1",	0 },
814 	{ "RXDMA_CFIG2",	8 },
815 	{ "RBR_CFIG_A",		0x10 },
816 	{ "RBR_CFIG_B",		0x18 },
817 	{ "RBR_KICK",		0x20 },
818 	{ "RBR_STAT",		0x28 },
819 	{ "RBR_HDH",		0x30 },
820 	{ "RBR_HDL",		0x38 },
821 	{ "RCRCFIG_A",		0x40 },
822 	{ "RCRCFIG_B",		0x48 },
823 	{ "RCRSTAT_A",		0x50 },
824 	{ "RCRSTAT_B",		0x58 },
825 	{ "RCRSTAT_C",		0x60 },
826 	{ "RX_DMA_ENT_MSK",	0x68 },
827 	{ "RX_DMA_CTL_STAT",	0x70 },
828 	{ "RCR_FLSH",		0x78 },
829 	{ "RXMISC",		0x90 },
830 	{ "RX_DMA_CTL_STAT_DBG", 0x98 },
831 	{ 0, -1 }
832 };
833 
834 static dmc_reg_name_t tx_names[] = {
835 	{ "Tx_RNG_CFIG",	0 },
836 	{ "Tx_RNG_HDL",		0x10 },
837 	{ "Tx_RNG_KICK",	0x18 },
838 	{ "Tx_ENT_MASK",	0x20 },
839 	{ "Tx_CS",		0x28 },
840 	{ "TxDMA_MBH",		0x30 },
841 	{ "TxDMA_MBL",		0x38 },
842 	{ "TxDMA_PRE_ST",	0x40 },
843 	{ "Tx_RNG_ERR_LOGH",	0x48 },
844 	{ "Tx_RNG_ERR_LOGL",	0x50 },
845 	{ "TDMC_INTR_DBG",	0x60 },
846 	{ "Tx_CS_DBG",		0x68 },
847 	{ 0, -1 }
848 };
849 
850 /*
851  * nxge_xx2str
852  *
853  *	Translate a register address into a string.
854  *
855  * Arguments:
856  * 	offset	The address of the register to translate.
857  *
858  * Notes:
859  *	These are developer-only function.
860  *
861  * Context:
862  *	Any domain
863  */
864 const char *
865 nxge_rx2str(
866 	int offset)
867 {
868 	dmc_reg_name_t *reg = &rx_names[0];
869 
870 	offset &= DMA_CSR_MASK;
871 
872 	while (reg->name) {
873 		if (offset == reg->offset)
874 			return (reg->name);
875 		reg++;
876 	}
877 
878 	return (0);
879 }
880 
881 const char *
882 nxge_tx2str(
883 	int offset)
884 {
885 	dmc_reg_name_t *reg = &tx_names[0];
886 
887 	offset &= DMA_CSR_MASK;
888 
889 	while (reg->name) {
890 		if (offset == reg->offset)
891 			return (reg->name);
892 		reg++;
893 	}
894 
895 	return (0);
896 }
897 
898 /*
899  * nxge_ddi_perror
900  *
901  *	Map a DDI error number to a string.
902  *
903  * Arguments:
904  * 	ddi_error	The DDI error number to map.
905  *
906  * Notes:
907  *
908  * Context:
909  *	Any domain
910  */
911 const char *
912 nxge_ddi_perror(
913 	int ddi_error)
914 {
915 	switch (ddi_error) {
916 	case DDI_SUCCESS:
917 		return ("DDI_SUCCESS");
918 	case DDI_FAILURE:
919 		return ("DDI_FAILURE");
920 	case DDI_NOT_WELL_FORMED:
921 		return ("DDI_NOT_WELL_FORMED");
922 	case DDI_EAGAIN:
923 		return ("DDI_EAGAIN");
924 	case DDI_EINVAL:
925 		return ("DDI_EINVAL");
926 	case DDI_ENOTSUP:
927 		return ("DDI_ENOTSUP");
928 	case DDI_EPENDING:
929 		return ("DDI_EPENDING");
930 	case DDI_ENOMEM:
931 		return ("DDI_ENOMEM");
932 	case DDI_EBUSY:
933 		return ("DDI_EBUSY");
934 	case DDI_ETRANSPORT:
935 		return ("DDI_ETRANSPORT");
936 	case DDI_ECONTEXT:
937 		return ("DDI_ECONTEXT");
938 	default:
939 		return ("Unknown error");
940 	}
941 }
942 
943 /*
944  * ---------------------------------------------------------------------
945  * These are Sun4v HIO function definitions
946  * ---------------------------------------------------------------------
947  */
948 
949 #if defined(sun4v)
950 
951 /*
952  * Local prototypes
953  */
954 static nxge_hio_vr_t *nxge_hio_vr_share(nxge_t *);
955 static void nxge_hio_unshare(nxge_hio_vr_t *);
956 
957 static int nxge_hio_addres(nxge_hio_vr_t *, mac_ring_type_t, uint64_t *);
958 static void nxge_hio_remres(nxge_hio_vr_t *, mac_ring_type_t, res_map_t);
959 
960 static void nxge_hio_tdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
961 static void nxge_hio_rdc_unshare(nxge_t *nxge, int dev_grpid, int channel);
962 static int nxge_hio_dc_share(nxge_t *, nxge_hio_vr_t *, mac_ring_type_t, int);
963 static void nxge_hio_dc_unshare(nxge_t *, nxge_hio_vr_t *,
964     mac_ring_type_t, int);
965 
966 /*
967  * nxge_hio_init
968  *
969  *	Initialize the HIO module of the NXGE driver.
970  *
971  * Arguments:
972  * 	nxge
973  *
974  * Notes:
975  *
976  * Context:
977  *	Any domain
978  */
979 int
980 nxge_hio_init(nxge_t *nxge)
981 {
982 	nxge_hio_data_t *nhd;
983 	int i, region;
984 
985 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
986 	if (nhd == 0) {
987 		nhd = KMEM_ZALLOC(sizeof (*nhd), KM_SLEEP);
988 		MUTEX_INIT(&nhd->lock, NULL, MUTEX_DRIVER, NULL);
989 		if (isLDOMguest(nxge))
990 			nhd->type = NXGE_HIO_TYPE_GUEST;
991 		else
992 			nhd->type = NXGE_HIO_TYPE_SERVICE;
993 		nxge->nxge_hw_p->hio = (uintptr_t)nhd;
994 	}
995 
996 	if ((nxge->environs == SOLARIS_DOMAIN) &&
997 	    (nxge->niu_type == N2_NIU)) {
998 		if (nxge->niu_hsvc_available == B_TRUE) {
999 			hsvc_info_t *niu_hsvc = &nxge->niu_hsvc;
1000 			/*
1001 			 * Versions supported now are:
1002 			 *  - major number >= 1 (NIU_MAJOR_VER).
1003 			 */
1004 			if ((niu_hsvc->hsvc_major >= NIU_MAJOR_VER) ||
1005 			    (niu_hsvc->hsvc_major == 1 &&
1006 			    niu_hsvc->hsvc_minor == 1)) {
1007 				nxge->environs = SOLARIS_SERVICE_DOMAIN;
1008 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1009 				    "nxge_hio_init: hypervisor services "
1010 				    "version %d.%d",
1011 				    niu_hsvc->hsvc_major,
1012 				    niu_hsvc->hsvc_minor));
1013 			}
1014 		}
1015 	}
1016 
1017 	/*
1018 	 * Initialize share and ring group structures.
1019 	 */
1020 	for (i = 0; i < NXGE_MAX_TDC_GROUPS; i++) {
1021 		nxge->tx_hio_groups[i].ghandle = NULL;
1022 		nxge->tx_hio_groups[i].nxgep = nxge;
1023 		nxge->tx_hio_groups[i].type = MAC_RING_TYPE_TX;
1024 		nxge->tx_hio_groups[i].gindex = 0;
1025 		nxge->tx_hio_groups[i].sindex = 0;
1026 	}
1027 
1028 	for (i = 0; i < NXGE_MAX_RDC_GROUPS; i++) {
1029 		nxge->rx_hio_groups[i].ghandle = NULL;
1030 		nxge->rx_hio_groups[i].nxgep = nxge;
1031 		nxge->rx_hio_groups[i].type = MAC_RING_TYPE_RX;
1032 		nxge->rx_hio_groups[i].gindex = 0;
1033 		nxge->rx_hio_groups[i].sindex = 0;
1034 		nxge->rx_hio_groups[i].started = B_FALSE;
1035 		nxge->rx_hio_groups[i].port_default_grp = B_FALSE;
1036 		nxge->rx_hio_groups[i].rdctbl = -1;
1037 		nxge->rx_hio_groups[i].n_mac_addrs = 0;
1038 	}
1039 
1040 	if (!isLDOMs(nxge)) {
1041 		nhd->hio.ldoms = B_FALSE;
1042 		return (NXGE_OK);
1043 	}
1044 
1045 	nhd->hio.ldoms = B_TRUE;
1046 
1047 	/*
1048 	 * Fill in what we can.
1049 	 */
1050 	for (region = 0; region < NXGE_VR_SR_MAX; region++) {
1051 		nhd->vr[region].region = region;
1052 	}
1053 	nhd->vrs = NXGE_VR_SR_MAX - 2;
1054 
1055 	/*
1056 	 * Initialize the share stuctures.
1057 	 */
1058 	for (i = 0; i < NXGE_MAX_TDCS; i++)
1059 		nxge->tdc_is_shared[i] = B_FALSE;
1060 
1061 	for (i = 0; i < NXGE_VR_SR_MAX; i++) {
1062 		nxge->shares[i].nxgep = nxge;
1063 		nxge->shares[i].index = 0;
1064 		nxge->shares[i].vrp = NULL;
1065 		nxge->shares[i].tmap = 0;
1066 		nxge->shares[i].rmap = 0;
1067 		nxge->shares[i].rxgroup = 0;
1068 		nxge->shares[i].active = B_FALSE;
1069 	}
1070 
1071 	/* Fill in the HV HIO function pointers. */
1072 	nxge_hio_hv_init(nxge);
1073 
1074 	if (isLDOMservice(nxge)) {
1075 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
1076 		    "Hybrid IO-capable service domain"));
1077 		return (NXGE_OK);
1078 	}
1079 
1080 	return (0);
1081 }
1082 #endif /* defined(sun4v) */
1083 
1084 static int
1085 nxge_hio_group_mac_add(nxge_t *nxge, nxge_ring_group_t *g,
1086     const uint8_t *macaddr)
1087 {
1088 	int rv;
1089 	nxge_rdc_grp_t *group;
1090 
1091 	mutex_enter(nxge->genlock);
1092 
1093 	/*
1094 	 * Initialize the NXGE RDC table data structure.
1095 	 */
1096 	group = &nxge->pt_config.rdc_grps[g->rdctbl];
1097 	if (!group->flag) {
1098 		group->port = NXGE_GET_PORT_NUM(nxge->function_num);
1099 		group->config_method = RDC_TABLE_ENTRY_METHOD_REP;
1100 		group->flag = B_TRUE;	/* This group has been configured. */
1101 	}
1102 
1103 	mutex_exit(nxge->genlock);
1104 
1105 	/*
1106 	 * Add the MAC address.
1107 	 */
1108 	if ((rv = nxge_m_mmac_add_g((void *)nxge, macaddr,
1109 	    g->rdctbl, B_TRUE)) != 0) {
1110 		return (rv);
1111 	}
1112 
1113 	mutex_enter(nxge->genlock);
1114 	g->n_mac_addrs++;
1115 	mutex_exit(nxge->genlock);
1116 	return (0);
1117 }
1118 
1119 static int
1120 nxge_hio_set_unicst(void *arg, const uint8_t *macaddr)
1121 {
1122 	p_nxge_t		nxgep = (p_nxge_t)arg;
1123 	struct ether_addr	addrp;
1124 
1125 	bcopy(macaddr, (uint8_t *)&addrp, ETHERADDRL);
1126 	if (nxge_set_mac_addr(nxgep, &addrp)) {
1127 		NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
1128 		    "<== nxge_m_unicst: set unitcast failed"));
1129 		return (EINVAL);
1130 	}
1131 
1132 	nxgep->primary = B_TRUE;
1133 
1134 	return (0);
1135 }
1136 
1137 /*ARGSUSED*/
1138 static int
1139 nxge_hio_clear_unicst(p_nxge_t nxgep, const uint8_t *mac_addr)
1140 {
1141 	nxgep->primary = B_FALSE;
1142 	return (0);
1143 }
1144 
1145 static int
1146 nxge_hio_add_mac(void *arg, const uint8_t *mac_addr)
1147 {
1148 	nxge_ring_group_t	*group = (nxge_ring_group_t *)arg;
1149 	p_nxge_t		nxge = group->nxgep;
1150 	int			rv;
1151 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1152 
1153 	ASSERT(group->type == MAC_RING_TYPE_RX);
1154 	ASSERT(group->nxgep != NULL);
1155 
1156 	if (isLDOMguest(group->nxgep))
1157 		return (0);
1158 
1159 	mutex_enter(nxge->genlock);
1160 
1161 	if (!nxge->primary && group->port_default_grp) {
1162 		rv = nxge_hio_set_unicst((void *)nxge, mac_addr);
1163 		mutex_exit(nxge->genlock);
1164 		return (rv);
1165 	}
1166 
1167 	/*
1168 	 * If the group is associated with a VR, then only one
1169 	 * address may be assigned to the group.
1170 	 */
1171 	vr = (nxge_hio_vr_t *)nxge->shares[group->sindex].vrp;
1172 	if ((vr != NULL) && (group->n_mac_addrs)) {
1173 		mutex_exit(nxge->genlock);
1174 		return (ENOSPC);
1175 	}
1176 
1177 	mutex_exit(nxge->genlock);
1178 
1179 	/*
1180 	 * Program the mac address for the group.
1181 	 */
1182 	if ((rv = nxge_hio_group_mac_add(nxge, group, mac_addr)) != 0) {
1183 		return (rv);
1184 	}
1185 
1186 	return (0);
1187 }
1188 
1189 static int
1190 find_mac_slot(nxge_mmac_t *mmac_info, const uint8_t *mac_addr)
1191 {
1192 	int i;
1193 	for (i = 0; i <= mmac_info->num_mmac; i++) {
1194 		if (memcmp(mmac_info->mac_pool[i].addr, mac_addr,
1195 		    ETHERADDRL) == 0) {
1196 			return (i);
1197 		}
1198 	}
1199 	return (-1);
1200 }
1201 
1202 /* ARGSUSED */
1203 static int
1204 nxge_hio_rem_mac(void *arg, const uint8_t *mac_addr)
1205 {
1206 	nxge_ring_group_t *group = (nxge_ring_group_t *)arg;
1207 	struct ether_addr addrp;
1208 	p_nxge_t nxge = group->nxgep;
1209 	nxge_mmac_t *mmac_info;
1210 	int rv, slot;
1211 
1212 	ASSERT(group->type == MAC_RING_TYPE_RX);
1213 	ASSERT(group->nxgep != NULL);
1214 
1215 	if (isLDOMguest(group->nxgep))
1216 		return (0);
1217 
1218 	mutex_enter(nxge->genlock);
1219 
1220 	mmac_info = &nxge->nxge_mmac_info;
1221 	slot = find_mac_slot(mmac_info, mac_addr);
1222 	if (slot < 0) {
1223 		if (group->port_default_grp && nxge->primary) {
1224 			bcopy(mac_addr, (uint8_t *)&addrp, ETHERADDRL);
1225 			if (ether_cmp(&addrp, &nxge->ouraddr) == 0) {
1226 				rv = nxge_hio_clear_unicst(nxge, mac_addr);
1227 				mutex_exit(nxge->genlock);
1228 				return (rv);
1229 			} else {
1230 				mutex_exit(nxge->genlock);
1231 				return (EINVAL);
1232 			}
1233 		} else {
1234 			mutex_exit(nxge->genlock);
1235 			return (EINVAL);
1236 		}
1237 	}
1238 
1239 	mutex_exit(nxge->genlock);
1240 
1241 	/*
1242 	 * Remove the mac address for the group
1243 	 */
1244 	if ((rv = nxge_m_mmac_remove(nxge, slot)) != 0) {
1245 		return (rv);
1246 	}
1247 
1248 	mutex_enter(nxge->genlock);
1249 	group->n_mac_addrs--;
1250 	mutex_exit(nxge->genlock);
1251 
1252 	return (0);
1253 }
1254 
1255 static int
1256 nxge_hio_group_start(mac_group_driver_t gdriver)
1257 {
1258 	nxge_ring_group_t	*group = (nxge_ring_group_t *)gdriver;
1259 	nxge_rdc_grp_t		*rdc_grp_p;
1260 	int			rdctbl;
1261 	int			dev_gindex;
1262 
1263 	ASSERT(group->type == MAC_RING_TYPE_RX);
1264 	ASSERT(group->nxgep != NULL);
1265 
1266 	ASSERT(group->nxgep->nxge_mac_state == NXGE_MAC_STARTED);
1267 	if (group->nxgep->nxge_mac_state != NXGE_MAC_STARTED)
1268 		return (ENXIO);
1269 
1270 	mutex_enter(group->nxgep->genlock);
1271 	if (isLDOMguest(group->nxgep))
1272 		goto nxge_hio_group_start_exit;
1273 
1274 	dev_gindex = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1275 	    group->gindex;
1276 	rdc_grp_p = &group->nxgep->pt_config.rdc_grps[dev_gindex];
1277 
1278 	/*
1279 	 * Get an rdc table for this group.
1280 	 * Group ID is given by the caller, and that's the group it needs
1281 	 * to bind to.  The default group is already bound when the driver
1282 	 * was attached.
1283 	 *
1284 	 * For Group 0, it's RDC table was allocated at attach time
1285 	 * no need to allocate a new table.
1286 	 */
1287 	if (group->gindex != 0) {
1288 		rdctbl = nxge_fzc_rdc_tbl_bind(group->nxgep,
1289 		    dev_gindex, B_TRUE);
1290 		if (rdctbl < 0) {
1291 			mutex_exit(group->nxgep->genlock);
1292 			return (rdctbl);
1293 		}
1294 	} else {
1295 		rdctbl = group->nxgep->pt_config.hw_config.def_mac_rxdma_grpid;
1296 	}
1297 
1298 	group->rdctbl = rdctbl;
1299 
1300 	(void) nxge_init_fzc_rdc_tbl(group->nxgep, rdc_grp_p, rdctbl);
1301 
1302 nxge_hio_group_start_exit:
1303 	group->started = B_TRUE;
1304 	mutex_exit(group->nxgep->genlock);
1305 	return (0);
1306 }
1307 
1308 static void
1309 nxge_hio_group_stop(mac_group_driver_t gdriver)
1310 {
1311 	nxge_ring_group_t *group = (nxge_ring_group_t *)gdriver;
1312 
1313 	ASSERT(group->type == MAC_RING_TYPE_RX);
1314 
1315 	mutex_enter(group->nxgep->genlock);
1316 	group->started = B_FALSE;
1317 
1318 	if (isLDOMguest(group->nxgep))
1319 		goto nxge_hio_group_stop_exit;
1320 
1321 	/*
1322 	 * Unbind the RDC table previously bound for this group.
1323 	 *
1324 	 * Since RDC table for group 0 was allocated at attach
1325 	 * time, no need to unbind the table here.
1326 	 */
1327 	if (group->gindex != 0)
1328 		(void) nxge_fzc_rdc_tbl_unbind(group->nxgep, group->rdctbl);
1329 
1330 nxge_hio_group_stop_exit:
1331 	mutex_exit(group->nxgep->genlock);
1332 }
1333 
1334 /* ARGSUSED */
1335 void
1336 nxge_hio_group_get(void *arg, mac_ring_type_t type, int groupid,
1337 	mac_group_info_t *infop, mac_group_handle_t ghdl)
1338 {
1339 	p_nxge_t		nxgep = (p_nxge_t)arg;
1340 	nxge_ring_group_t	*group;
1341 	int			dev_gindex;
1342 
1343 	switch (type) {
1344 	case MAC_RING_TYPE_RX:
1345 		group = &nxgep->rx_hio_groups[groupid];
1346 		group->nxgep = nxgep;
1347 		group->ghandle = ghdl;
1348 		group->gindex = groupid;
1349 		group->sindex = 0;	/* not yet bound to a share */
1350 
1351 		if (!isLDOMguest(nxgep)) {
1352 			dev_gindex =
1353 			    nxgep->pt_config.hw_config.def_mac_rxdma_grpid +
1354 			    groupid;
1355 
1356 			if (nxgep->pt_config.hw_config.def_mac_rxdma_grpid ==
1357 			    dev_gindex)
1358 				group->port_default_grp = B_TRUE;
1359 
1360 			infop->mgi_count =
1361 			    nxgep->pt_config.rdc_grps[dev_gindex].max_rdcs;
1362 		} else {
1363 			infop->mgi_count = NXGE_HIO_SHARE_MAX_CHANNELS;
1364 		}
1365 
1366 		infop->mgi_driver = (mac_group_driver_t)group;
1367 		infop->mgi_start = nxge_hio_group_start;
1368 		infop->mgi_stop = nxge_hio_group_stop;
1369 		infop->mgi_addmac = nxge_hio_add_mac;
1370 		infop->mgi_remmac = nxge_hio_rem_mac;
1371 		break;
1372 
1373 	case MAC_RING_TYPE_TX:
1374 		/*
1375 		 * 'groupid' for TX should be incremented by one since
1376 		 * the default group (groupid 0) is not known by the MAC layer
1377 		 */
1378 		group = &nxgep->tx_hio_groups[groupid + 1];
1379 		group->nxgep = nxgep;
1380 		group->ghandle = ghdl;
1381 		group->gindex = groupid + 1;
1382 		group->sindex = 0;	/* not yet bound to a share */
1383 
1384 		infop->mgi_driver = (mac_group_driver_t)group;
1385 		infop->mgi_start = NULL;
1386 		infop->mgi_stop = NULL;
1387 		infop->mgi_addmac = NULL;	/* not needed */
1388 		infop->mgi_remmac = NULL;	/* not needed */
1389 		/* no rings associated with group initially */
1390 		infop->mgi_count = 0;
1391 		break;
1392 	}
1393 }
1394 
1395 #if defined(sun4v)
1396 
1397 int
1398 nxge_hio_share_assign(
1399 	nxge_t *nxge,
1400 	uint64_t cookie,
1401 	res_map_t *tmap,
1402 	res_map_t *rmap,
1403 	nxge_hio_vr_t *vr)
1404 {
1405 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1406 	uint64_t slot, hv_rv;
1407 	nxge_hio_dc_t *dc;
1408 	nxhv_vr_fp_t *fp;
1409 	int i;
1410 	uint64_t major;
1411 
1412 	/*
1413 	 * Ask the Hypervisor to set up the VR for us
1414 	 */
1415 	fp = &nhd->hio.vr;
1416 	major = nxge->niu_hsvc.hsvc_major;
1417 	switch (major) {
1418 	case NIU_MAJOR_VER: /* 1 */
1419 		if ((hv_rv = (*fp->assign)(vr->region, cookie, &vr->cookie))) {
1420 			NXGE_ERROR_MSG((nxge, HIO_CTL,
1421 			    "nxge_hio_share_assign: major %d "
1422 			    "vr->assign() returned %d", major, hv_rv));
1423 			nxge_hio_unshare(vr);
1424 			return (-EIO);
1425 		}
1426 
1427 		break;
1428 
1429 	case NIU_MAJOR_VER_2: /* 2 */
1430 	default:
1431 		if ((hv_rv = (*fp->cfgh_assign)
1432 		    (nxge->niu_cfg_hdl, vr->region, cookie, &vr->cookie))) {
1433 			NXGE_ERROR_MSG((nxge, HIO_CTL,
1434 			    "nxge_hio_share_assign: major %d "
1435 			    "vr->assign() returned %d", major, hv_rv));
1436 			nxge_hio_unshare(vr);
1437 			return (-EIO);
1438 		}
1439 
1440 		break;
1441 	}
1442 
1443 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
1444 	    "nxge_hio_share_assign: major %d "
1445 	    "vr->assign() success", major));
1446 
1447 	/*
1448 	 * For each shared TDC, ask the HV to find us an empty slot.
1449 	 */
1450 	dc = vr->tx_group.dc;
1451 	for (i = 0; i < NXGE_MAX_TDCS; i++) {
1452 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1453 		while (dc) {
1454 			hv_rv = (*tx->assign)
1455 			    (vr->cookie, dc->channel, &slot);
1456 			if (hv_rv != 0) {
1457 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1458 				    "nxge_hio_share_assign: "
1459 				    "tx->assign(%x, %d) failed: %ld",
1460 				    vr->cookie, dc->channel, hv_rv));
1461 				return (-EIO);
1462 			}
1463 
1464 			dc->cookie = vr->cookie;
1465 			dc->page = (vp_channel_t)slot;
1466 
1467 			/* Inform the caller about the slot chosen. */
1468 			(*tmap) |= 1 << slot;
1469 
1470 			dc = dc->next;
1471 		}
1472 	}
1473 
1474 	/*
1475 	 * For each shared RDC, ask the HV to find us an empty slot.
1476 	 */
1477 	dc = vr->rx_group.dc;
1478 	for (i = 0; i < NXGE_MAX_RDCS; i++) {
1479 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1480 		while (dc) {
1481 			hv_rv = (*rx->assign)
1482 			    (vr->cookie, dc->channel, &slot);
1483 			if (hv_rv != 0) {
1484 				NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1485 				    "nxge_hio_share_assign: "
1486 				    "rx->assign(%x, %d) failed: %ld",
1487 				    vr->cookie, dc->channel, hv_rv));
1488 				return (-EIO);
1489 			}
1490 
1491 			dc->cookie = vr->cookie;
1492 			dc->page = (vp_channel_t)slot;
1493 
1494 			/* Inform the caller about the slot chosen. */
1495 			(*rmap) |= 1 << slot;
1496 
1497 			dc = dc->next;
1498 		}
1499 	}
1500 
1501 	return (0);
1502 }
1503 
1504 void
1505 nxge_hio_share_unassign(
1506 	nxge_hio_vr_t *vr)
1507 {
1508 	nxge_t *nxge = (nxge_t *)vr->nxge;
1509 	nxge_hio_data_t *nhd;
1510 	nxge_hio_dc_t *dc;
1511 	nxhv_vr_fp_t *fp;
1512 	uint64_t hv_rv;
1513 
1514 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1515 
1516 	dc = vr->tx_group.dc;
1517 	while (dc) {
1518 		nxhv_dc_fp_t *tx = &nhd->hio.tx;
1519 		hv_rv = (*tx->unassign)(vr->cookie, dc->page);
1520 		if (hv_rv != 0) {
1521 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1522 			    "nxge_hio_share_unassign: "
1523 			    "tx->unassign(%x, %d) failed: %ld",
1524 			    vr->cookie, dc->page, hv_rv));
1525 		}
1526 		dc = dc->next;
1527 	}
1528 
1529 	dc = vr->rx_group.dc;
1530 	while (dc) {
1531 		nxhv_dc_fp_t *rx = &nhd->hio.rx;
1532 		hv_rv = (*rx->unassign)(vr->cookie, dc->page);
1533 		if (hv_rv != 0) {
1534 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1535 			    "nxge_hio_share_unassign: "
1536 			    "rx->unassign(%x, %d) failed: %ld",
1537 			    vr->cookie, dc->page, hv_rv));
1538 		}
1539 		dc = dc->next;
1540 	}
1541 
1542 	fp = &nhd->hio.vr;
1543 	if (fp->unassign) {
1544 		hv_rv = (*fp->unassign)(vr->cookie);
1545 		if (hv_rv != 0) {
1546 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1547 			    "nxge_hio_share_unassign: "
1548 			    "vr->assign(%x) failed: %ld",
1549 			    vr->cookie, hv_rv));
1550 		}
1551 	}
1552 }
1553 
1554 int
1555 nxge_hio_share_alloc(void *arg, mac_share_handle_t *shandle)
1556 {
1557 	p_nxge_t		nxge = (p_nxge_t)arg;
1558 	nxge_share_handle_t	*shp;
1559 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1560 	nxge_hio_data_t		*nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1561 
1562 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_share"));
1563 
1564 	if (nhd->hio.vr.assign == 0 || nhd->hio.tx.assign == 0 ||
1565 	    nhd->hio.rx.assign == 0) {
1566 		NXGE_ERROR_MSG((nxge, HIO_CTL, "HV assign function(s) NULL"));
1567 		return (EIO);
1568 	}
1569 
1570 	/*
1571 	 * Get a VR.
1572 	 */
1573 	if ((vr = nxge_hio_vr_share(nxge)) == 0)
1574 		return (EAGAIN);
1575 
1576 	shp = &nxge->shares[vr->region];
1577 	shp->nxgep = nxge;
1578 	shp->index = vr->region;
1579 	shp->vrp = (void *)vr;
1580 	shp->tmap = shp->rmap = 0;	/* to be assigned by ms_sbind */
1581 	shp->rxgroup = 0;		/* to be assigned by ms_sadd */
1582 	shp->active = B_FALSE;		/* not bound yet */
1583 
1584 	*shandle = (mac_share_handle_t)shp;
1585 
1586 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_share"));
1587 	return (0);
1588 }
1589 
1590 
1591 void
1592 nxge_hio_share_free(mac_share_handle_t shandle)
1593 {
1594 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1595 	nxge_hio_vr_t		*vr;
1596 
1597 	/*
1598 	 * Clear internal handle state.
1599 	 */
1600 	vr = shp->vrp;
1601 	shp->vrp = (void *)NULL;
1602 	shp->index = 0;
1603 	shp->tmap = 0;
1604 	shp->rmap = 0;
1605 	shp->rxgroup = 0;
1606 	shp->active = B_FALSE;
1607 
1608 	/*
1609 	 * Free VR resource.
1610 	 */
1611 	nxge_hio_unshare(vr);
1612 }
1613 
1614 
1615 void
1616 nxge_hio_share_query(mac_share_handle_t shandle, mac_ring_type_t type,
1617     mac_ring_handle_t *rings, uint_t *n_rings)
1618 {
1619 	nxge_t			*nxge;
1620 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1621 	nxge_ring_handle_t	*rh;
1622 	uint32_t		offset;
1623 
1624 	nxge = shp->nxgep;
1625 
1626 	switch (type) {
1627 	case MAC_RING_TYPE_RX:
1628 		rh = nxge->rx_ring_handles;
1629 		offset = nxge->pt_config.hw_config.start_rdc;
1630 		break;
1631 
1632 	case MAC_RING_TYPE_TX:
1633 		rh = nxge->tx_ring_handles;
1634 		offset = nxge->pt_config.hw_config.tdc.start;
1635 		break;
1636 	}
1637 
1638 	/*
1639 	 * In version 1.0, we may only give a VR 2 RDCs/TDCs.  Not only that,
1640 	 * but the HV has statically assigned the channels like so:
1641 	 * VR0: RDC0 & RDC1
1642 	 * VR1: RDC2 & RDC3, etc.
1643 	 * The TDCs are assigned in exactly the same way.
1644 	 */
1645 	if (rings != NULL) {
1646 		rings[0] = rh[(shp->index * 2) - offset].ring_handle;
1647 		rings[1] = rh[(shp->index * 2 + 1) - offset].ring_handle;
1648 	}
1649 	if (n_rings != NULL) {
1650 		*n_rings = 2;
1651 	}
1652 }
1653 
1654 int
1655 nxge_hio_share_add_group(mac_share_handle_t shandle,
1656     mac_group_driver_t ghandle)
1657 {
1658 	nxge_t			*nxge;
1659 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1660 	nxge_ring_group_t	*rg = (nxge_ring_group_t *)ghandle;
1661 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1662 	nxge_grp_t		*group;
1663 	int			i;
1664 
1665 	if (rg->sindex != 0) {
1666 		/* the group is already bound to a share */
1667 		return (EALREADY);
1668 	}
1669 
1670 	/*
1671 	 * If we are adding a group 0 to a share, this
1672 	 * is not correct.
1673 	 */
1674 	ASSERT(rg->gindex != 0);
1675 
1676 	nxge = rg->nxgep;
1677 	vr = shp->vrp;
1678 
1679 	switch (rg->type) {
1680 	case MAC_RING_TYPE_RX:
1681 		/*
1682 		 * Make sure that the group has the right rings associated
1683 		 * for the share. In version 1.0, we may only give a VR
1684 		 * 2 RDCs.  Not only that, but the HV has statically
1685 		 * assigned the channels like so:
1686 		 * VR0: RDC0 & RDC1
1687 		 * VR1: RDC2 & RDC3, etc.
1688 		 */
1689 		group = nxge->rx_set.group[rg->gindex];
1690 
1691 		if (group->count > 2) {
1692 			/* a share can have at most 2 rings */
1693 			return (EINVAL);
1694 		}
1695 
1696 		for (i = 0; i < NXGE_MAX_RDCS; i++) {
1697 			if (group->map & (1 << i)) {
1698 				if ((i != shp->index * 2) &&
1699 				    (i != (shp->index * 2 + 1))) {
1700 					/*
1701 					 * A group with invalid rings was
1702 					 * attempted to bind to this share
1703 					 */
1704 					return (EINVAL);
1705 				}
1706 			}
1707 		}
1708 
1709 		rg->sindex = vr->region;
1710 		vr->rdc_tbl = rg->rdctbl;
1711 		shp->rxgroup = vr->rdc_tbl;
1712 		break;
1713 
1714 	case MAC_RING_TYPE_TX:
1715 		/*
1716 		 * Make sure that the group has the right rings associated
1717 		 * for the share. In version 1.0, we may only give a VR
1718 		 * 2 TDCs.  Not only that, but the HV has statically
1719 		 * assigned the channels like so:
1720 		 * VR0: TDC0 & TDC1
1721 		 * VR1: TDC2 & TDC3, etc.
1722 		 */
1723 		group = nxge->tx_set.group[rg->gindex];
1724 
1725 		if (group->count > 2) {
1726 			/* a share can have at most 2 rings */
1727 			return (EINVAL);
1728 		}
1729 
1730 		for (i = 0; i < NXGE_MAX_TDCS; i++) {
1731 			if (group->map & (1 << i)) {
1732 				if ((i != shp->index * 2) &&
1733 				    (i != (shp->index * 2 + 1))) {
1734 					/*
1735 					 * A group with invalid rings was
1736 					 * attempted to bind to this share
1737 					 */
1738 					return (EINVAL);
1739 				}
1740 			}
1741 		}
1742 
1743 		vr->tdc_tbl = nxge->pt_config.hw_config.def_mac_txdma_grpid +
1744 		    rg->gindex;
1745 		rg->sindex = vr->region;
1746 		break;
1747 	}
1748 	return (0);
1749 }
1750 
1751 int
1752 nxge_hio_share_rem_group(mac_share_handle_t shandle,
1753     mac_group_driver_t ghandle)
1754 {
1755 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1756 	nxge_ring_group_t	*group = (nxge_ring_group_t *)ghandle;
1757 	nxge_hio_vr_t		*vr;	/* The Virtualization Region */
1758 	int			rv = 0;
1759 
1760 	vr = shp->vrp;
1761 
1762 	switch (group->type) {
1763 	case MAC_RING_TYPE_RX:
1764 		group->sindex = 0;
1765 		vr->rdc_tbl = 0;
1766 		shp->rxgroup = 0;
1767 		break;
1768 
1769 	case MAC_RING_TYPE_TX:
1770 		group->sindex = 0;
1771 		vr->tdc_tbl = 0;
1772 		break;
1773 	}
1774 
1775 	return (rv);
1776 }
1777 
1778 int
1779 nxge_hio_share_bind(mac_share_handle_t shandle, uint64_t cookie,
1780     uint64_t *rcookie)
1781 {
1782 	nxge_t			*nxge;
1783 	nxge_share_handle_t	*shp = (nxge_share_handle_t *)shandle;
1784 	nxge_hio_vr_t		*vr;
1785 	uint64_t		rmap, tmap, hv_rmap, hv_tmap;
1786 	int			rv;
1787 
1788 	ASSERT(shp != NULL);
1789 	ASSERT(shp->nxgep != NULL);
1790 	ASSERT(shp->vrp != NULL);
1791 
1792 	nxge = shp->nxgep;
1793 	vr = (nxge_hio_vr_t *)shp->vrp;
1794 
1795 	/*
1796 	 * Add resources to the share.
1797 	 * For each DMA channel associated with the VR, bind its resources
1798 	 * to the VR.
1799 	 */
1800 	tmap = 0;
1801 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_TX, &tmap);
1802 	if (rv != 0) {
1803 		return (rv);
1804 	}
1805 
1806 	rmap = 0;
1807 	rv = nxge_hio_addres(vr, MAC_RING_TYPE_RX, &rmap);
1808 	if (rv != 0) {
1809 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1810 		return (rv);
1811 	}
1812 
1813 	/*
1814 	 * Ask the Hypervisor to set up the VR and allocate slots for
1815 	 * each rings associated with the VR.
1816 	 */
1817 	hv_tmap = hv_rmap = 0;
1818 	if ((rv = nxge_hio_share_assign(nxge, cookie,
1819 	    &hv_tmap, &hv_rmap, vr))) {
1820 		nxge_hio_remres(vr, MAC_RING_TYPE_TX, tmap);
1821 		nxge_hio_remres(vr, MAC_RING_TYPE_RX, rmap);
1822 		return (rv);
1823 	}
1824 
1825 	shp->active = B_TRUE;
1826 	shp->tmap = hv_tmap;
1827 	shp->rmap = hv_rmap;
1828 
1829 	/* high 32 bits are cfg_hdl and low 32 bits are HV cookie */
1830 	*rcookie = (((uint64_t)nxge->niu_cfg_hdl) << 32) | vr->cookie;
1831 
1832 	return (0);
1833 }
1834 
1835 void
1836 nxge_hio_share_unbind(mac_share_handle_t shandle)
1837 {
1838 	nxge_share_handle_t *shp = (nxge_share_handle_t *)shandle;
1839 
1840 	/*
1841 	 * First, unassign the VR (take it back),
1842 	 * so we can enable interrupts again.
1843 	 */
1844 	nxge_hio_share_unassign(shp->vrp);
1845 
1846 	/*
1847 	 * Free Ring Resources for TX and RX
1848 	 */
1849 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_TX, shp->tmap);
1850 	nxge_hio_remres(shp->vrp, MAC_RING_TYPE_RX, shp->rmap);
1851 }
1852 
1853 
1854 /*
1855  * nxge_hio_vr_share
1856  *
1857  *	Find an unused Virtualization Region (VR).
1858  *
1859  * Arguments:
1860  * 	nxge
1861  *
1862  * Notes:
1863  *
1864  * Context:
1865  *	Service domain
1866  */
1867 nxge_hio_vr_t *
1868 nxge_hio_vr_share(
1869 	nxge_t *nxge)
1870 {
1871 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1872 	nxge_hio_vr_t *vr;
1873 
1874 	int first, limit, region;
1875 
1876 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_vr_share"));
1877 
1878 	MUTEX_ENTER(&nhd->lock);
1879 
1880 	if (nhd->vrs == 0) {
1881 		MUTEX_EXIT(&nhd->lock);
1882 		return (0);
1883 	}
1884 
1885 	/* Find an empty virtual region (VR). */
1886 	if (nxge->function_num == 0) {
1887 		// FUNC0_VIR0 'belongs' to NIU port 0.
1888 		first = FUNC0_VIR1;
1889 		limit = FUNC2_VIR0;
1890 	} else if (nxge->function_num == 1) {
1891 		// FUNC2_VIR0 'belongs' to NIU port 1.
1892 		first = FUNC2_VIR1;
1893 		limit = FUNC_VIR_MAX;
1894 	} else {
1895 		cmn_err(CE_WARN,
1896 		    "Shares not supported on function(%d) at this time.\n",
1897 		    nxge->function_num);
1898 	}
1899 
1900 	for (region = first; region < limit; region++) {
1901 		if (nhd->vr[region].nxge == 0)
1902 			break;
1903 	}
1904 
1905 	if (region == limit) {
1906 		MUTEX_EXIT(&nhd->lock);
1907 		return (0);
1908 	}
1909 
1910 	vr = &nhd->vr[region];
1911 	vr->nxge = (uintptr_t)nxge;
1912 	vr->region = (uintptr_t)region;
1913 
1914 	nhd->vrs--;
1915 
1916 	MUTEX_EXIT(&nhd->lock);
1917 
1918 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_vr_share"));
1919 
1920 	return (vr);
1921 }
1922 
1923 void
1924 nxge_hio_unshare(
1925 	nxge_hio_vr_t *vr)
1926 {
1927 	nxge_t *nxge = (nxge_t *)vr->nxge;
1928 	nxge_hio_data_t *nhd;
1929 
1930 	vr_region_t region;
1931 
1932 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_unshare"));
1933 
1934 	if (!nxge) {
1935 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_unshare: "
1936 		    "vr->nxge is NULL"));
1937 		return;
1938 	}
1939 
1940 	/*
1941 	 * This function is no longer called, but I will keep it
1942 	 * here in case we want to revisit this topic in the future.
1943 	 *
1944 	 * nxge_hio_hostinfo_uninit(nxge, vr);
1945 	 */
1946 
1947 	/*
1948 	 * XXX: This is done by ms_sremove?
1949 	 * (void) nxge_fzc_rdc_tbl_unbind(nxge, vr->rdc_tbl);
1950 	 */
1951 
1952 	nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
1953 
1954 	MUTEX_ENTER(&nhd->lock);
1955 
1956 	region = vr->region;
1957 	(void) memset(vr, 0, sizeof (*vr));
1958 	vr->region = region;
1959 
1960 	nhd->vrs++;
1961 
1962 	MUTEX_EXIT(&nhd->lock);
1963 
1964 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_unshare"));
1965 }
1966 
1967 int
1968 nxge_hio_addres(nxge_hio_vr_t *vr, mac_ring_type_t type, uint64_t *map)
1969 {
1970 	nxge_t		*nxge;
1971 	nxge_grp_t	*group;
1972 	int		groupid;
1973 	int		i, rv = 0;
1974 	int		max_dcs;
1975 
1976 	ASSERT(vr != NULL);
1977 	ASSERT(vr->nxge != NULL);
1978 	nxge = (nxge_t *)vr->nxge;
1979 
1980 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_addres"));
1981 
1982 	/*
1983 	 * For each ring associated with the group, add the resources
1984 	 * to the group and bind.
1985 	 */
1986 	max_dcs = (type == MAC_RING_TYPE_TX) ? NXGE_MAX_TDCS : NXGE_MAX_RDCS;
1987 	if (type == MAC_RING_TYPE_TX) {
1988 		/* set->group is an array of group indexed by a port group id */
1989 		groupid = vr->tdc_tbl -
1990 		    nxge->pt_config.hw_config.def_mac_txdma_grpid;
1991 		group = nxge->tx_set.group[groupid];
1992 	} else {
1993 		/* set->group is an array of group indexed by a port group id */
1994 		groupid = vr->rdc_tbl -
1995 		    nxge->pt_config.hw_config.def_mac_rxdma_grpid;
1996 		group = nxge->rx_set.group[groupid];
1997 	}
1998 
1999 	ASSERT(group != NULL);
2000 
2001 	if (group->map == 0) {
2002 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "There is no rings associated "
2003 		    "with this VR"));
2004 		return (EINVAL);
2005 	}
2006 
2007 	for (i = 0; i < max_dcs; i++) {
2008 		if (group->map & (1 << i)) {
2009 			if ((rv = nxge_hio_dc_share(nxge, vr, type, i)) < 0) {
2010 				if (*map == 0) /* Couldn't get even one DC. */
2011 					return (-rv);
2012 				else
2013 					break;
2014 			}
2015 			*map |= (1 << i);
2016 		}
2017 	}
2018 
2019 	if ((*map == 0) || (rv != 0)) {
2020 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
2021 		    "<== nxge_hio_addres: rv(%x)", rv));
2022 		return (EIO);
2023 	}
2024 
2025 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_addres"));
2026 	return (0);
2027 }
2028 
2029 /* ARGSUSED */
2030 void
2031 nxge_hio_remres(
2032 	nxge_hio_vr_t *vr,
2033 	mac_ring_type_t type,
2034 	res_map_t res_map)
2035 {
2036 	nxge_t *nxge = (nxge_t *)vr->nxge;
2037 	nxge_grp_t *group;
2038 
2039 	if (!nxge) {
2040 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
2041 		    "vr->nxge is NULL"));
2042 		return;
2043 	}
2044 
2045 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_remres(%lx)", res_map));
2046 
2047 	/*
2048 	 * For each ring bound to the group, remove the DMA resources
2049 	 * from the group and unbind.
2050 	 */
2051 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2052 	while (group->dc) {
2053 		nxge_hio_dc_t *dc = group->dc;
2054 		NXGE_DC_RESET(res_map, dc->page);
2055 		nxge_hio_dc_unshare(nxge, vr, type, dc->channel);
2056 	}
2057 
2058 	if (res_map) {
2059 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_remres: "
2060 		    "res_map %lx", res_map));
2061 	}
2062 
2063 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_remres"));
2064 }
2065 
2066 /*
2067  * nxge_hio_tdc_share
2068  *
2069  *	Share an unused TDC channel.
2070  *
2071  * Arguments:
2072  * 	nxge
2073  *
2074  * Notes:
2075  *
2076  * A.7.3 Reconfigure Tx DMA channel
2077  *	Disable TxDMA			A.9.6.10
2078  *     [Rebind TxDMA channel to Port	A.9.6.7]
2079  *
2080  * We don't have to Rebind the TDC to the port - it always already bound.
2081  *
2082  *	Soft Reset TxDMA		A.9.6.2
2083  *
2084  * This procedure will be executed by nxge_init_txdma_channel() in the
2085  * guest domain:
2086  *
2087  *	Re-initialize TxDMA		A.9.6.8
2088  *	Reconfigure TxDMA
2089  *	Enable TxDMA			A.9.6.9
2090  *
2091  * Context:
2092  *	Service domain
2093  */
2094 int
2095 nxge_hio_tdc_share(
2096 	nxge_t *nxge,
2097 	int channel)
2098 {
2099 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2100 	nxge_grp_set_t *set = &nxge->tx_set;
2101 	tx_ring_t *ring;
2102 	int count;
2103 
2104 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_share"));
2105 
2106 	/*
2107 	 * Wait until this channel is idle.
2108 	 */
2109 	ring = nxge->tx_rings->rings[channel];
2110 	ASSERT(ring != NULL);
2111 
2112 	(void) atomic_swap_32(&ring->tx_ring_offline, NXGE_TX_RING_OFFLINING);
2113 	if (ring->tx_ring_busy) {
2114 		/*
2115 		 * Wait for 30 seconds.
2116 		 */
2117 		for (count = 30 * 1000; count; count--) {
2118 			if (ring->tx_ring_offline & NXGE_TX_RING_OFFLINED) {
2119 				break;
2120 			}
2121 
2122 			drv_usecwait(1000);
2123 		}
2124 
2125 		if (count == 0) {
2126 			(void) atomic_swap_32(&ring->tx_ring_offline,
2127 			    NXGE_TX_RING_ONLINE);
2128 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2129 			    "nxge_hio_tdc_share: "
2130 			    "Tx ring %d was always BUSY", channel));
2131 			return (-EIO);
2132 		}
2133 	} else {
2134 		(void) atomic_swap_32(&ring->tx_ring_offline,
2135 		    NXGE_TX_RING_OFFLINED);
2136 	}
2137 
2138 	MUTEX_ENTER(&nhd->lock);
2139 	nxge->tdc_is_shared[channel] = B_TRUE;
2140 	MUTEX_EXIT(&nhd->lock);
2141 
2142 	if (nxge_intr_remove(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2143 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_share: "
2144 		    "Failed to remove interrupt for TxDMA channel %d",
2145 		    channel));
2146 		return (-EINVAL);
2147 	}
2148 
2149 	/* Disable TxDMA A.9.6.10 */
2150 	(void) nxge_txdma_channel_disable(nxge, channel);
2151 
2152 	/* The SD is sharing this channel. */
2153 	NXGE_DC_SET(set->shared.map, channel);
2154 	set->shared.count++;
2155 
2156 	/* Soft Reset TxDMA A.9.6.2 */
2157 	nxge_grp_dc_remove(nxge, VP_BOUND_TX, channel);
2158 
2159 	/*
2160 	 * Initialize the DC-specific FZC control registers.
2161 	 * -----------------------------------------------------
2162 	 */
2163 	if (nxge_init_fzc_tdc(nxge, channel) != NXGE_OK) {
2164 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2165 		    "nxge_hio_tdc_share: FZC TDC failed: %d", channel));
2166 		return (-EIO);
2167 	}
2168 
2169 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_share"));
2170 
2171 	return (0);
2172 }
2173 
2174 /*
2175  * nxge_hio_rdc_share
2176  *
2177  *	Share an unused RDC channel.
2178  *
2179  * Arguments:
2180  * 	nxge
2181  *
2182  * Notes:
2183  *
2184  * This is the latest version of the procedure to
2185  * Reconfigure an Rx DMA channel:
2186  *
2187  * A.6.3 Reconfigure Rx DMA channel
2188  *	Stop RxMAC		A.9.2.6
2189  *	Drain IPP Port		A.9.3.6
2190  *	Stop and reset RxDMA	A.9.5.3
2191  *
2192  * This procedure will be executed by nxge_init_rxdma_channel() in the
2193  * guest domain:
2194  *
2195  *	Initialize RxDMA	A.9.5.4
2196  *	Reconfigure RxDMA
2197  *	Enable RxDMA		A.9.5.5
2198  *
2199  * We will do this here, since the RDC is a canalis non grata:
2200  *	Enable RxMAC		A.9.2.10
2201  *
2202  * Context:
2203  *	Service domain
2204  */
2205 int
2206 nxge_hio_rdc_share(
2207 	nxge_t *nxge,
2208 	nxge_hio_vr_t *vr,
2209 	int channel)
2210 {
2211 	nxge_grp_set_t *set = &nxge->rx_set;
2212 	nxge_rdc_grp_t *rdc_grp;
2213 
2214 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_share"));
2215 
2216 	/* Disable interrupts. */
2217 	if (nxge_intr_remove(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2218 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2219 		    "Failed to remove interrupt for RxDMA channel %d",
2220 		    channel));
2221 		return (NXGE_ERROR);
2222 	}
2223 
2224 	/* Stop RxMAC = A.9.2.6 */
2225 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2226 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2227 		    "Failed to disable RxMAC"));
2228 	}
2229 
2230 	/* Drain IPP Port = A.9.3.6 */
2231 	(void) nxge_ipp_drain(nxge);
2232 
2233 	/* Stop and reset RxDMA = A.9.5.3 */
2234 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2235 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2236 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_share: "
2237 		    "Failed to disable RxDMA channel %d", channel));
2238 	}
2239 
2240 	/* The SD is sharing this channel. */
2241 	NXGE_DC_SET(set->shared.map, channel);
2242 	set->shared.count++;
2243 
2244 	// Assert RST: RXDMA_CFIG1[30] = 1
2245 	nxge_grp_dc_remove(nxge, VP_BOUND_RX, channel);
2246 
2247 	/*
2248 	 * The guest domain will reconfigure the RDC later.
2249 	 *
2250 	 * But in the meantime, we must re-enable the Rx MAC so
2251 	 * that we can start receiving packets again on the
2252 	 * remaining RDCs:
2253 	 *
2254 	 * Enable RxMAC = A.9.2.10
2255 	 */
2256 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2257 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2258 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2259 	}
2260 
2261 	/*
2262 	 * Initialize the DC-specific FZC control registers.
2263 	 * -----------------------------------------------------
2264 	 */
2265 	if (nxge_init_fzc_rdc(nxge, channel) != NXGE_OK) {
2266 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2267 		    "nxge_hio_rdc_share: RZC RDC failed: %ld", channel));
2268 		return (-EIO);
2269 	}
2270 
2271 	/*
2272 	 * Update the RDC group.
2273 	 */
2274 	rdc_grp = &nxge->pt_config.rdc_grps[vr->rdc_tbl];
2275 	NXGE_DC_SET(rdc_grp->map, channel);
2276 
2277 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_share"));
2278 
2279 	return (0);
2280 }
2281 
2282 /*
2283  * nxge_hio_dc_share
2284  *
2285  *	Share a DMA channel with a guest domain.
2286  *
2287  * Arguments:
2288  * 	nxge
2289  * 	vr	The VR that <channel> will belong to.
2290  * 	type	Tx or Rx.
2291  * 	channel	Channel to share
2292  *
2293  * Notes:
2294  *
2295  * Context:
2296  *	Service domain
2297  */
2298 int
2299 nxge_hio_dc_share(
2300 	nxge_t *nxge,
2301 	nxge_hio_vr_t *vr,
2302 	mac_ring_type_t type,
2303 	int channel)
2304 {
2305 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
2306 	nxge_hio_dc_t *dc;
2307 	nxge_grp_t *group;
2308 	int slot;
2309 
2310 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_share(%cdc %d",
2311 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2312 
2313 
2314 	/* -------------------------------------------------- */
2315 	slot = (type == MAC_RING_TYPE_TX) ?
2316 	    nxge_hio_tdc_share(nxge, channel) :
2317 	    nxge_hio_rdc_share(nxge, vr, channel);
2318 
2319 	if (slot < 0) {
2320 		if (type == MAC_RING_TYPE_RX) {
2321 			nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2322 		} else {
2323 			nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2324 		}
2325 		return (slot);
2326 	}
2327 
2328 	MUTEX_ENTER(&nhd->lock);
2329 
2330 	/*
2331 	 * Tag this channel.
2332 	 * --------------------------------------------------
2333 	 */
2334 	dc = type == MAC_RING_TYPE_TX ? &nhd->tdc[channel] : &nhd->rdc[channel];
2335 
2336 	dc->vr = vr;
2337 	dc->channel = (nxge_channel_t)channel;
2338 
2339 	MUTEX_EXIT(&nhd->lock);
2340 
2341 	/*
2342 	 * vr->[t|r]x_group is used by the service domain to
2343 	 * keep track of its shared DMA channels.
2344 	 */
2345 	MUTEX_ENTER(&nxge->group_lock);
2346 	group = (type == MAC_RING_TYPE_TX ? &vr->tx_group : &vr->rx_group);
2347 
2348 	dc->group = group;
2349 	/* Initialize <group>, if necessary */
2350 	if (group->count == 0) {
2351 		group->nxge = nxge;
2352 		group->type = (type == MAC_RING_TYPE_TX) ?
2353 		    VP_BOUND_TX : VP_BOUND_RX;
2354 		group->sequence	= nhd->sequence++;
2355 		group->active = B_TRUE;
2356 	}
2357 
2358 	MUTEX_EXIT(&nxge->group_lock);
2359 
2360 	NXGE_ERROR_MSG((nxge, HIO_CTL,
2361 	    "DC share: %cDC %d was assigned to slot %d",
2362 	    type == MAC_RING_TYPE_TX ? 'T' : 'R', channel, slot));
2363 
2364 	nxge_grp_dc_append(nxge, group, dc);
2365 
2366 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_share"));
2367 
2368 	return (0);
2369 }
2370 
2371 /*
2372  * nxge_hio_tdc_unshare
2373  *
2374  *	Unshare a TDC.
2375  *
2376  * Arguments:
2377  * 	nxge
2378  * 	channel	The channel to unshare (add again).
2379  *
2380  * Notes:
2381  *
2382  * Context:
2383  *	Service domain
2384  */
2385 void
2386 nxge_hio_tdc_unshare(
2387 	nxge_t *nxge,
2388 	int dev_grpid,
2389 	int channel)
2390 {
2391 	nxge_grp_set_t *set = &nxge->tx_set;
2392 	nxge_grp_t *group;
2393 	int grpid;
2394 
2395 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_tdc_unshare"));
2396 
2397 	NXGE_DC_RESET(set->shared.map, channel);
2398 	set->shared.count--;
2399 
2400 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_txdma_grpid;
2401 	group = set->group[grpid];
2402 
2403 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_TX, channel))) {
2404 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2405 		    "Failed to initialize TxDMA channel %d", channel));
2406 		return;
2407 	}
2408 
2409 	/* Re-add this interrupt. */
2410 	if (nxge_intr_add(nxge, VP_BOUND_TX, channel) != NXGE_OK) {
2411 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_tdc_unshare: "
2412 		    "Failed to add interrupt for TxDMA channel %d", channel));
2413 	}
2414 
2415 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_tdc_unshare"));
2416 }
2417 
2418 /*
2419  * nxge_hio_rdc_unshare
2420  *
2421  *	Unshare an RDC: add it to the SD's RDC groups (tables).
2422  *
2423  * Arguments:
2424  * 	nxge
2425  * 	channel	The channel to unshare (add again).
2426  *
2427  * Notes:
2428  *
2429  * Context:
2430  *	Service domain
2431  */
2432 void
2433 nxge_hio_rdc_unshare(
2434 	nxge_t *nxge,
2435 	int dev_grpid,
2436 	int channel)
2437 {
2438 	nxge_grp_set_t		*set = &nxge->rx_set;
2439 	nxge_grp_t		*group;
2440 	int			grpid;
2441 	int			i;
2442 
2443 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_rdc_unshare"));
2444 
2445 	/* Stop RxMAC = A.9.2.6 */
2446 	if (nxge_rx_mac_disable(nxge) != NXGE_OK) {
2447 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2448 		    "Failed to disable RxMAC"));
2449 	}
2450 
2451 	/* Drain IPP Port = A.9.3.6 */
2452 	(void) nxge_ipp_drain(nxge);
2453 
2454 	/* Stop and reset RxDMA = A.9.5.3 */
2455 	// De-assert EN: RXDMA_CFIG1[31] = 0 (DMC+00000 )
2456 	if (nxge_disable_rxdma_channel(nxge, channel) != NXGE_OK) {
2457 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2458 		    "Failed to disable RxDMA channel %d", channel));
2459 	}
2460 
2461 	NXGE_DC_RESET(set->shared.map, channel);
2462 	set->shared.count--;
2463 
2464 	grpid = dev_grpid - nxge->pt_config.hw_config.def_mac_rxdma_grpid;
2465 	group = set->group[grpid];
2466 
2467 	/*
2468 	 * Assert RST: RXDMA_CFIG1[30] = 1
2469 	 *
2470 	 * Initialize RxDMA	A.9.5.4
2471 	 * Reconfigure RxDMA
2472 	 * Enable RxDMA		A.9.5.5
2473 	 */
2474 	if ((nxge_grp_dc_add(nxge, group, VP_BOUND_RX, channel))) {
2475 		/* Be sure to re-enable the RX MAC. */
2476 		if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2477 			NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2478 			    "nxge_hio_rdc_share: Rx MAC still disabled"));
2479 		}
2480 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_hio_rdc_unshare: "
2481 		    "Failed to initialize RxDMA channel %d", channel));
2482 		return;
2483 	}
2484 
2485 	/*
2486 	 * Enable RxMAC = A.9.2.10
2487 	 */
2488 	if (nxge_rx_mac_enable(nxge) != NXGE_OK) {
2489 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2490 		    "nxge_hio_rdc_share: Rx MAC still disabled"));
2491 		return;
2492 	}
2493 
2494 	/* Re-add this interrupt. */
2495 	if (nxge_intr_add(nxge, VP_BOUND_RX, channel) != NXGE_OK) {
2496 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2497 		    "nxge_hio_rdc_unshare: Failed to add interrupt for "
2498 		    "RxDMA CHANNEL %d", channel));
2499 	}
2500 
2501 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_rdc_unshare"));
2502 
2503 	for (i = 0; i < NXGE_MAX_RDCS; i++) {
2504 		if (nxge->rx_ring_handles[i].channel == channel) {
2505 			(void) nxge_rx_ring_start(
2506 			    (mac_ring_driver_t)&nxge->rx_ring_handles[i],
2507 			    nxge->rx_ring_handles[i].ring_gen_num);
2508 		}
2509 	}
2510 }
2511 
2512 /*
2513  * nxge_hio_dc_unshare
2514  *
2515  *	Unshare (reuse) a DMA channel.
2516  *
2517  * Arguments:
2518  * 	nxge
2519  * 	vr	The VR that <channel> belongs to.
2520  * 	type	Tx or Rx.
2521  * 	channel	The DMA channel to reuse.
2522  *
2523  * Notes:
2524  *
2525  * Context:
2526  *	Service domain
2527  */
2528 void
2529 nxge_hio_dc_unshare(
2530 	nxge_t *nxge,
2531 	nxge_hio_vr_t *vr,
2532 	mac_ring_type_t type,
2533 	int channel)
2534 {
2535 	nxge_grp_t *group;
2536 	nxge_hio_dc_t *dc;
2537 
2538 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_dc_unshare(%cdc %d)",
2539 	    type == MAC_RING_TYPE_TX ? 't' : 'r', channel));
2540 
2541 	/* Unlink the channel from its group. */
2542 	/* -------------------------------------------------- */
2543 	group = (type == MAC_RING_TYPE_TX) ? &vr->tx_group : &vr->rx_group;
2544 	NXGE_DC_RESET(group->map, channel);
2545 	if ((dc = nxge_grp_dc_unlink(nxge, group, channel)) == 0) {
2546 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
2547 		    "nxge_hio_dc_unshare(%d) failed", channel));
2548 		return;
2549 	}
2550 
2551 	dc->vr = 0;
2552 	dc->cookie = 0;
2553 
2554 	if (type == MAC_RING_TYPE_RX) {
2555 		nxge_hio_rdc_unshare(nxge, vr->rdc_tbl, channel);
2556 	} else {
2557 		nxge_hio_tdc_unshare(nxge, vr->tdc_tbl, channel);
2558 	}
2559 
2560 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_dc_unshare"));
2561 }
2562 
2563 
2564 /*
2565  * nxge_hio_rxdma_bind_intr():
2566  *
2567  *	For the guest domain driver, need to bind the interrupt group
2568  *	and state to the rx_rcr_ring_t.
2569  */
2570 
2571 int
2572 nxge_hio_rxdma_bind_intr(nxge_t *nxge, rx_rcr_ring_t *ring, int channel)
2573 {
2574 	nxge_hio_dc_t	*dc;
2575 	nxge_ldgv_t	*control;
2576 	nxge_ldg_t	*group;
2577 	nxge_ldv_t	*device;
2578 
2579 	/*
2580 	 * Find the DMA channel.
2581 	 */
2582 	if (!(dc = nxge_grp_dc_find(nxge, VP_BOUND_RX, channel))) {
2583 		return (NXGE_ERROR);
2584 	}
2585 
2586 	/*
2587 	 * Get the control structure.
2588 	 */
2589 	control = nxge->ldgvp;
2590 	if (control == NULL) {
2591 		return (NXGE_ERROR);
2592 	}
2593 
2594 	group = &control->ldgp[dc->ldg.vector];
2595 	device = &control->ldvp[dc->ldg.ldsv];
2596 
2597 	MUTEX_ENTER(&ring->lock);
2598 	ring->ldgp = group;
2599 	ring->ldvp = device;
2600 	MUTEX_EXIT(&ring->lock);
2601 
2602 	return (NXGE_OK);
2603 }
2604 #endif	/* if defined(sun4v) */
2605