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