changeset 3698:e3112ae4a154

6504795 Huron NIU driver should support multiple MAC addresses
author speer
date Thu, 22 Feb 2007 15:55:32 -0800
parents 5340a4d98e0b
children 73d0ad1094f7
files usr/src/uts/sun4v/io/nxge/npi/npi_mac.c usr/src/uts/sun4v/io/nxge/nxge_fflp.c usr/src/uts/sun4v/io/nxge/nxge_kstats.c usr/src/uts/sun4v/io/nxge/nxge_mac.c usr/src/uts/sun4v/io/nxge/nxge_main.c usr/src/uts/sun4v/io/nxge/nxge_virtual.c usr/src/uts/sun4v/sys/nxge/nxge_impl.h usr/src/uts/sun4v/sys/nxge/nxge_mac_hw.h
diffstat 8 files changed, 634 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/sun4v/io/nxge/npi/npi_mac.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/npi/npi_mac.c	Thu Feb 22 15:55:32 2007 -0800
@@ -729,8 +729,8 @@
 	ASSERT(IS_PORT_NUM_VALID(portn));
 
 	if ((portn == XMAC_PORT_0) || (portn == XMAC_PORT_1)) {
-		ASSERT(addrn < XMAC_MAX_ALT_ADDR_ENTRY);
-		if (addrn >= XMAC_MAX_ALT_ADDR_ENTRY) {
+		ASSERT(addrn <= XMAC_MAX_ALT_ADDR_ENTRY);
+		if (addrn > XMAC_MAX_ALT_ADDR_ENTRY) {
 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
 					    " npi_mac_altaddr_enable"
 					    " Invalid Input: addrn <0x%x>",
@@ -771,8 +771,8 @@
 	ASSERT(IS_PORT_NUM_VALID(portn));
 
 	if ((portn == XMAC_PORT_0) || (portn == XMAC_PORT_1)) {
-		ASSERT(addrn < XMAC_MAX_ALT_ADDR_ENTRY);
-		if (addrn >= XMAC_MAX_ALT_ADDR_ENTRY) {
+		ASSERT(addrn <= XMAC_MAX_ALT_ADDR_ENTRY);
+		if (addrn > XMAC_MAX_ALT_ADDR_ENTRY) {
 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
 					" npi_mac_altaddr_disable"
 					" Invalid Input: addrn <0x%x>",
@@ -811,8 +811,8 @@
 	ASSERT((op == OP_GET) || (op == OP_SET));
 
 	if ((portn == XMAC_PORT_0) || (portn == XMAC_PORT_1)) {
-		ASSERT(entryn < XMAC_MAX_ALT_ADDR_ENTRY);
-		if (entryn >= XMAC_MAX_ALT_ADDR_ENTRY) {
+		ASSERT(entryn <= XMAC_MAX_ALT_ADDR_ENTRY);
+		if (entryn > XMAC_MAX_ALT_ADDR_ENTRY) {
 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
 					    " npi_mac_altaddr_entry"
 					    " Invalid Input: entryn <0x%x>",
@@ -842,8 +842,8 @@
 			data->w2 = val2 & 0xFFFF;
 		}
 	} else {
-		ASSERT(entryn < BMAC_MAX_ALT_ADDR_ENTRY);
-		if (entryn >= BMAC_MAX_ALT_ADDR_ENTRY) {
+		ASSERT(entryn <= BMAC_MAX_ALT_ADDR_ENTRY);
+		if (entryn > BMAC_MAX_ALT_ADDR_ENTRY) {
 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
 					    " npi_mac_altaddr_entry"
 					    " Invalid Input: entryn <0x%x>",
--- a/usr/src/uts/sun4v/io/nxge/nxge_fflp.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/nxge_fflp.c	Thu Feb 22 15:55:32 2007 -0800
@@ -457,7 +457,7 @@
 		break;
 	default:
 		NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
-			"failed Assign RDC table (invalid funcion #)"));
+			"failed Assign RDC table (invalid function #)"));
 		return (NXGE_ERROR);
 	}
 
@@ -469,12 +469,17 @@
 	return (NXGE_OK);
 }
 
+/*
+ * Initialize hostinfo registers for alternate MAC addresses and
+ * multicast MAC address.
+ */
 nxge_status_t
-nxge_multicast_mac_assign_rdc_table(p_nxge_t nxgep)
+nxge_alt_mcast_mac_assign_rdc_table(p_nxge_t nxgep)
 {
 	npi_status_t rs = NPI_SUCCESS;
 	hostinfo_t mac_rdc;
 	npi_handle_t handle;
+	int i;
 
 	handle = nxgep->npi_reg_handle;
 	mac_rdc.value = 0;
@@ -483,13 +488,23 @@
 	switch (nxgep->function_num) {
 	case 0:
 	case 1:
+		/*
+		 * Tests indicate that it is OK not to re-initialize the
+		 * hostinfo registers for the XMAC's alternate MAC
+		 * addresses. But that is necessary for BMAC (case 2
+		 * and case 3 below)
+		 */
 		rs = npi_mac_hostinfo_entry(handle, OP_SET,
 			nxgep->function_num,
 			XMAC_MULTI_HOST_INFO_ENTRY, &mac_rdc);
 		break;
 	case 2:
 	case 3:
-		rs = npi_mac_hostinfo_entry(handle, OP_SET,
+		for (i = 1; i <= BMAC_MAX_ALT_ADDR_ENTRY; i++)
+			rs |= npi_mac_hostinfo_entry(handle, OP_SET,
+			nxgep->function_num, i, &mac_rdc);
+
+		rs |= npi_mac_hostinfo_entry(handle, OP_SET,
 			nxgep->function_num,
 			BMAC_MULTI_HOST_INFO_ENTRY, &mac_rdc);
 		break;
@@ -512,8 +527,8 @@
 {
 	nxge_status_t status = NXGE_OK;
 
-	status = nxge_multicast_mac_assign_rdc_table(nxgep);
-	status = nxge_main_mac_assign_rdc_table(nxgep);
+	status = nxge_alt_mcast_mac_assign_rdc_table(nxgep);
+	status |= nxge_main_mac_assign_rdc_table(nxgep);
 	return (status);
 }
 
@@ -1899,7 +1914,7 @@
 		return (NXGE_ERROR);
 	}
 
-	status = nxge_multicast_mac_assign_rdc_table(nxgep);
+	status = nxge_alt_mcast_mac_assign_rdc_table(nxgep);
 	if (status != NXGE_OK) {
 		NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
 			"nxge_multicast_mac_assign_rdc_table failed"));
--- a/usr/src/uts/sun4v/io/nxge/nxge_kstats.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/nxge_kstats.c	Thu Feb 22 15:55:32 2007 -0800
@@ -493,8 +493,8 @@
 } nxge_mmac_stat_index_t;
 
 nxge_kstat_index_t nxge_mmac_stats[] = {
-	{MMAC_MAX_ADDR, KSTAT_DATA_CHAR, "max_mmac_addr"},
-	{MMAC_AVAIL_ADDR, KSTAT_DATA_CHAR, "avail_mmac_addr"},
+	{MMAC_MAX_ADDR, KSTAT_DATA_UINT64, "max_mmac_addr"},
+	{MMAC_AVAIL_ADDR, KSTAT_DATA_UINT64, "avail_mmac_addr"},
 	{MMAC_ADDR_POOL1, KSTAT_DATA_UINT64, "mmac_addr_1"},
 	{MMAC_ADDR_POOL2, KSTAT_DATA_UINT64, "mmac_addr_2"},
 	{MMAC_ADDR_POOL3, KSTAT_DATA_UINT64, "mmac_addr_3"},
--- a/usr/src/uts/sun4v/io/nxge/nxge_mac.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/nxge_mac.c	Thu Feb 22 15:55:32 2007 -0800
@@ -875,7 +875,7 @@
 	statsp = nxgep->statsp;
 
 	/*
-	 * Initialise the xcvr statistics.
+	 * Initialize the xcvr statistics.
 	 */
 	statsp->mac_stats.cap_autoneg = 0;
 	statsp->mac_stats.cap_100T4 = 0;
@@ -1254,7 +1254,6 @@
 	uint32_t		i;
 	uint16_t		hashtab_e;
 	p_hash_filter_t		hash_filter;
-	npi_mac_addr_t		altmac_e;
 	nxge_port_t		portt;
 	uint8_t			portn;
 	npi_handle_t		handle;
@@ -1310,15 +1309,6 @@
 			goto fail;
 		nxgep->mac.rx_iconfig = NXGE_XMAC_RX_INTRS;
 
-		altmac_e.w0 = 0;
-		altmac_e.w1 = 0;
-		altmac_e.w2 = 0;
-		for (i = 0; i < XMAC_MAX_ALT_ADDR_ENTRY; i++) {
-			if ((rs = npi_mac_altaddr_entry(handle, OP_SET, portn,
-				i, (npi_mac_addr_t *)&altmac_e)) != NPI_SUCCESS)
-				goto fail;
-		}
-
 		(void) nxge_fflp_init_hostinfo(nxgep);
 
 		xconfig = CFG_XMAC_RX_ERRCHK | CFG_XMAC_RX_CRC_CHK |
@@ -1344,15 +1334,6 @@
 							!= NPI_SUCCESS)
 			goto fail;
 	} else {
-		altmac_e.w0 = 0;
-		altmac_e.w1 = 0;
-		altmac_e.w2 = 0;
-		for (i = 0; i < BMAC_MAX_ALT_ADDR_ENTRY; i++) {
-			if ((rs = npi_mac_altaddr_entry(handle, OP_SET, portn,
-				i, (npi_mac_addr_t *)&altmac_e)) != NPI_SUCCESS)
-				goto fail;
-		}
-
 		(void) nxge_fflp_init_hostinfo(nxgep);
 
 		if (npi_bmac_rx_iconfig(nxgep->npi_handle, INIT, portn,
@@ -1754,7 +1735,7 @@
 	param_arr[param_anar_10hdx].value = 0;
 
 	/*
-	 * Initialise the xcvr statistics.
+	 * Initialize the xcvr statistics.
 	 */
 	statsp->mac_stats.cap_autoneg = bmsr.bits.auto_neg_able;
 	statsp->mac_stats.cap_100T4 = bmsr.bits.link_100T4;
--- a/usr/src/uts/sun4v/io/nxge/nxge_main.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/nxge_main.c	Thu Feb 22 15:55:32 2007 -0800
@@ -182,6 +182,15 @@
 static void nxge_m_resources(void *);
 mblk_t *nxge_m_tx(void *arg, mblk_t *);
 static nxge_status_t nxge_mac_register(p_nxge_t);
+static int nxge_altmac_set(p_nxge_t nxgep, uint8_t *mac_addr,
+	mac_addr_slot_t slot);
+static void nxge_mmac_kstat_update(p_nxge_t nxgep, mac_addr_slot_t slot,
+	boolean_t factory);
+static int nxge_m_mmac_add(void *arg, mac_multi_addr_t *maddr);
+static int nxge_m_mmac_reserve(void *arg, mac_multi_addr_t *maddr);
+static int nxge_m_mmac_remove(void *arg, mac_addr_slot_t slot);
+static int nxge_m_mmac_modify(void *arg, mac_multi_addr_t *maddr);
+static int nxge_m_mmac_get(void *arg, mac_multi_addr_t *maddr);
 
 #define	NXGE_NEPTUNE_MAGIC	0x4E584745UL
 #define	MAX_DUMP_SZ 256
@@ -239,6 +248,7 @@
 					ddi_device_acc_attr_t *,
 					ddi_dma_attr_t *);
 extern void		nxge_fm_fini(p_nxge_t);
+extern npi_status_t	npi_mac_altaddr_disable(npi_handle_t, uint8_t, uint8_t);
 
 /*
  * Count used to maintain the number of buffers being used
@@ -363,6 +373,7 @@
 	int		status = DDI_SUCCESS;
 	nxge_status_t	nxge_status = NXGE_OK;
 	uint8_t		portn;
+	nxge_mmac_t	*mmac_info;
 
 	NXGE_DEBUG_MSG((nxgep, DDI_CTL, "==> nxge_attach"));
 
@@ -455,7 +466,19 @@
 		nxgep->mac.porttype = PORT_TYPE_XMAC;
 	else
 		nxgep->mac.porttype = PORT_TYPE_BMAC;
-
+	/*
+	 * Neptune has 4 ports, the first 2 ports use XMAC (10G MAC)
+	 * internally, the rest 2 ports use BMAC (1G "Big" MAC).
+	 * The two types of MACs have different characterizations.
+	 */
+	mmac_info = &nxgep->nxge_mmac_info;
+	if (nxgep->function_num < 2) {
+		mmac_info->num_mmac = XMAC_MAX_ALT_ADDR_ENTRY;
+		mmac_info->naddrfree = XMAC_MAX_ALT_ADDR_ENTRY;
+	} else {
+		mmac_info->num_mmac = BMAC_MAX_ALT_ADDR_ENTRY;
+		mmac_info->naddrfree = BMAC_MAX_ALT_ADDR_ENTRY;
+	}
 	/*
 	 * Setup the Ndd parameters for the this instance.
 	 */
@@ -1056,7 +1079,7 @@
 
 	/*
 	 * Get the interrupt cookie so the mutexes can be
-	 * Initialised.
+	 * Initialized.
 	 */
 	ddi_status = ddi_get_iblock_cookie(nxgep->dip, 0,
 					&nxgep->interrupt_cookie);
@@ -3152,7 +3175,7 @@
 	MUTEX_ENTER(nxgep->genlock);
 
 	/*
-	 * CR 6492541 Check to see if the drv_state has been intialized,
+	 * CR 6492541 Check to see if the drv_state has been initialized,
 	 * if not * call nxge_init().
 	 */
 	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
@@ -3192,17 +3215,459 @@
 	NXGE_DEBUG_MSG((nxgep, RX_CTL, "<== nxge_m_resources"));
 }
 
-/*ARGSUSED*/
+static void
+nxge_mmac_kstat_update(p_nxge_t nxgep, mac_addr_slot_t slot, boolean_t factory)
+{
+	p_nxge_mmac_stats_t mmac_stats;
+	int i;
+	nxge_mmac_t *mmac_info;
+
+	mmac_info = &nxgep->nxge_mmac_info;
+
+	mmac_stats = &nxgep->statsp->mmac_stats;
+	mmac_stats->mmac_max_cnt = mmac_info->num_mmac;
+	mmac_stats->mmac_avail_cnt = mmac_info->naddrfree;
+
+	for (i = 0; i < ETHERADDRL; i++) {
+		if (factory) {
+			mmac_stats->mmac_avail_pool[slot-1].ether_addr_octet[i]
+			= mmac_info->factory_mac_pool[slot][(ETHERADDRL-1) - i];
+		} else {
+			mmac_stats->mmac_avail_pool[slot-1].ether_addr_octet[i]
+			= mmac_info->mac_pool[slot].addr[(ETHERADDRL - 1) - i];
+		}
+	}
+}
+
+/*
+ * nxge_altmac_set() -- Set an alternate MAC address
+ */
+static int
+nxge_altmac_set(p_nxge_t nxgep, uint8_t *maddr, mac_addr_slot_t slot)
+{
+	uint8_t addrn;
+	uint8_t portn;
+	npi_mac_addr_t altmac;
+
+	altmac.w2 = ((uint16_t)maddr[0] << 8) | ((uint16_t)maddr[1] & 0x0ff);
+	altmac.w1 = ((uint16_t)maddr[2] << 8) | ((uint16_t)maddr[3] & 0x0ff);
+	altmac.w0 = ((uint16_t)maddr[4] << 8) | ((uint16_t)maddr[5] & 0x0ff);
+
+	portn = nxgep->mac.portnum;
+	addrn = (uint8_t)slot - 1;
+
+	if (npi_mac_altaddr_entry(nxgep->npi_handle, OP_SET, portn,
+		addrn, &altmac) != NPI_SUCCESS)
+		return (EIO);
+	/*
+	 * Enable comparison with the alternate MAC address.
+	 * While the first alternate addr is enabled by bit 1 of register
+	 * BMAC_ALTAD_CMPEN, it is enabled by bit 0 of register
+	 * XMAC_ADDR_CMPEN, so slot needs to be converted to addrn
+	 * accordingly before calling npi_mac_altaddr_entry.
+	 */
+	if (portn == XMAC_PORT_0 || portn == XMAC_PORT_1)
+		addrn = (uint8_t)slot - 1;
+	else
+		addrn = (uint8_t)slot;
+
+	if (npi_mac_altaddr_enable(nxgep->npi_handle, portn, addrn)
+		!= NPI_SUCCESS)
+		return (EIO);
+
+	return (0);
+}
+
+/*
+ * nxeg_m_mmac_add() - find an unused address slot, set the address
+ * value to the one specified, enable the port to start filtering on
+ * the new MAC address.  Returns 0 on success.
+ */
+static int
+nxge_m_mmac_add(void *arg, mac_multi_addr_t *maddr)
+{
+	p_nxge_t nxgep = arg;
+	mac_addr_slot_t slot;
+	nxge_mmac_t *mmac_info;
+	int err;
+	nxge_status_t status;
+
+	mutex_enter(nxgep->genlock);
+
+	/*
+	 * Make sure that nxge is initialized, if _start() has
+	 * not been called.
+	 */
+	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
+		status = nxge_init(nxgep);
+		if (status != NXGE_OK) {
+			mutex_exit(nxgep->genlock);
+			return (ENXIO);
+		}
+	}
+
+	mmac_info = &nxgep->nxge_mmac_info;
+	if (mmac_info->naddrfree == 0) {
+		mutex_exit(nxgep->genlock);
+		return (ENOSPC);
+	}
+	if (!mac_unicst_verify(nxgep->mach, maddr->mma_addr,
+		maddr->mma_addrlen)) {
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+	/*
+	 * 	Search for the first available slot. Because naddrfree
+	 * is not zero, we are guaranteed to find one.
+	 * 	Slot 0 is for unique (primary) MAC. The first alternate
+	 * MAC slot is slot 1.
+	 *	Each of the first two ports of Neptune has 16 alternate
+	 * MAC slots but only the first 7 (of 15) slots have assigned factory
+	 * MAC addresses. We first search among the slots without bundled
+	 * factory MACs. If we fail to find one in that range, then we
+	 * search the slots with bundled factory MACs.  A factory MAC
+	 * will be wasted while the slot is used with a user MAC address.
+	 * But the slot could be used by factory MAC again after calling
+	 * nxge_m_mmac_remove and nxge_m_mmac_reserve.
+	 */
+	if (mmac_info->num_factory_mmac < mmac_info->num_mmac) {
+		for (slot = mmac_info->num_factory_mmac + 1;
+			slot <= mmac_info->num_mmac; slot++) {
+			if (!(mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED))
+				break;
+		}
+		if (slot > mmac_info->num_mmac) {
+			for (slot = 1; slot <= mmac_info->num_factory_mmac;
+				slot++) {
+				if (!(mmac_info->mac_pool[slot].flags
+					& MMAC_SLOT_USED))
+					break;
+			}
+		}
+	} else {
+		for (slot = 1; slot <= mmac_info->num_mmac; slot++) {
+			if (!(mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED))
+				break;
+		}
+	}
+	ASSERT(slot <= mmac_info->num_mmac);
+	if ((err = nxge_altmac_set(nxgep, maddr->mma_addr, slot)) != 0) {
+		mutex_exit(nxgep->genlock);
+		return (err);
+	}
+	bcopy(maddr->mma_addr, mmac_info->mac_pool[slot].addr, ETHERADDRL);
+	mmac_info->mac_pool[slot].flags |= MMAC_SLOT_USED;
+	mmac_info->mac_pool[slot].flags &= ~MMAC_VENDOR_ADDR;
+	mmac_info->naddrfree--;
+	nxge_mmac_kstat_update(nxgep, slot, B_FALSE);
+
+	maddr->mma_slot = slot;
+
+	mutex_exit(nxgep->genlock);
+	return (0);
+}
+
+/*
+ * This function reserves an unused slot and programs the slot and the HW
+ * with a factory mac address.
+ */
+static int
+nxge_m_mmac_reserve(void *arg, mac_multi_addr_t *maddr)
+{
+	p_nxge_t nxgep = arg;
+	mac_addr_slot_t slot;
+	nxge_mmac_t *mmac_info;
+	int err;
+	nxge_status_t status;
+
+	mutex_enter(nxgep->genlock);
+
+	/*
+	 * Make sure that nxge is initialized, if _start() has
+	 * not been called.
+	 */
+	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
+		status = nxge_init(nxgep);
+		if (status != NXGE_OK) {
+			mutex_exit(nxgep->genlock);
+			return (ENXIO);
+		}
+	}
+
+	mmac_info = &nxgep->nxge_mmac_info;
+	if (mmac_info->naddrfree == 0) {
+		mutex_exit(nxgep->genlock);
+		return (ENOSPC);
+	}
+
+	slot = maddr->mma_slot;
+	if (slot == -1) {  /* -1: Take the first available slot */
+		for (slot = 1; slot <= mmac_info->num_factory_mmac; slot++) {
+			if (!(mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED))
+				break;
+		}
+		if (slot > mmac_info->num_factory_mmac) {
+			mutex_exit(nxgep->genlock);
+			return (ENOSPC);
+		}
+	}
+	if (slot < 1 || slot > mmac_info->num_factory_mmac) {
+		/*
+		 * Do not support factory MAC at a slot greater than
+		 * num_factory_mmac even when there are available factory
+		 * MAC addresses because the alternate MACs are bundled with
+		 * slot[1] through slot[num_factory_mmac]
+		 */
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+	if (mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED) {
+		mutex_exit(nxgep->genlock);
+		return (EBUSY);
+	}
+	/* Verify the address to be reserved */
+	if (!mac_unicst_verify(nxgep->mach,
+		mmac_info->factory_mac_pool[slot], ETHERADDRL)) {
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+	if (err = nxge_altmac_set(nxgep,
+		mmac_info->factory_mac_pool[slot], slot)) {
+		mutex_exit(nxgep->genlock);
+		return (err);
+	}
+	bcopy(mmac_info->factory_mac_pool[slot], maddr->mma_addr, ETHERADDRL);
+	mmac_info->mac_pool[slot].flags |= MMAC_SLOT_USED | MMAC_VENDOR_ADDR;
+	mmac_info->naddrfree--;
+
+	nxge_mmac_kstat_update(nxgep, slot, B_TRUE);
+	mutex_exit(nxgep->genlock);
+
+	/* Pass info back to the caller */
+	maddr->mma_slot = slot;
+	maddr->mma_addrlen = ETHERADDRL;
+	maddr->mma_flags = MMAC_SLOT_USED | MMAC_VENDOR_ADDR;
+
+	return (0);
+}
+
+/*
+ * Remove the specified mac address and update the HW not to filter
+ * the mac address anymore.
+ */
+static int
+nxge_m_mmac_remove(void *arg, mac_addr_slot_t slot)
+{
+	p_nxge_t nxgep = arg;
+	nxge_mmac_t *mmac_info;
+	uint8_t addrn;
+	uint8_t portn;
+	int err = 0;
+	nxge_status_t status;
+
+	mutex_enter(nxgep->genlock);
+
+	/*
+	 * Make sure that nxge is initialized, if _start() has
+	 * not been called.
+	 */
+	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
+		status = nxge_init(nxgep);
+		if (status != NXGE_OK) {
+			mutex_exit(nxgep->genlock);
+			return (ENXIO);
+		}
+	}
+
+	mmac_info = &nxgep->nxge_mmac_info;
+	if (slot < 1 || slot > mmac_info->num_mmac) {
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+
+	portn = nxgep->mac.portnum;
+	if (portn == XMAC_PORT_0 || portn == XMAC_PORT_1)
+		addrn = (uint8_t)slot - 1;
+	else
+		addrn = (uint8_t)slot;
+
+	if (mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED) {
+		if (npi_mac_altaddr_disable(nxgep->npi_handle, portn, addrn)
+				== NPI_SUCCESS) {
+			mmac_info->naddrfree++;
+			mmac_info->mac_pool[slot].flags &= ~MMAC_SLOT_USED;
+			/*
+			 * Regardless if the MAC we just stopped filtering
+			 * is a user addr or a facory addr, we must set
+			 * the MMAC_VENDOR_ADDR flag if this slot has an
+			 * associated factory MAC to indicate that a factory
+			 * MAC is available.
+			 */
+			if (slot <= mmac_info->num_factory_mmac) {
+				mmac_info->mac_pool[slot].flags
+					|= MMAC_VENDOR_ADDR;
+			}
+			/*
+			 * Clear mac_pool[slot].addr so that kstat shows 0
+			 * alternate MAC address if the slot is not used.
+			 * (But nxge_m_mmac_get returns the factory MAC even
+			 * when the slot is not used!)
+			 */
+			bzero(mmac_info->mac_pool[slot].addr, ETHERADDRL);
+			nxge_mmac_kstat_update(nxgep, slot, B_FALSE);
+		} else {
+			err = EIO;
+		}
+	} else {
+		err = EINVAL;
+	}
+
+	mutex_exit(nxgep->genlock);
+	return (err);
+}
+
+
+/*
+ * Modify a mac address added by nxge_m_mmac_add or nxge_m_mmac_reserve().
+ */
+static int
+nxge_m_mmac_modify(void *arg, mac_multi_addr_t *maddr)
+{
+	p_nxge_t nxgep = arg;
+	mac_addr_slot_t slot;
+	nxge_mmac_t *mmac_info;
+	int err = 0;
+	nxge_status_t status;
+
+	if (!mac_unicst_verify(nxgep->mach, maddr->mma_addr,
+			maddr->mma_addrlen))
+		return (EINVAL);
+
+	slot = maddr->mma_slot;
+
+	mutex_enter(nxgep->genlock);
+
+	/*
+	 * Make sure that nxge is initialized, if _start() has
+	 * not been called.
+	 */
+	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
+		status = nxge_init(nxgep);
+		if (status != NXGE_OK) {
+			mutex_exit(nxgep->genlock);
+			return (ENXIO);
+		}
+	}
+
+	mmac_info = &nxgep->nxge_mmac_info;
+	if (slot < 1 || slot > mmac_info->num_mmac) {
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+	if (mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED) {
+		if ((err = nxge_altmac_set(nxgep, maddr->mma_addr, slot))
+			!= 0) {
+			bcopy(maddr->mma_addr, mmac_info->mac_pool[slot].addr,
+				ETHERADDRL);
+			/*
+			 * Assume that the MAC passed down from the caller
+			 * is not a factory MAC address (The user should
+			 * call mmac_remove followed by mmac_reserve if
+			 * he wants to use the factory MAC for this slot).
+			 */
+			mmac_info->mac_pool[slot].flags &= ~MMAC_VENDOR_ADDR;
+			nxge_mmac_kstat_update(nxgep, slot, B_FALSE);
+		}
+	} else {
+		err = EINVAL;
+	}
+	mutex_exit(nxgep->genlock);
+	return (err);
+}
+
+/*
+ * nxge_m_mmac_get() - Get the MAC address and other information
+ * related to the slot.  mma_flags should be set to 0 in the call.
+ * Note: although kstat shows MAC address as zero when a slot is
+ * not used, Crossbow expects nxge_m_mmac_get to copy factory MAC
+ * to the caller as long as the slot is not using a user MAC address.
+ * The following table shows the rules,
+ *
+ *				   USED    VENDOR    mma_addr
+ * ------------------------------------------------------------
+ * (1) Slot uses a user MAC:        yes      no     user MAC
+ * (2) Slot uses a factory MAC:     yes      yes    factory MAC
+ * (3) Slot is not used but is
+ *     factory MAC capable:         no       yes    factory MAC
+ * (4) Slot is not used and is
+ *     not factory MAC capable:     no       no        0
+ * ------------------------------------------------------------
+ */
+static int
+nxge_m_mmac_get(void *arg, mac_multi_addr_t *maddr)
+{
+	nxge_t *nxgep = arg;
+	mac_addr_slot_t slot;
+	nxge_mmac_t *mmac_info;
+	nxge_status_t status;
+
+	slot = maddr->mma_slot;
+
+	mutex_enter(nxgep->genlock);
+
+	/*
+	 * Make sure that nxge is initialized, if _start() has
+	 * not been called.
+	 */
+	if (!(nxgep->drv_state & STATE_HW_INITIALIZED)) {
+		status = nxge_init(nxgep);
+		if (status != NXGE_OK) {
+			mutex_exit(nxgep->genlock);
+			return (ENXIO);
+		}
+	}
+
+	mmac_info = &nxgep->nxge_mmac_info;
+
+	if (slot < 1 || slot > mmac_info->num_mmac) {
+		mutex_exit(nxgep->genlock);
+		return (EINVAL);
+	}
+	maddr->mma_flags = 0;
+	if (mmac_info->mac_pool[slot].flags & MMAC_SLOT_USED)
+		maddr->mma_flags |= MMAC_SLOT_USED;
+
+	if (mmac_info->mac_pool[slot].flags & MMAC_VENDOR_ADDR) {
+		maddr->mma_flags |= MMAC_VENDOR_ADDR;
+		bcopy(mmac_info->factory_mac_pool[slot],
+			maddr->mma_addr, ETHERADDRL);
+		maddr->mma_addrlen = ETHERADDRL;
+	} else {
+		if (maddr->mma_flags & MMAC_SLOT_USED) {
+			bcopy(mmac_info->mac_pool[slot].addr,
+				maddr->mma_addr, ETHERADDRL);
+			maddr->mma_addrlen = ETHERADDRL;
+		} else {
+			bzero(maddr->mma_addr, ETHERADDRL);
+			maddr->mma_addrlen = 0;
+		}
+	}
+	mutex_exit(nxgep->genlock);
+	return (0);
+}
+
+
 static boolean_t
 nxge_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
 {
+	nxge_t *nxgep = arg;
+	uint32_t *txflags = cap_data;
+	multiaddress_capab_t *mmacp = cap_data;
+
 	switch (cap) {
-	case MAC_CAPAB_HCKSUM: {
-		uint32_t *txflags = cap_data;
-
+	case MAC_CAPAB_HCKSUM:
 		*txflags = HCKSUM_INET_PARTIAL;
 		break;
-	}
 	case MAC_CAPAB_POLL:
 		/*
 		 * There's nothing for us to fill in, simply returning
@@ -3210,6 +3675,25 @@
 		 */
 		break;
 
+	case MAC_CAPAB_MULTIADDRESS:
+		mutex_enter(nxgep->genlock);
+
+		mmacp->maddr_naddr = nxgep->nxge_mmac_info.num_mmac;
+		mmacp->maddr_naddrfree = nxgep->nxge_mmac_info.naddrfree;
+		mmacp->maddr_flag = 0; /* 0 is requried by PSARC2006/265 */
+		/*
+		 * maddr_handle is driver's private data, passed back to
+		 * entry point functions as arg.
+		 */
+		mmacp->maddr_handle	= nxgep;
+		mmacp->maddr_add	= nxge_m_mmac_add;
+		mmacp->maddr_remove	= nxge_m_mmac_remove;
+		mmacp->maddr_modify	= nxge_m_mmac_modify;
+		mmacp->maddr_get	= nxge_m_mmac_get;
+		mmacp->maddr_reserve	= nxge_m_mmac_reserve;
+
+		mutex_exit(nxgep->genlock);
+		break;
 	default:
 		return (B_FALSE);
 	}
@@ -3245,14 +3729,14 @@
 	DEVO_REV,		/* devo_rev */
 	0,			/* devo_refcnt */
 	nulldev,
-	nulldev,		 /* devo_identify	*/
-	nulldev,		 /* devo_probe    	*/
-	nxge_attach,		 /* devo_attach    	*/
-	nxge_detach,		 /* devo_detach 	*/
-	nodev,			 /* devo_reset 		*/
-	&nxge_cb_ops,		 /* devo_cb_ops 	*/
-	(struct bus_ops *)NULL, /* devo_bus_ops 	*/
-	ddi_power		 /* devo_power 		*/
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	nxge_attach,		/* devo_attach */
+	nxge_detach,		/* devo_detach */
+	nodev,			/* devo_reset */
+	&nxge_cb_ops,		/* devo_cb_ops */
+	(struct bus_ops *)NULL, /* devo_bus_ops	*/
+	ddi_power		/* devo_power */
 };
 
 extern	struct	mod_ops	mod_driverops;
--- a/usr/src/uts/sun4v/io/nxge/nxge_virtual.c	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/io/nxge/nxge_virtual.c	Thu Feb 22 15:55:32 2007 -0800
@@ -1156,7 +1156,7 @@
 	 * these parameter affect the classification outcome.
 	 * these parameters are used to configure the Flow key and
 	 * the TCAM key for each of the IP classes.
-	 * Includee here are also the H1 and H2 initial values
+	 * Included here are also the H1 and H2 initial values
 	 * which affect the distribution as well as final hash value
 	 * (hence the offset into RDC table and FCRAM bucket location)
 	 *
@@ -1652,8 +1652,8 @@
 	 * Config types: equal: (default) DMA channels, RDC groups, TCAM, FCRAM
 	 * are shared equally across all the ports.
 	 *
-	 * Fair: DMA channels, RDC groups, TCAM, FCRAM are shared proprtional to
-	 * te port speed.
+	 * Fair: DMA channels, RDC groups, TCAM, FCRAM are shared proportional
+	 * to the port speed.
 	 *
 	 *
 	 * custom: DMA channels, RDC groups, TCAM, FCRAM partition is
@@ -3363,7 +3363,7 @@
 	uint_t prop_len;
 	uint_t i;
 	uint8_t func_num;
-	uint8_t num_macs;
+	uint8_t total_factory_macs;
 
 	NXGE_DEBUG_MSG((nxgep, DDI_CTL, "==> nxge_get_mac_addr_properties "));
 
@@ -3408,11 +3408,18 @@
 
 	func_num = nxgep->function_num;
 
-	/* NIU does not need max_num_mmac */
+	/*
+	 * total_factory_macs is the total number of MACs the factory assigned
+	 * to the whole Neptune device.  NIU does not need this parameter
+	 * because it derives the number of factory MACs for each port from
+	 * the device properties.
+	 */
 	if (nxgep->niu_type == NEPTUNE || nxgep->niu_type == NEPTUNE_2) {
-		if (nxge_espc_num_macs_get(nxgep, &num_macs) == NXGE_OK) {
-			nxgep->nxge_mmac_info.max_num_mmac = num_macs;
-		} else {
+		if (nxge_espc_num_macs_get(nxgep, &total_factory_macs)
+			== NXGE_OK) {
+			nxgep->nxge_mmac_info.total_factory_macs
+				= total_factory_macs;
+	} else {
 			NXGE_ERROR_MSG((NULL, NXGE_ERR_CTL,
 				"nxge_espc_num_macs_get: espc access failed"));
 			return (NXGE_ERROR);
@@ -3432,35 +3439,31 @@
 			 * XAUI may have up to 18 MACs, more than the XMAC can
 			 * use (1 unique MAC plus 16 alternate MACs)
 			 */
-			nxgep->nxge_mmac_info.num_mmac = prop_len / 6;
-			if (nxgep->nxge_mmac_info.num_mmac >
-				XMAC_MAX_ALT_ADDR_ENTRY + 1) {
-				nxgep->nxge_mmac_info.num_mmac =
-					XMAC_MAX_ALT_ADDR_ENTRY + 1;
+			nxgep->nxge_mmac_info.num_factory_mmac
+			    = prop_len / ETHERADDRL - 1;
+			if (nxgep->nxge_mmac_info.num_factory_mmac >
+				XMAC_MAX_ALT_ADDR_ENTRY) {
+				nxgep->nxge_mmac_info.num_factory_mmac =
+					XMAC_MAX_ALT_ADDR_ENTRY;
 			}
 			ddi_prop_free(prop_val);
 		}
 	} else {
-		nxgep->nxge_mmac_info.num_mmac
-			= nxgep->nxge_mmac_info.max_num_mmac >>
-			(nxgep->nports >> 1);
+		/*
+		 * total_factory_macs = 32
+		 * num_factory_mmac = (32 >> (nports/2)) - 1
+		 * So if nports = 4, then num_factory_mmac =  7
+		 *    if nports = 2, then num_factory_mmac = 15
+		 */
+		nxgep->nxge_mmac_info.num_factory_mmac
+			= ((nxgep->nxge_mmac_info.total_factory_macs >>
+			(nxgep->nports >> 1))) - 1;
 	}
-
-	for (i = 0; i < nxgep->nxge_mmac_info.num_mmac - 1; ++i) {
-		/* Initialze all mac addr. to "AVAILABLE" state */
-		nxgep->nxge_mmac_info.rsv_mmac[i] = B_FALSE;
-		/*
-		 * XMAC: Disable alter MAC address comparison only (XMAC's
-		 * unique MAC comparison is always enabled. BMAC: (Neptune
-		 * only) Disable both unique and alter MAC address comparison
-		 */
+	for (i = 0; i <= nxgep->nxge_mmac_info.num_mmac; i++) {
 		(void) npi_mac_altaddr_disable(nxgep->npi_handle,
 			NXGE_GET_PORT_NUM(func_num), i);
 	}
 
-	/*
-	 * Initialize alt. mac addr. in the mac pool
-	 */
 	(void) nxge_init_mmac(nxgep);
 	return (NXGE_OK);
 }
@@ -3548,7 +3551,7 @@
 }
 
 /*
- * Note: This function assume the following distribution of mac
+ * Note: This function assumes the following distribution of mac
  * addresses among 4 ports in neptune:
  *
  *      -------------
@@ -3587,49 +3590,61 @@
 static void
 nxge_init_mmac(p_nxge_t nxgep)
 {
-	int i;
+	int slot;
 	uint8_t func_num;
 	uint16_t *base_mmac_addr;
-	uint32_t first_alt_mac_ls4b;
+	uint32_t alt_mac_ls4b;
 	uint16_t *mmac_addr;
-	uint32_t base_mac_ls4b;	/* least significant 4 bytes */
-	nxge_mmac_t *mac_poolp;
+	uint32_t base_mac_ls4b; /* least significant 4 bytes */
+	nxge_mmac_t *mmac_info;
 	npi_mac_addr_t mac_addr;
 
 	func_num = nxgep->function_num;
 	base_mmac_addr = (uint16_t *)&nxgep->factaddr;
-	mac_poolp = (nxge_mmac_t *)&nxgep->nxge_mmac_info;
+	mmac_info = (nxge_mmac_t *)&nxgep->nxge_mmac_info;
 
 	base_mac_ls4b = ((uint32_t)base_mmac_addr[1]) << 16 |
 		base_mmac_addr[2];
 
-	if (nxgep->niu_type == N2_NIU)
-		first_alt_mac_ls4b = base_mac_ls4b + 1;
-	else			/* Neptune */
-		first_alt_mac_ls4b = base_mac_ls4b + (nxgep->nports - func_num)
-			+ (func_num * (nxgep->nxge_mmac_info.num_mmac - 1));
-
-	for (i = 0; i < nxgep->nxge_mmac_info.num_mmac - 1; ++i) {
+	if (nxgep->niu_type == N2_NIU) {
+		alt_mac_ls4b = base_mac_ls4b + 1; /* ls4b of 1st altmac */
+	} else {			/* Neptune */
+		alt_mac_ls4b = base_mac_ls4b + (nxgep->nports - func_num)
+			+ (func_num * (mmac_info->num_factory_mmac));
+	}
+
+	/* Set flags for unique MAC */
+	mmac_info->mac_pool[0].flags |= MMAC_SLOT_USED | MMAC_VENDOR_ADDR;
+
+	/* Clear flags of all alternate MAC slots */
+	for (slot = 1; slot <= mmac_info->num_mmac; slot++) {
+		if (slot <= mmac_info->num_factory_mmac)
+			mmac_info->mac_pool[slot].flags = MMAC_VENDOR_ADDR;
+		else
+			mmac_info->mac_pool[slot].flags = 0;
+	}
+
+	/* Generate and store factory alternate MACs */
+	for (slot = 1; slot <= mmac_info->num_factory_mmac; slot++) {
+		mmac_addr = (uint16_t *)&mmac_info->factory_mac_pool[slot];
+		mmac_addr[0] = base_mmac_addr[0];
+		mac_addr.w2 = mmac_addr[0];
+
+		mmac_addr[1] = (alt_mac_ls4b >> 16) & 0x0FFFF;
+		mac_addr.w1 = mmac_addr[1];
+
+		mmac_addr[2] = alt_mac_ls4b & 0x0FFFF;
+		mac_addr.w0 = mmac_addr[2];
 		/*
-		 * Populate shadow mac pool w/ available mac. so we dont have
-		 * to read the h/w to search for a mac addr.
-		 */
-		mmac_addr = (uint16_t *)&mac_poolp->mmac_pool[i];
-		mmac_addr[0] = base_mmac_addr[0];
-		mac_addr.w0 = mmac_addr[0];
-
-		mmac_addr[1] = (first_alt_mac_ls4b >> 16) & 0x0FFFF;
-		mac_addr.w1 = mmac_addr[1];
-
-		mmac_addr[2] = first_alt_mac_ls4b & 0x0FFFF;
-		mac_addr.w2 = mmac_addr[2];
-
-		/*
-		 * Program the h/w alt. mac address, starting from reg1(reg0
-		 * corr. to unique mac addr)
+		 * slot minus 1 because npi_mac_alraddr_entry expects 0
+		 * for the first alternate mac address.
 		 */
 		(void) npi_mac_altaddr_entry(nxgep->npi_handle, OP_SET,
-			NXGE_GET_PORT_NUM(func_num), i, &mac_addr);
-		first_alt_mac_ls4b++;
+			NXGE_GET_PORT_NUM(func_num), slot - 1, &mac_addr);
+
+		alt_mac_ls4b++;
 	}
+	/* Initialize the first two parameters for mmac kstat */
+	nxgep->statsp->mmac_stats.mmac_max_cnt = mmac_info->num_mmac;
+	nxgep->statsp->mmac_stats.mmac_avail_cnt = mmac_info->num_mmac;
 }
--- a/usr/src/uts/sun4v/sys/nxge/nxge_impl.h	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/sys/nxge/nxge_impl.h	Thu Feb 22 15:55:32 2007 -0800
@@ -531,16 +531,33 @@
 	unsigned char		*nxge_romp;	/* fcode pointer */
 } dev_regs_t, *p_dev_regs_t;
 
+
+typedef struct _nxge_mac_addr_t {
+	ether_addr_t	addr;
+	uint_t		flags;
+} nxge_mac_addr_t;
+
 /*
- * Driver alternate mac address structure.
+ * The hardware supports 1 unique MAC and 16 alternate MACs (num_mmac)
+ * for each XMAC port and supports 1 unique MAC and 7 alternate MACs
+ * for each BMAC port.  The number of MACs assigned by the factory is
+ * different and is as follows,
+ * 	BMAC port:		   num_factory_mmac = num_mmac = 7
+ *	XMAC port on a 2-port NIC: num_factory_mmac = num_mmac - 1 = 15
+ *	XMAC port on a 4-port NIC: num_factory_mmac = 7
+ * So num_factory_mmac is smaller than num_mmac.  nxge_m_mmac_add uses
+ * num_mmac and nxge_m_mmac_reserve uses num_factory_mmac.
+ *
+ * total_factory_macs is the total number of factory MACs, including
+ * the unique MAC, assigned to a Neptune based NIC card, it is 32.
  */
 typedef struct _nxge_mmac_t {
-	kmutex_t	mmac_lock;
-	uint8_t		max_num_mmac;	/* Max allocated per card */
-	uint8_t		num_mmac;	/* Mac addr. per function */
-	struct ether_addr mmac_pool[16]; /* Mac addr pool per function in s/w */
-	boolean_t	rsv_mmac[16];	/* Reserved mac addr. in the pool */
-	uint8_t		num_avail_mmac;	/* # of rsv.ed mac addr. in the pool */
+	uint8_t		total_factory_macs;
+	uint8_t		num_mmac;
+	uint8_t		num_factory_mmac;
+	nxge_mac_addr_t	mac_pool[XMAC_MAX_ADDR_ENTRY];
+	ether_addr_t	factory_mac_pool[XMAC_MAX_ADDR_ENTRY];
+	uint8_t		naddrfree;  /* number of alt mac addr available */
 } nxge_mmac_t;
 
 /*
--- a/usr/src/uts/sun4v/sys/nxge/nxge_mac_hw.h	Thu Feb 22 13:40:56 2007 -0800
+++ b/usr/src/uts/sun4v/sys/nxge/nxge_mac_hw.h	Thu Feb 22 15:55:32 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -815,6 +815,7 @@
 
 #define	BMAC_ALT_ADDR_BASE		0x118
 #define	BMAC_MAX_ALT_ADDR_ENTRY		7	   /* 7 alternate MAC addr */
+#define	BMAC_MAX_ADDR_ENTRY		(BMAC_MAX_ALT_ADDR_ENTRY + 1)
 
 /* hash table registers */
 #define	MAC_MAX_HASH_ENTRY		16
@@ -1051,6 +1052,7 @@
 
 /* Alternate MAC address registers */
 #define	XMAC_MAX_ALT_ADDR_ENTRY		16	   /* 16 alternate MAC addrs */
+#define	XMAC_MAX_ADDR_ENTRY		(XMAC_MAX_ALT_ADDR_ENTRY + 1)
 
 /* Max / Min parameters for Neptune MAC */