xref: /illumos-gate/usr/src/uts/common/io/i40e/i40e_gld.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
14  * Copyright 2022 Joyent, Inc.
15  * Copyright 2017 Tegile Systems, Inc.  All rights reserved.
16  * Copyright 2020 Ryan Zezeski
17  * Copyright 2020 RackTop Systems, Inc.
18  * Copyright 2021 Oxide Computer Company
19  */
20 
21 /*
22  * For more information, please see the big theory statement in i40e_main.c.
23  */
24 
25 #include "i40e_sw.h"
26 
27 #define	I40E_PROP_RX_DMA_THRESH	"_rx_dma_threshold"
28 #define	I40E_PROP_TX_DMA_THRESH	"_tx_dma_threshold"
29 #define	I40E_PROP_RX_ITR	"_rx_intr_throttle"
30 #define	I40E_PROP_TX_ITR	"_tx_intr_throttle"
31 #define	I40E_PROP_OTHER_ITR	"_other_intr_throttle"
32 
33 char *i40e_priv_props[] = {
34 	I40E_PROP_RX_DMA_THRESH,
35 	I40E_PROP_TX_DMA_THRESH,
36 	I40E_PROP_RX_ITR,
37 	I40E_PROP_TX_ITR,
38 	I40E_PROP_OTHER_ITR,
39 	NULL
40 };
41 
42 static int
43 i40e_group_remove_mac(void *arg, const uint8_t *mac_addr)
44 {
45 	i40e_rx_group_t *rxg = arg;
46 	i40e_t *i40e = rxg->irg_i40e;
47 	struct i40e_aqc_remove_macvlan_element_data filt;
48 	struct i40e_hw *hw = &i40e->i40e_hw_space;
49 	int ret, i, last;
50 	i40e_uaddr_t *iua;
51 
52 	if (I40E_IS_MULTICAST(mac_addr))
53 		return (EINVAL);
54 
55 	mutex_enter(&i40e->i40e_general_lock);
56 
57 	if (i40e->i40e_state & I40E_SUSPENDED) {
58 		ret = ECANCELED;
59 		goto done;
60 	}
61 
62 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
63 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
64 		    ETHERADDRL) == 0)
65 			break;
66 	}
67 
68 	if (i == i40e->i40e_resources.ifr_nmacfilt_used) {
69 		ret = ENOENT;
70 		goto done;
71 	}
72 
73 	iua = &i40e->i40e_uaddrs[i];
74 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used > 0);
75 
76 	bzero(&filt, sizeof (filt));
77 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
78 	filt.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
79 	    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
80 
81 	if (i40e_aq_remove_macvlan(hw, iua->iua_vsi, &filt, 1, NULL) !=
82 	    I40E_SUCCESS) {
83 		i40e_error(i40e, "failed to remove mac address "
84 		    "%02x:%02x:%02x:%02x:%02x:%02x from unicast filter: %d",
85 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
86 		    mac_addr[4], mac_addr[5], filt.error_code);
87 		ret = EIO;
88 		goto done;
89 	}
90 
91 	last = i40e->i40e_resources.ifr_nmacfilt_used - 1;
92 	if (i != last) {
93 		i40e_uaddr_t *src = &i40e->i40e_uaddrs[last];
94 		bcopy(src, iua, sizeof (i40e_uaddr_t));
95 	}
96 
97 	/*
98 	 * Set the multicast bit in the last one to indicate to ourselves that
99 	 * it's invalid.
100 	 */
101 	bzero(&i40e->i40e_uaddrs[last], sizeof (i40e_uaddr_t));
102 	i40e->i40e_uaddrs[last].iua_mac[0] = 0x01;
103 	i40e->i40e_resources.ifr_nmacfilt_used--;
104 	ret = 0;
105 done:
106 	mutex_exit(&i40e->i40e_general_lock);
107 
108 	return (ret);
109 }
110 
111 static int
112 i40e_group_add_mac(void *arg, const uint8_t *mac_addr)
113 {
114 	i40e_rx_group_t	*rxg = arg;
115 	i40e_t		*i40e = rxg->irg_i40e;
116 	struct i40e_hw	*hw = &i40e->i40e_hw_space;
117 	int		i, ret;
118 	i40e_uaddr_t	*iua;
119 	struct i40e_aqc_add_macvlan_element_data filt;
120 
121 	if (I40E_IS_MULTICAST(mac_addr))
122 		return (EINVAL);
123 
124 	mutex_enter(&i40e->i40e_general_lock);
125 	if (i40e->i40e_state & I40E_SUSPENDED) {
126 		ret = ECANCELED;
127 		goto done;
128 	}
129 
130 	if (i40e->i40e_resources.ifr_nmacfilt ==
131 	    i40e->i40e_resources.ifr_nmacfilt_used) {
132 		ret = ENOSPC;
133 		goto done;
134 	}
135 
136 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
137 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
138 		    ETHERADDRL) == 0) {
139 			ret = EEXIST;
140 			goto done;
141 		}
142 	}
143 
144 	bzero(&filt, sizeof (filt));
145 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
146 	filt.flags = I40E_AQC_MACVLAN_ADD_PERFECT_MATCH	|
147 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
148 
149 	if ((ret = i40e_aq_add_macvlan(hw, rxg->irg_vsi_seid, &filt, 1,
150 	    NULL)) != I40E_SUCCESS) {
151 		i40e_error(i40e, "failed to add mac address "
152 		    "%02x:%02x:%02x:%02x:%02x:%02x to unicast filter: %d",
153 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
154 		    mac_addr[4], mac_addr[5], ret);
155 		ret = EIO;
156 		goto done;
157 	}
158 
159 	iua = &i40e->i40e_uaddrs[i40e->i40e_resources.ifr_nmacfilt_used];
160 	bcopy(mac_addr, iua->iua_mac, ETHERADDRL);
161 	iua->iua_vsi = rxg->irg_vsi_seid;
162 	i40e->i40e_resources.ifr_nmacfilt_used++;
163 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used <=
164 	    i40e->i40e_resources.ifr_nmacfilt);
165 	ret = 0;
166 done:
167 	mutex_exit(&i40e->i40e_general_lock);
168 	return (ret);
169 }
170 
171 static int
172 i40e_m_start(void *arg)
173 {
174 	i40e_t *i40e = arg;
175 	int rc = 0;
176 
177 	mutex_enter(&i40e->i40e_general_lock);
178 	if (i40e->i40e_state & I40E_SUSPENDED) {
179 		rc = ECANCELED;
180 		goto done;
181 	}
182 
183 	if (!i40e_start(i40e)) {
184 		rc = EIO;
185 		goto done;
186 	}
187 
188 	atomic_or_32(&i40e->i40e_state, I40E_STARTED);
189 done:
190 	mutex_exit(&i40e->i40e_general_lock);
191 
192 	return (rc);
193 }
194 
195 static void
196 i40e_m_stop(void *arg)
197 {
198 	i40e_t *i40e = arg;
199 
200 	mutex_enter(&i40e->i40e_general_lock);
201 
202 	if (i40e->i40e_state & I40E_SUSPENDED)
203 		goto done;
204 
205 	atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
206 	i40e_stop(i40e);
207 done:
208 	mutex_exit(&i40e->i40e_general_lock);
209 }
210 
211 /*
212  * Enable and disable promiscuous mode as requested. We have to toggle both
213  * unicast and multicast. Note that multicast may already be enabled due to the
214  * i40e_m_multicast may toggle it itself. See i40e_main.c for more information
215  * on this.
216  */
217 static int
218 i40e_m_promisc(void *arg, boolean_t on)
219 {
220 	i40e_t *i40e = arg;
221 	struct i40e_hw *hw = &i40e->i40e_hw_space;
222 	int ret = 0, err = 0;
223 
224 	mutex_enter(&i40e->i40e_general_lock);
225 	if (i40e->i40e_state & I40E_SUSPENDED) {
226 		ret = ECANCELED;
227 		goto done;
228 	}
229 
230 
231 	ret = i40e_aq_set_vsi_unicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
232 	    on, NULL, B_FALSE);
233 	if (ret != I40E_SUCCESS) {
234 		i40e_error(i40e, "failed to %s unicast promiscuity on "
235 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
236 		    ret);
237 		err = EIO;
238 		goto done;
239 	}
240 
241 	/*
242 	 * If we have a non-zero mcast_promisc_count, then it has already been
243 	 * enabled or we need to leave it that way and not touch it.
244 	 */
245 	if (i40e->i40e_mcast_promisc_count > 0) {
246 		i40e->i40e_promisc_on = on;
247 		goto done;
248 	}
249 
250 	ret = i40e_aq_set_vsi_multicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
251 	    on, NULL);
252 	if (ret != I40E_SUCCESS) {
253 		i40e_error(i40e, "failed to %s multicast promiscuity on "
254 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
255 		    ret);
256 
257 		/*
258 		 * Try our best to put us back into a state that MAC expects us
259 		 * to be in.
260 		 */
261 		ret = i40e_aq_set_vsi_unicast_promiscuous(hw,
262 		    I40E_DEF_VSI_SEID(i40e), !on, NULL, B_FALSE);
263 		if (ret != I40E_SUCCESS) {
264 			i40e_error(i40e, "failed to %s unicast promiscuity on "
265 			    "the default VSI after toggling multicast failed: "
266 			    "%d", on == B_TRUE ? "disable" : "enable", ret);
267 		}
268 
269 		err = EIO;
270 		goto done;
271 	} else {
272 		i40e->i40e_promisc_on = on;
273 	}
274 
275 done:
276 	mutex_exit(&i40e->i40e_general_lock);
277 	return (err);
278 }
279 
280 /*
281  * See the big theory statement in i40e_main.c for multicast address management.
282  */
283 static int
284 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
285 {
286 	struct i40e_hw *hw = &i40e->i40e_hw_space;
287 	struct i40e_aqc_add_macvlan_element_data filt;
288 	i40e_maddr_t *mc;
289 	int ret;
290 
291 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
292 
293 	if (i40e->i40e_resources.ifr_nmcastfilt_used ==
294 	    i40e->i40e_resources.ifr_nmcastfilt) {
295 		if (i40e->i40e_mcast_promisc_count == 0 &&
296 		    i40e->i40e_promisc_on == B_FALSE) {
297 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
298 			    I40E_DEF_VSI_SEID(i40e), B_TRUE, NULL);
299 			if (ret != I40E_SUCCESS) {
300 				i40e_error(i40e, "failed to enable multicast "
301 				    "promiscuous mode on VSI %d: %d",
302 				    I40E_DEF_VSI_SEID(i40e), ret);
303 				return (EIO);
304 			}
305 		}
306 		i40e->i40e_mcast_promisc_count++;
307 		return (0);
308 	}
309 
310 	mc = &i40e->i40e_maddrs[i40e->i40e_resources.ifr_nmcastfilt_used];
311 	bzero(&filt, sizeof (filt));
312 	bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
313 	filt.flags = I40E_AQC_MACVLAN_ADD_HASH_MATCH |
314 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
315 
316 	if ((ret = i40e_aq_add_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt, 1,
317 	    NULL)) != I40E_SUCCESS) {
318 		i40e_error(i40e, "failed to add mac address "
319 		    "%02x:%02x:%02x:%02x:%02x:%02x to multicast filter: %d",
320 		    multicast_address[0], multicast_address[1],
321 		    multicast_address[2], multicast_address[3],
322 		    multicast_address[4], multicast_address[5],
323 		    ret);
324 		return (EIO);
325 	}
326 
327 	bcopy(multicast_address, mc->ima_mac, ETHERADDRL);
328 	i40e->i40e_resources.ifr_nmcastfilt_used++;
329 	return (0);
330 }
331 
332 /*
333  * See the big theory statement in i40e_main.c for multicast address management.
334  */
335 static int
336 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
337 {
338 	int i, ret;
339 	struct i40e_hw *hw = &i40e->i40e_hw_space;
340 
341 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
342 
343 	for (i = 0; i < i40e->i40e_resources.ifr_nmcastfilt_used; i++) {
344 		struct i40e_aqc_remove_macvlan_element_data filt;
345 		int last;
346 
347 		if (bcmp(multicast_address, i40e->i40e_maddrs[i].ima_mac,
348 		    ETHERADDRL) != 0) {
349 			continue;
350 		}
351 
352 		bzero(&filt, sizeof (filt));
353 		bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
354 		filt.flags = I40E_AQC_MACVLAN_DEL_HASH_MATCH |
355 		    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
356 
357 		if (i40e_aq_remove_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt,
358 		    1, NULL) != I40E_SUCCESS) {
359 			i40e_error(i40e, "failed to remove mac address "
360 			    "%02x:%02x:%02x:%02x:%02x:%02x from multicast "
361 			    "filter: %d",
362 			    multicast_address[0], multicast_address[1],
363 			    multicast_address[2], multicast_address[3],
364 			    multicast_address[4], multicast_address[5],
365 			    filt.error_code);
366 			return (EIO);
367 		}
368 
369 		last = i40e->i40e_resources.ifr_nmcastfilt_used - 1;
370 		if (i != last) {
371 			bcopy(&i40e->i40e_maddrs[last], &i40e->i40e_maddrs[i],
372 			    sizeof (i40e_maddr_t));
373 			bzero(&i40e->i40e_maddrs[last], sizeof (i40e_maddr_t));
374 		}
375 
376 		ASSERT(i40e->i40e_resources.ifr_nmcastfilt_used > 0);
377 		i40e->i40e_resources.ifr_nmcastfilt_used--;
378 		return (0);
379 	}
380 
381 	if (i40e->i40e_mcast_promisc_count > 0) {
382 		if (i40e->i40e_mcast_promisc_count == 1 &&
383 		    i40e->i40e_promisc_on == B_FALSE) {
384 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
385 			    I40E_DEF_VSI_SEID(i40e), B_FALSE, NULL);
386 			if (ret != I40E_SUCCESS) {
387 				i40e_error(i40e, "failed to disable "
388 				    "multicast promiscuous mode on VSI %d: %d",
389 				    I40E_DEF_VSI_SEID(i40e), ret);
390 				return (EIO);
391 			}
392 		}
393 		i40e->i40e_mcast_promisc_count--;
394 
395 		return (0);
396 	}
397 
398 	return (ENOENT);
399 }
400 
401 static int
402 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
403 {
404 	i40e_t *i40e = arg;
405 	int rc;
406 
407 	mutex_enter(&i40e->i40e_general_lock);
408 
409 	if (i40e->i40e_state & I40E_SUSPENDED) {
410 		mutex_exit(&i40e->i40e_general_lock);
411 		return (ECANCELED);
412 	}
413 
414 	if (add == B_TRUE) {
415 		rc = i40e_multicast_add(i40e, multicast_address);
416 	} else {
417 		rc = i40e_multicast_remove(i40e, multicast_address);
418 	}
419 
420 	mutex_exit(&i40e->i40e_general_lock);
421 	return (rc);
422 }
423 
424 /* ARGSUSED */
425 static void
426 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
427 {
428 	/*
429 	 * At this time, we don't support toggling i40e into loopback mode. It's
430 	 * questionable how much value this has when there's no clear way to
431 	 * toggle this behavior from a supported way in userland.
432 	 */
433 	miocnak(q, mp, 0, EINVAL);
434 }
435 
436 static int
437 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
438 {
439 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
440 	int rv;
441 
442 	if ((rv = i40e_setup_ring(itrq)) != 0)
443 		return (rv);
444 
445 	/*
446 	 * GLDv3 requires we keep track of a generation number, as it uses
447 	 * that number to keep track of whether or not a ring is active.
448 	 */
449 	mutex_enter(&itrq->itrq_rx_lock);
450 	itrq->itrq_rxgen = gen_num;
451 	mutex_exit(&itrq->itrq_rx_lock);
452 	return (0);
453 }
454 
455 static void
456 i40e_ring_stop(mac_ring_driver_t rh)
457 {
458 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
459 
460 	if (!i40e_shutdown_ring(itrq)) {
461 		i40e_t *i40e = itrq->itrq_i40e;
462 
463 		ddi_fm_service_impact(i40e->i40e_dip, DDI_SERVICE_LOST);
464 		i40e_error(i40e, "Failed to stop ring %u", itrq->itrq_index);
465 	}
466 }
467 
468 /* ARGSUSED */
469 static int
470 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
471 {
472 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
473 
474 	mutex_enter(&itrq->itrq_rx_lock);
475 	ASSERT(itrq->itrq_intr_poll == B_TRUE);
476 	i40e_intr_rx_queue_enable(itrq);
477 	itrq->itrq_intr_poll = B_FALSE;
478 	mutex_exit(&itrq->itrq_rx_lock);
479 
480 	return (0);
481 }
482 
483 /* ARGSUSED */
484 static int
485 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
486 {
487 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
488 
489 	mutex_enter(&itrq->itrq_rx_lock);
490 	i40e_intr_rx_queue_disable(itrq);
491 	itrq->itrq_intr_poll = B_TRUE;
492 	mutex_exit(&itrq->itrq_rx_lock);
493 
494 	return (0);
495 }
496 
497 /* ARGSUSED */
498 static void
499 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
500     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
501 {
502 	i40e_t *i40e = arg;
503 	mac_intr_t *mintr = &infop->mri_intr;
504 	i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
505 
506 	/*
507 	 * Note the group index here is expected to be -1 due to the fact that
508 	 * we're not actually grouping things tx-wise at this time.
509 	 */
510 	ASSERT(group_index == -1);
511 	ASSERT(ring_index < i40e->i40e_num_trqpairs_per_vsi);
512 
513 	itrq->itrq_mactxring = rh;
514 	infop->mri_driver = (mac_ring_driver_t)itrq;
515 	infop->mri_start = NULL;
516 	infop->mri_stop = NULL;
517 	infop->mri_tx = i40e_ring_tx;
518 	infop->mri_stat = i40e_tx_ring_stat;
519 
520 	/*
521 	 * We only provide the handle in cases where we have MSI-X interrupts,
522 	 * to indicate that we'd actually support retargetting.
523 	 */
524 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
525 		mintr->mi_ddi_handle =
526 		    i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
527 	}
528 }
529 
530 /* ARGSUSED */
531 static void
532 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
533     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
534 {
535 	i40e_t *i40e = arg;
536 	mac_intr_t *mintr = &infop->mri_intr;
537 	uint_t trqpair_index;
538 	i40e_trqpair_t *itrq;
539 
540 	/* This assumes static groups. */
541 	ASSERT3S(group_index, >=, 0);
542 	ASSERT3S(ring_index, >=, 0);
543 	trqpair_index = (group_index * i40e->i40e_num_trqpairs_per_vsi) +
544 	    ring_index;
545 	ASSERT3U(trqpair_index, <, i40e->i40e_num_trqpairs);
546 	itrq = &i40e->i40e_trqpairs[trqpair_index];
547 
548 	itrq->itrq_macrxring = rh;
549 	infop->mri_driver = (mac_ring_driver_t)itrq;
550 	infop->mri_start = i40e_ring_start;
551 	infop->mri_stop = i40e_ring_stop;
552 	infop->mri_poll = i40e_ring_rx_poll;
553 	infop->mri_stat = i40e_rx_ring_stat;
554 	mintr->mi_handle = (mac_intr_handle_t)itrq;
555 	mintr->mi_enable = i40e_rx_ring_intr_enable;
556 	mintr->mi_disable = i40e_rx_ring_intr_disable;
557 
558 	/*
559 	 * We only provide the handle in cases where we have MSI-X interrupts,
560 	 * to indicate that we'd actually support retargetting.
561 	 */
562 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
563 		mintr->mi_ddi_handle =
564 		    i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
565 	}
566 }
567 
568 /* ARGSUSED */
569 static void
570 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
571     mac_group_info_t *infop, mac_group_handle_t gh)
572 {
573 	i40e_t *i40e = arg;
574 	i40e_rx_group_t *rxg;
575 
576 	if (rtype != MAC_RING_TYPE_RX)
577 		return;
578 
579 	rxg = &i40e->i40e_rx_groups[index];
580 	rxg->irg_grp_hdl = gh;
581 
582 	infop->mgi_driver = (mac_group_driver_t)rxg;
583 	infop->mgi_start = NULL;
584 	infop->mgi_stop = NULL;
585 	infop->mgi_addmac = i40e_group_add_mac;
586 	infop->mgi_remmac = i40e_group_remove_mac;
587 
588 	ASSERT3U(i40e->i40e_num_rx_groups, <=, I40E_MAX_NUM_RX_GROUPS);
589 	infop->mgi_count = i40e->i40e_num_trqpairs_per_vsi;
590 }
591 
592 static int
593 i40e_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
594 {
595 	boolean_t present, usable;
596 	i40e_t *i40e = arg;
597 
598 	if (id != 0 || infop == NULL)
599 		return (EINVAL);
600 
601 	mutex_enter(&i40e->i40e_general_lock);
602 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
603 	case I40E_MODULE_TYPE_SFP:
604 	case I40E_MODULE_TYPE_QSFP:
605 		break;
606 	default:
607 		mutex_exit(&i40e->i40e_general_lock);
608 		return (ENOTSUP);
609 	}
610 
611 	present = !!(i40e->i40e_hw_space.phy.link_info.link_info &
612 	    I40E_AQ_MEDIA_AVAILABLE);
613 	if (present) {
614 		usable = !!(i40e->i40e_hw_space.phy.link_info.an_info &
615 		    I40E_AQ_QUALIFIED_MODULE);
616 	} else {
617 		usable = B_FALSE;
618 	}
619 	mutex_exit(&i40e->i40e_general_lock);
620 
621 	mac_transceiver_info_set_usable(infop, usable);
622 	mac_transceiver_info_set_present(infop, present);
623 
624 	return (0);
625 }
626 
627 static int
628 i40e_transceiver_read(void *arg, uint_t id, uint_t page, void *buf,
629     size_t nbytes, off_t offset, size_t *nread)
630 {
631 	i40e_t *i40e = arg;
632 	struct i40e_hw *hw = &i40e->i40e_hw_space;
633 	uint8_t *buf8 = buf;
634 	size_t i;
635 
636 	if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
637 	    (page != 0xa0 && page != 0xa2) || offset < 0)
638 		return (EINVAL);
639 
640 	/*
641 	 * Both supported pages have a length of 256 bytes, ensure nothing asks
642 	 * us to go beyond that.
643 	 */
644 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
645 		return (EINVAL);
646 	}
647 
648 	mutex_enter(&i40e->i40e_general_lock);
649 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
650 	case I40E_MODULE_TYPE_SFP:
651 	case I40E_MODULE_TYPE_QSFP:
652 		break;
653 	default:
654 		mutex_exit(&i40e->i40e_general_lock);
655 		return (ENOTSUP);
656 	}
657 
658 	/*
659 	 * Make sure we have a sufficiently new firmware version to run this
660 	 * command. This was introduced in firmware API 1.7. This is apparently
661 	 * only supported on the XL710 MAC, not the XL722.
662 	 */
663 	if (hw->mac.type != I40E_MAC_XL710 || hw->aq.api_maj_ver != 1 ||
664 	    hw->aq.api_min_ver < 7) {
665 		mutex_exit(&i40e->i40e_general_lock);
666 		return (ENOTSUP);
667 	}
668 
669 	for (i = 0; i < nbytes; i++, offset++) {
670 		enum i40e_status_code status;
671 		uint32_t val;
672 
673 		status = i40e_aq_get_phy_register(hw,
674 		    I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, page, TRUE, offset,
675 		    &val, NULL);
676 		if (status != I40E_SUCCESS) {
677 			mutex_exit(&i40e->i40e_general_lock);
678 			return (EIO);
679 		}
680 
681 		buf8[i] = (uint8_t)val;
682 	}
683 
684 	mutex_exit(&i40e->i40e_general_lock);
685 	*nread = nbytes;
686 
687 	return (0);
688 }
689 
690 static int
691 i40e_gld_led_set(void *arg, mac_led_mode_t mode, uint_t flags)
692 {
693 	i40e_t *i40e = arg;
694 	struct i40e_hw *hw = &i40e->i40e_hw_space;
695 
696 	if (flags != 0)
697 		return (EINVAL);
698 
699 	if (mode != MAC_LED_DEFAULT &&
700 	    mode != MAC_LED_IDENT &&
701 	    mode != MAC_LED_OFF &&
702 	    mode != MAC_LED_ON)
703 		return (ENOTSUP);
704 
705 	if (mode != MAC_LED_DEFAULT && !i40e->i40e_led_saved) {
706 		i40e->i40e_led_status = i40e_led_get(hw);
707 		i40e->i40e_led_saved = B_TRUE;
708 	}
709 
710 	switch (mode) {
711 	case MAC_LED_DEFAULT:
712 		if (i40e->i40e_led_saved) {
713 			i40e_led_set(hw, i40e->i40e_led_status, B_FALSE);
714 			i40e->i40e_led_status = 0;
715 			i40e->i40e_led_saved = B_FALSE;
716 		}
717 		break;
718 	case MAC_LED_IDENT:
719 		i40e_led_set(hw, 0xf, B_TRUE);
720 		break;
721 	case MAC_LED_OFF:
722 		i40e_led_set(hw, 0x0, B_FALSE);
723 		break;
724 	case MAC_LED_ON:
725 		i40e_led_set(hw, 0xf, B_FALSE);
726 		break;
727 	default:
728 		return (ENOTSUP);
729 	}
730 
731 	return (0);
732 }
733 
734 static boolean_t
735 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
736 {
737 	i40e_t *i40e = arg;
738 	mac_capab_rings_t *cap_rings;
739 	mac_capab_transceiver_t *mct;
740 	mac_capab_led_t *mcl;
741 
742 	switch (cap) {
743 	case MAC_CAPAB_HCKSUM: {
744 		uint32_t *txflags = cap_data;
745 
746 		*txflags = 0;
747 		if (i40e->i40e_tx_hcksum_enable == B_TRUE)
748 			*txflags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
749 		break;
750 	}
751 
752 	case MAC_CAPAB_LSO: {
753 		mac_capab_lso_t *cap_lso = cap_data;
754 
755 		if (i40e->i40e_tx_lso_enable == B_TRUE) {
756 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4 |
757 			    LSO_TX_BASIC_TCP_IPV6;
758 			cap_lso->lso_basic_tcp_ipv4.lso_max = I40E_LSO_MAXLEN;
759 			cap_lso->lso_basic_tcp_ipv6.lso_max = I40E_LSO_MAXLEN;
760 		} else {
761 			return (B_FALSE);
762 		}
763 		break;
764 	}
765 
766 	case MAC_CAPAB_RINGS:
767 		cap_rings = cap_data;
768 		cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
769 		switch (cap_rings->mr_type) {
770 		case MAC_RING_TYPE_TX:
771 			/*
772 			 * Note, saying we have no groups, but some
773 			 * number of rings indicates to MAC that it
774 			 * should create psuedo-groups with one for
775 			 * each TX ring. This may not be the long term
776 			 * behavior we want, but it'll work for now.
777 			 */
778 			cap_rings->mr_gnum = 0;
779 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs_per_vsi;
780 			cap_rings->mr_rget = i40e_fill_tx_ring;
781 			cap_rings->mr_gget = NULL;
782 			cap_rings->mr_gaddring = NULL;
783 			cap_rings->mr_gremring = NULL;
784 			break;
785 		case MAC_RING_TYPE_RX:
786 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
787 			cap_rings->mr_rget = i40e_fill_rx_ring;
788 			cap_rings->mr_gnum = i40e->i40e_num_rx_groups;
789 			cap_rings->mr_gget = i40e_fill_rx_group;
790 			cap_rings->mr_gaddring = NULL;
791 			cap_rings->mr_gremring = NULL;
792 			break;
793 		default:
794 			return (B_FALSE);
795 		}
796 		break;
797 	case MAC_CAPAB_TRANSCEIVER:
798 		mct = cap_data;
799 
800 		/*
801 		 * Firmware doesn't have a great way of telling us in advance
802 		 * whether we'd expect a SFF transceiver. As such, we always
803 		 * advertise the support for this capability.
804 		 */
805 		mct->mct_flags = 0;
806 		mct->mct_ntransceivers = 1;
807 		mct->mct_info = i40e_transceiver_info;
808 		mct->mct_read = i40e_transceiver_read;
809 
810 		return (B_TRUE);
811 	case MAC_CAPAB_LED:
812 		mcl = cap_data;
813 
814 		mcl->mcl_flags = 0;
815 		mcl->mcl_modes = MAC_LED_DEFAULT | MAC_LED_IDENT | MAC_LED_OFF |
816 		    MAC_LED_ON;
817 		mcl->mcl_set = i40e_gld_led_set;
818 		break;
819 
820 	default:
821 		return (B_FALSE);
822 	}
823 
824 	return (B_TRUE);
825 }
826 
827 /* ARGSUSED */
828 static int
829 i40e_m_setprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
830     const void *pr_val)
831 {
832 	int ret;
833 	long val;
834 	char *eptr;
835 
836 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
837 
838 	if ((ret = ddi_strtol(pr_val, &eptr, 10, &val)) != 0 ||
839 	    *eptr != '\0') {
840 		return (ret);
841 	}
842 
843 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
844 		if (val < I40E_MIN_RX_DMA_THRESH ||
845 		    val > I40E_MAX_RX_DMA_THRESH) {
846 			return (EINVAL);
847 		}
848 		i40e->i40e_rx_dma_min = (uint32_t)val;
849 		return (0);
850 	}
851 
852 	if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
853 		if (val < I40E_MIN_TX_DMA_THRESH ||
854 		    val > I40E_MAX_TX_DMA_THRESH) {
855 			return (EINVAL);
856 		}
857 		i40e->i40e_tx_dma_min = (uint32_t)val;
858 		return (0);
859 	}
860 
861 	if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
862 		if (val < I40E_MIN_ITR ||
863 		    val > I40E_MAX_ITR) {
864 			return (EINVAL);
865 		}
866 		i40e->i40e_rx_itr = (uint32_t)val;
867 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
868 		return (0);
869 	}
870 
871 	if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
872 		if (val < I40E_MIN_ITR ||
873 		    val > I40E_MAX_ITR) {
874 			return (EINVAL);
875 		}
876 		i40e->i40e_tx_itr = (uint32_t)val;
877 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
878 		return (0);
879 	}
880 
881 	if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
882 		if (val < I40E_MIN_ITR ||
883 		    val > I40E_MAX_ITR) {
884 			return (EINVAL);
885 		}
886 		i40e->i40e_tx_itr = (uint32_t)val;
887 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER,
888 		    i40e->i40e_other_itr);
889 		return (0);
890 	}
891 
892 	return (ENOTSUP);
893 }
894 
895 static int
896 i40e_m_getprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
897     void *pr_val)
898 {
899 	uint32_t val;
900 
901 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
902 
903 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
904 		val = i40e->i40e_rx_dma_min;
905 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
906 		val = i40e->i40e_tx_dma_min;
907 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
908 		val = i40e->i40e_rx_itr;
909 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
910 		val = i40e->i40e_tx_itr;
911 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
912 		val = i40e->i40e_other_itr;
913 	} else {
914 		return (ENOTSUP);
915 	}
916 
917 	if (snprintf(pr_val, pr_valsize, "%d", val) >= pr_valsize)
918 		return (ERANGE);
919 	return (0);
920 }
921 
922 /*
923  * Annoyingly for private properties MAC seems to ignore default values that
924  * aren't strings. That means that we have to translate all of these into
925  * uint32_t's and instead we size the buffer to be large enough to hold a
926  * uint32_t.
927  */
928 /* ARGSUSED */
929 static void
930 i40e_m_propinfo_private(i40e_t *i40e, const char *pr_name,
931     mac_prop_info_handle_t prh)
932 {
933 	char buf[64];
934 	uint32_t def;
935 
936 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
937 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
938 		def = I40E_DEF_RX_DMA_THRESH;
939 		mac_prop_info_set_range_uint32(prh,
940 		    I40E_MIN_RX_DMA_THRESH,
941 		    I40E_MAX_RX_DMA_THRESH);
942 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
943 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
944 		def = I40E_DEF_TX_DMA_THRESH;
945 		mac_prop_info_set_range_uint32(prh,
946 		    I40E_MIN_TX_DMA_THRESH,
947 		    I40E_MAX_TX_DMA_THRESH);
948 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
949 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
950 		def = I40E_DEF_RX_ITR;
951 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
952 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
953 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
954 		def = I40E_DEF_TX_ITR;
955 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
956 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
957 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
958 		def = I40E_DEF_OTHER_ITR;
959 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
960 	} else {
961 		return;
962 	}
963 
964 	(void) snprintf(buf, sizeof (buf), "%d", def);
965 	mac_prop_info_set_default_str(prh, buf);
966 }
967 
968 static int
969 i40e_update_fec(i40e_t *i40e, link_fec_t fec)
970 {
971 	struct i40e_hw *hw = &i40e->i40e_hw_space;
972 	struct i40e_aq_get_phy_abilities_resp abilities;
973 	struct i40e_aq_set_phy_config config;
974 	link_fec_t fec_requested;
975 	int req_fec;
976 
977 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
978 
979 	if (fec == i40e->i40e_fec_requested)
980 		return (0);
981 
982 	fec_requested = fec;
983 	if ((fec & LINK_FEC_AUTO) != 0) {
984 		req_fec = I40E_AQ_SET_FEC_AUTO;
985 		fec &= ~LINK_FEC_AUTO;
986 	} else if ((fec & LINK_FEC_NONE) != 0) {
987 		req_fec = 0;
988 		fec &= ~LINK_FEC_NONE;
989 	} else {
990 		req_fec = 0;
991 		if ((fec & LINK_FEC_BASE_R) != 0) {
992 			req_fec |= I40E_AQ_SET_FEC_ABILITY_KR |
993 			    I40E_AQ_SET_FEC_REQUEST_KR;
994 			fec &= ~LINK_FEC_BASE_R;
995 		}
996 		if ((fec & LINK_FEC_RS) != 0) {
997 			req_fec |= I40E_AQ_SET_FEC_ABILITY_RS |
998 			    I40E_AQ_SET_FEC_REQUEST_RS;
999 			fec &= ~LINK_FEC_RS;
1000 		}
1001 		if (req_fec == 0)
1002 			return (EINVAL);
1003 	}
1004 
1005 	/*
1006 	 * if fec is not zero now, then there is an invalid fec or
1007 	 * combination of settings.
1008 	 */
1009 	if (fec != 0)
1010 		return (EINVAL);
1011 
1012 	if (i40e_aq_get_phy_capabilities(hw, B_FALSE, B_FALSE, &abilities,
1013 	    NULL) != I40E_SUCCESS)
1014 		return (EIO);
1015 
1016 	bzero(&config, sizeof (config));
1017 	config.abilities = abilities.abilities;
1018 	/* Restart the link */
1019 	config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
1020 	config.phy_type = abilities.phy_type;
1021 	config.phy_type_ext = abilities.phy_type_ext;
1022 	config.link_speed = abilities.link_speed;
1023 	config.eee_capability = abilities.eee_capability;
1024 	config.eeer = abilities.eeer_val;
1025 	config.low_power_ctrl = abilities.d3_lpan;
1026 	config.fec_config = req_fec & I40E_AQ_PHY_FEC_CONFIG_MASK;
1027 	if (i40e_aq_set_phy_config(hw, &config, NULL) != I40E_SUCCESS)
1028 		return (EIO);
1029 
1030 	if (i40e_update_link_info(hw) != I40E_SUCCESS)
1031 		return (EIO);
1032 
1033 	i40e->i40e_fec_requested = fec_requested;
1034 
1035 	return (0);
1036 }
1037 static int
1038 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1039     uint_t pr_valsize, const void *pr_val)
1040 {
1041 	uint32_t new_mtu;
1042 	link_fec_t fec;
1043 	i40e_t *i40e = arg;
1044 	int ret = 0;
1045 
1046 	mutex_enter(&i40e->i40e_general_lock);
1047 	if (i40e->i40e_state & I40E_SUSPENDED) {
1048 		mutex_exit(&i40e->i40e_general_lock);
1049 		return (ECANCELED);
1050 	}
1051 
1052 	switch (pr_num) {
1053 	/*
1054 	 * These properties are always read-only across every device.
1055 	 */
1056 	case MAC_PROP_DUPLEX:
1057 	case MAC_PROP_SPEED:
1058 	case MAC_PROP_STATUS:
1059 	case MAC_PROP_ADV_100FDX_CAP:
1060 	case MAC_PROP_ADV_1000FDX_CAP:
1061 	case MAC_PROP_ADV_2500FDX_CAP:
1062 	case MAC_PROP_ADV_5000FDX_CAP:
1063 	case MAC_PROP_ADV_10GFDX_CAP:
1064 	case MAC_PROP_ADV_25GFDX_CAP:
1065 	case MAC_PROP_ADV_40GFDX_CAP:
1066 		ret = ENOTSUP;
1067 		break;
1068 	/*
1069 	 * These are read-only at this time as we don't support configuring
1070 	 * auto-negotiation. See the theory statement in i40e_main.c.
1071 	 */
1072 	case MAC_PROP_EN_100FDX_CAP:
1073 	case MAC_PROP_EN_1000FDX_CAP:
1074 	case MAC_PROP_EN_2500FDX_CAP:
1075 	case MAC_PROP_EN_5000FDX_CAP:
1076 	case MAC_PROP_EN_10GFDX_CAP:
1077 	case MAC_PROP_EN_25GFDX_CAP:
1078 	case MAC_PROP_EN_40GFDX_CAP:
1079 	case MAC_PROP_AUTONEG:
1080 	case MAC_PROP_FLOWCTRL:
1081 		ret = ENOTSUP;
1082 		break;
1083 
1084 	case MAC_PROP_MTU:
1085 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
1086 		if (new_mtu == i40e->i40e_sdu)
1087 			break;
1088 
1089 		if (new_mtu < I40E_MIN_MTU ||
1090 		    new_mtu > I40E_MAX_MTU) {
1091 			ret = EINVAL;
1092 			break;
1093 		}
1094 
1095 		if (i40e->i40e_state & I40E_STARTED) {
1096 			ret = EBUSY;
1097 			break;
1098 		}
1099 
1100 		ret = mac_maxsdu_update(i40e->i40e_mac_hdl, new_mtu);
1101 		if (ret == 0) {
1102 			i40e->i40e_sdu = new_mtu;
1103 			i40e_update_mtu(i40e);
1104 		}
1105 		break;
1106 
1107 	case MAC_PROP_EN_FEC_CAP:
1108 		bcopy(pr_val, &fec, sizeof (fec));
1109 
1110 		ret = i40e_update_fec(i40e, fec);
1111 		break;
1112 
1113 	case MAC_PROP_PRIVATE:
1114 		ret = i40e_m_setprop_private(i40e, pr_name, pr_valsize, pr_val);
1115 		break;
1116 	default:
1117 		ret = ENOTSUP;
1118 		break;
1119 	}
1120 
1121 	mutex_exit(&i40e->i40e_general_lock);
1122 	return (ret);
1123 }
1124 
1125 static link_fec_t
1126 i40e_fec_to_linkfec(struct i40e_hw *hw)
1127 {
1128 	struct i40e_link_status *ls = &hw->phy.link_info;
1129 
1130 	if ((ls->fec_info & I40E_AQ_CONFIG_FEC_KR_ENA) != 0)
1131 		return (LINK_FEC_BASE_R);
1132 
1133 	if ((ls->fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) != 0)
1134 		return (LINK_FEC_RS);
1135 
1136 	return (LINK_FEC_NONE);
1137 }
1138 
1139 static int
1140 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1141     uint_t pr_valsize, void *pr_val)
1142 {
1143 	i40e_t *i40e = arg;
1144 	uint64_t speed;
1145 	int ret = 0;
1146 	uint8_t *u8;
1147 	link_flowctrl_t fctl;
1148 
1149 	mutex_enter(&i40e->i40e_general_lock);
1150 
1151 	switch (pr_num) {
1152 	case MAC_PROP_DUPLEX:
1153 		if (pr_valsize < sizeof (link_duplex_t)) {
1154 			ret = EOVERFLOW;
1155 			break;
1156 		}
1157 		bcopy(&i40e->i40e_link_duplex, pr_val, sizeof (link_duplex_t));
1158 		break;
1159 	case MAC_PROP_SPEED:
1160 		if (pr_valsize < sizeof (uint64_t)) {
1161 			ret = EOVERFLOW;
1162 			break;
1163 		}
1164 		speed = i40e->i40e_link_speed * 1000000ULL;
1165 		bcopy(&speed, pr_val, sizeof (speed));
1166 		break;
1167 	case MAC_PROP_STATUS:
1168 		if (pr_valsize < sizeof (link_state_t)) {
1169 			ret = EOVERFLOW;
1170 			break;
1171 		}
1172 		bcopy(&i40e->i40e_link_state, pr_val, sizeof (link_state_t));
1173 		break;
1174 	case MAC_PROP_AUTONEG:
1175 		if (pr_valsize < sizeof (uint8_t)) {
1176 			ret = EOVERFLOW;
1177 			break;
1178 		}
1179 		u8 = pr_val;
1180 		*u8 = 1;
1181 		break;
1182 	case MAC_PROP_FLOWCTRL:
1183 		/*
1184 		 * Because we don't currently support hardware flow control, we
1185 		 * just hardcode this to be none.
1186 		 */
1187 		if (pr_valsize < sizeof (link_flowctrl_t)) {
1188 			ret = EOVERFLOW;
1189 			break;
1190 		}
1191 		fctl = LINK_FLOWCTRL_NONE;
1192 		bcopy(&fctl, pr_val, sizeof (link_flowctrl_t));
1193 		break;
1194 	case MAC_PROP_MTU:
1195 		if (pr_valsize < sizeof (uint32_t)) {
1196 			ret = EOVERFLOW;
1197 			break;
1198 		}
1199 		bcopy(&i40e->i40e_sdu, pr_val, sizeof (uint32_t));
1200 		break;
1201 	case MAC_PROP_ADV_FEC_CAP:
1202 		if (pr_valsize < sizeof (link_fec_t)) {
1203 			ret = EOVERFLOW;
1204 			break;
1205 		}
1206 		*(link_fec_t *)pr_val =
1207 		    i40e_fec_to_linkfec(&i40e->i40e_hw_space);
1208 		break;
1209 	case MAC_PROP_EN_FEC_CAP:
1210 		if (pr_valsize < sizeof (link_fec_t)) {
1211 			ret = EOVERFLOW;
1212 			break;
1213 		}
1214 		*(link_fec_t *)pr_val = i40e->i40e_fec_requested;
1215 		break;
1216 
1217 	/*
1218 	 * Because we don't let users control the speeds we may auto-negotiate
1219 	 * to, the values of the ADV_ and EN_ will always be the same.
1220 	 */
1221 	case MAC_PROP_ADV_100FDX_CAP:
1222 	case MAC_PROP_EN_100FDX_CAP:
1223 		if (pr_valsize < sizeof (uint8_t)) {
1224 			ret = EOVERFLOW;
1225 			break;
1226 		}
1227 		u8 = pr_val;
1228 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0;
1229 		break;
1230 	case MAC_PROP_ADV_1000FDX_CAP:
1231 	case MAC_PROP_EN_1000FDX_CAP:
1232 		if (pr_valsize < sizeof (uint8_t)) {
1233 			ret = EOVERFLOW;
1234 			break;
1235 		}
1236 		u8 = pr_val;
1237 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0;
1238 		break;
1239 	case MAC_PROP_ADV_2500FDX_CAP:
1240 	case MAC_PROP_EN_2500FDX_CAP:
1241 		if (pr_valsize < sizeof (uint8_t)) {
1242 			ret = EOVERFLOW;
1243 			break;
1244 		}
1245 		u8 = pr_val;
1246 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_2_5GB) != 0;
1247 		break;
1248 	case MAC_PROP_ADV_5000FDX_CAP:
1249 	case MAC_PROP_EN_5000FDX_CAP:
1250 		if (pr_valsize < sizeof (uint8_t)) {
1251 			ret = EOVERFLOW;
1252 			break;
1253 		}
1254 		u8 = pr_val;
1255 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_5GB) != 0;
1256 		break;
1257 	case MAC_PROP_ADV_10GFDX_CAP:
1258 	case MAC_PROP_EN_10GFDX_CAP:
1259 		if (pr_valsize < sizeof (uint8_t)) {
1260 			ret = EOVERFLOW;
1261 			break;
1262 		}
1263 		u8 = pr_val;
1264 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0;
1265 		break;
1266 	case MAC_PROP_ADV_25GFDX_CAP:
1267 	case MAC_PROP_EN_25GFDX_CAP:
1268 		if (pr_valsize < sizeof (uint8_t)) {
1269 			ret = EOVERFLOW;
1270 			break;
1271 		}
1272 		u8 = pr_val;
1273 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0;
1274 		break;
1275 	case MAC_PROP_ADV_40GFDX_CAP:
1276 	case MAC_PROP_EN_40GFDX_CAP:
1277 		if (pr_valsize < sizeof (uint8_t)) {
1278 			ret = EOVERFLOW;
1279 			break;
1280 		}
1281 		u8 = pr_val;
1282 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0;
1283 		break;
1284 	case MAC_PROP_PRIVATE:
1285 		ret = i40e_m_getprop_private(i40e, pr_name, pr_valsize, pr_val);
1286 		break;
1287 	default:
1288 		ret = ENOTSUP;
1289 		break;
1290 	}
1291 
1292 	mutex_exit(&i40e->i40e_general_lock);
1293 
1294 	return (ret);
1295 }
1296 
1297 static void
1298 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1299     mac_prop_info_handle_t prh)
1300 {
1301 	i40e_t *i40e = arg;
1302 
1303 	mutex_enter(&i40e->i40e_general_lock);
1304 
1305 	switch (pr_num) {
1306 	case MAC_PROP_DUPLEX:
1307 	case MAC_PROP_SPEED:
1308 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1309 		break;
1310 	case MAC_PROP_FLOWCTRL:
1311 		/*
1312 		 * At the moment, the driver doesn't support flow control, hence
1313 		 * why this is set to read-only and none.
1314 		 */
1315 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1316 		mac_prop_info_set_default_link_flowctrl(prh,
1317 		    LINK_FLOWCTRL_NONE);
1318 		break;
1319 	case MAC_PROP_MTU:
1320 		mac_prop_info_set_range_uint32(prh, I40E_MIN_MTU, I40E_MAX_MTU);
1321 		break;
1322 	case MAC_PROP_ADV_FEC_CAP:
1323 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1324 		if (i40e_is_25G_device(i40e->i40e_hw_space.device_id))
1325 			mac_prop_info_set_default_fec(prh, LINK_FEC_AUTO);
1326 		break;
1327 	case MAC_PROP_EN_FEC_CAP:
1328 		if (i40e_is_25G_device(i40e->i40e_hw_space.device_id)) {
1329 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1330 			mac_prop_info_set_default_fec(prh, LINK_FEC_AUTO);
1331 		} else {
1332 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1333 		}
1334 		break;
1335 
1336 	/*
1337 	 * We set the defaults for these based upon the phy's ability to
1338 	 * support the speeds. Note, auto-negotiation is required for fiber,
1339 	 * hence it is read-only and always enabled. When we have access to
1340 	 * copper phys we can revisit this.
1341 	 */
1342 	case MAC_PROP_AUTONEG:
1343 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1344 		mac_prop_info_set_default_uint8(prh, 1);
1345 		break;
1346 	case MAC_PROP_ADV_100FDX_CAP:
1347 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1348 		mac_prop_info_set_default_uint8(prh,
1349 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1350 		break;
1351 	case MAC_PROP_EN_100FDX_CAP:
1352 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1353 		mac_prop_info_set_default_uint8(prh,
1354 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1355 		break;
1356 	case MAC_PROP_ADV_1000FDX_CAP:
1357 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1358 		mac_prop_info_set_default_uint8(prh,
1359 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1360 		break;
1361 	case MAC_PROP_EN_1000FDX_CAP:
1362 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1363 		mac_prop_info_set_default_uint8(prh,
1364 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1365 		break;
1366 	case MAC_PROP_ADV_10GFDX_CAP:
1367 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1368 		mac_prop_info_set_default_uint8(prh,
1369 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1370 		break;
1371 	case MAC_PROP_EN_10GFDX_CAP:
1372 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1373 		mac_prop_info_set_default_uint8(prh,
1374 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1375 		break;
1376 	case MAC_PROP_ADV_25GFDX_CAP:
1377 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1378 		mac_prop_info_set_default_uint8(prh,
1379 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1380 		break;
1381 	case MAC_PROP_EN_25GFDX_CAP:
1382 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1383 		mac_prop_info_set_default_uint8(prh,
1384 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1385 		break;
1386 	case MAC_PROP_ADV_40GFDX_CAP:
1387 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1388 		mac_prop_info_set_default_uint8(prh,
1389 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1390 		break;
1391 	case MAC_PROP_EN_40GFDX_CAP:
1392 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1393 		mac_prop_info_set_default_uint8(prh,
1394 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1395 		break;
1396 	case MAC_PROP_PRIVATE:
1397 		i40e_m_propinfo_private(i40e, pr_name, prh);
1398 		break;
1399 	default:
1400 		break;
1401 	}
1402 
1403 	mutex_exit(&i40e->i40e_general_lock);
1404 }
1405 
1406 #define	I40E_M_CALLBACK_FLAGS \
1407 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
1408 
1409 static mac_callbacks_t i40e_m_callbacks = {
1410 	I40E_M_CALLBACK_FLAGS,
1411 	i40e_m_stat,
1412 	i40e_m_start,
1413 	i40e_m_stop,
1414 	i40e_m_promisc,
1415 	i40e_m_multicast,
1416 	NULL,
1417 	NULL,
1418 	NULL,
1419 	i40e_m_ioctl,
1420 	i40e_m_getcapab,
1421 	NULL,
1422 	NULL,
1423 	i40e_m_setprop,
1424 	i40e_m_getprop,
1425 	i40e_m_propinfo
1426 };
1427 
1428 boolean_t
1429 i40e_register_mac(i40e_t *i40e)
1430 {
1431 	struct i40e_hw *hw = &i40e->i40e_hw_space;
1432 	int status;
1433 	mac_register_t *mac = mac_alloc(MAC_VERSION);
1434 
1435 	if (mac == NULL)
1436 		return (B_FALSE);
1437 
1438 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1439 	mac->m_driver = i40e;
1440 	mac->m_dip = i40e->i40e_dip;
1441 	mac->m_src_addr = hw->mac.addr;
1442 	mac->m_callbacks = &i40e_m_callbacks;
1443 	mac->m_min_sdu = 0;
1444 	mac->m_max_sdu = i40e->i40e_sdu;
1445 	mac->m_margin = VLAN_TAGSZ;
1446 	mac->m_priv_props = i40e_priv_props;
1447 	mac->m_v12n = MAC_VIRT_LEVEL1;
1448 
1449 	status = mac_register(mac, &i40e->i40e_mac_hdl);
1450 	if (status != 0)
1451 		i40e_error(i40e, "mac_register() returned %d", status);
1452 	mac_free(mac);
1453 
1454 	return (status == 0);
1455 }
1456