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