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