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