The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/mlx5/mlx5_core/mlx5_fwdump.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2018, 2019 Mellanox Technologies, Ltd.  All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
   14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  */
   25 
   26 #include "opt_rss.h"
   27 #include "opt_ratelimit.h"
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/conf.h>
   35 #include <sys/fcntl.h>
   36 #include <dev/mlx5/driver.h>
   37 #include <dev/mlx5/device.h>
   38 #include <dev/mlx5/port.h>
   39 #include <dev/mlx5/mlx5_core/mlx5_core.h>
   40 #include <dev/mlx5/mlx5io.h>
   41 #include <dev/mlx5/diagnostics.h>
   42 
   43 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
   44 
   45 static unsigned
   46 mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
   47 {
   48         const struct mlx5_crspace_regmap *r;
   49         unsigned sz;
   50 
   51         for (sz = 0, r = rege; r->cnt != 0; r++)
   52                 sz += r->cnt;
   53         return (sz);
   54 }
   55 
   56 static void
   57 mlx5_fwdump_destroy_dd(struct mlx5_core_dev *mdev)
   58 {
   59 
   60         mtx_assert(&mdev->dump_lock, MA_OWNED);
   61         free(mdev->dump_data, M_MLX5_DUMP);
   62         mdev->dump_data = NULL;
   63 }
   64 
   65 static int mlx5_fw_dump_enable = 1;
   66 SYSCTL_INT(_hw_mlx5, OID_AUTO, fw_dump_enable, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
   67     &mlx5_fw_dump_enable, 0,
   68     "Enable fw dump setup and op");
   69 
   70 void
   71 mlx5_fwdump_prep(struct mlx5_core_dev *mdev)
   72 {
   73         device_t dev;
   74         int error, vsc_addr;
   75         unsigned i, sz;
   76         u32 addr, in, out, next_addr;
   77 
   78         mdev->dump_data = NULL;
   79 
   80         TUNABLE_INT_FETCH("hw.mlx5.fw_dump_enable", &mlx5_fw_dump_enable);
   81         if (!mlx5_fw_dump_enable) {
   82                 mlx5_core_warn(mdev,
   83                     "Firmware dump administratively prohibited\n");
   84                 return;
   85         }
   86 
   87         DROP_GIANT();
   88 
   89         error = mlx5_vsc_find_cap(mdev);
   90         if (error != 0) {
   91                 /* Inability to create a firmware dump is not fatal. */
   92                 mlx5_core_warn(mdev,
   93                     "Unable to find vendor-specific capability, error %d\n",
   94                     error);
   95                 goto pickup_g;
   96         }
   97         error = mlx5_vsc_lock(mdev);
   98         if (error != 0)
   99                 goto pickup_g;
  100         error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SCAN_CRSPACE);
  101         if (error != 0) {
  102                 mlx5_core_warn(mdev, "VSC scan space is not supported\n");
  103                 goto unlock_vsc;
  104         }
  105         dev = mdev->pdev->dev.bsddev;
  106         vsc_addr = mdev->vsc_addr;
  107         if (vsc_addr == 0) {
  108                 mlx5_core_warn(mdev, "Cannot read VSC, no address\n");
  109                 goto unlock_vsc;
  110         }
  111 
  112         in = 0;
  113         for (sz = 1, addr = 0;;) {
  114                 MLX5_VSC_SET(vsc_addr, &in, address, addr);
  115                 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
  116                 error = mlx5_vsc_wait_on_flag(mdev, 1);
  117                 if (error != 0) {
  118                         mlx5_core_warn(mdev,
  119                     "Failed waiting for read complete flag, error %d addr %#x\n",
  120                             error, addr);
  121                         goto unlock_vsc;
  122                 }
  123                 pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
  124                 out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
  125                 next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
  126                 if (next_addr == 0 || next_addr == addr)
  127                         break;
  128                 if (next_addr != addr + 4)
  129                         sz++;
  130                 addr = next_addr;
  131         }
  132         if (sz == 1) {
  133                 mlx5_core_warn(mdev, "no output from scan space\n");
  134                 goto unlock_vsc;
  135         }
  136 
  137         /*
  138          * We add a sentinel element at the end of the array to
  139          * terminate the read loop in mlx5_fwdump(), so allocate sz + 1.
  140          */
  141         mdev->dump_rege = malloc((sz + 1) * sizeof(struct mlx5_crspace_regmap),
  142             M_MLX5_DUMP, M_WAITOK | M_ZERO);
  143 
  144         for (i = 0, addr = 0;;) {
  145                 mdev->dump_rege[i].cnt++;
  146                 MLX5_VSC_SET(vsc_addr, &in, address, addr);
  147                 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
  148                 error = mlx5_vsc_wait_on_flag(mdev, 1);
  149                 if (error != 0) {
  150                         mlx5_core_warn(mdev,
  151                     "Failed waiting for read complete flag, error %d addr %#x\n",
  152                             error, addr);
  153                         free(mdev->dump_rege, M_MLX5_DUMP);
  154                         mdev->dump_rege = NULL;
  155                         goto unlock_vsc;
  156                 }
  157                 pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
  158                 out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
  159                 next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
  160                 if (next_addr == 0 || next_addr == addr)
  161                         break;
  162                 if (next_addr != addr + 4) {
  163                         if (++i == sz) {
  164                                 mlx5_core_err(mdev,
  165                     "Inconsistent hw crspace reads (1): sz %u i %u addr %#lx",
  166                                     sz, i, (unsigned long)addr);
  167                                 break;
  168                         }
  169                         mdev->dump_rege[i].addr = next_addr;
  170                 }
  171                 addr = next_addr;
  172         }
  173         /* i == sz case already reported by loop above */
  174         if (i + 1 != sz && i != sz) {
  175                 mlx5_core_err(mdev,
  176                     "Inconsistent hw crspace reads (2): sz %u i %u addr %#lx",
  177                     sz, i, (unsigned long)addr);
  178         }
  179 
  180         mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
  181         mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
  182             M_MLX5_DUMP, M_WAITOK | M_ZERO);
  183         mdev->dump_valid = false;
  184         mdev->dump_copyout = false;
  185 
  186 unlock_vsc:
  187         mlx5_vsc_unlock(mdev);
  188 pickup_g:
  189         PICKUP_GIANT();
  190 }
  191 
  192 int
  193 mlx5_fwdump(struct mlx5_core_dev *mdev)
  194 {
  195         const struct mlx5_crspace_regmap *r;
  196         uint32_t i, ri;
  197         int error;
  198 
  199         mlx5_core_info(mdev, "Issuing FW dump\n");
  200         mtx_lock(&mdev->dump_lock);
  201         if (mdev->dump_data == NULL) {
  202                 error = EIO;
  203                 goto failed;
  204         }
  205         if (mdev->dump_valid) {
  206                 /* only one dump */
  207                 mlx5_core_warn(mdev,
  208                     "Only one FW dump can be captured aborting FW dump\n");
  209                 error = EEXIST;
  210                 goto failed;
  211         }
  212 
  213         /* mlx5_vsc already warns, be silent. */
  214         error = mlx5_vsc_lock(mdev);
  215         if (error != 0)
  216                 goto failed;
  217         error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
  218         if (error != 0)
  219                 goto unlock_vsc;
  220         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
  221                 for (ri = 0; ri < r->cnt; ri++) {
  222                         error = mlx5_vsc_read(mdev, r->addr + ri * 4,
  223                             &mdev->dump_data[i]);
  224                         if (error != 0)
  225                                 goto unlock_vsc;
  226                         i++;
  227                 }
  228         }
  229         mdev->dump_valid = true;
  230 unlock_vsc:
  231         mlx5_vsc_unlock(mdev);
  232 failed:
  233         mtx_unlock(&mdev->dump_lock);
  234         return (error);
  235 }
  236 
  237 void
  238 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
  239 {
  240 
  241         mtx_lock(&mdev->dump_lock);
  242         while (mdev->dump_copyout)
  243                 msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
  244         mlx5_fwdump_destroy_dd(mdev);
  245         mtx_unlock(&mdev->dump_lock);
  246         free(mdev->dump_rege, M_MLX5_DUMP);
  247 }
  248 
  249 static int
  250 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
  251 {
  252         int error;
  253 
  254         error = 0;
  255         mtx_lock(&mdev->dump_lock);
  256         if (mdev->dump_data != NULL) {
  257                 while (mdev->dump_copyout) {
  258                         msleep(&mdev->dump_copyout, &mdev->dump_lock,
  259                             0, "mlx5fwr", 0);
  260                 }
  261                 mdev->dump_valid = false;
  262         } else {
  263                 error = ENOENT;
  264         }
  265         mtx_unlock(&mdev->dump_lock);
  266         return (error);
  267 }
  268 
  269 static int
  270 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
  271     struct mlx5_core_dev **mdev)
  272 {
  273         device_t dev;
  274         struct pci_dev *pdev;
  275 
  276         dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
  277             devaddr->func);
  278         if (dev == NULL)
  279                 return (ENOENT);
  280         if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
  281                 return (EINVAL);
  282         pdev = device_get_softc(dev);
  283         *mdev = pci_get_drvdata(pdev);
  284         if (*mdev == NULL)
  285                 return (ENOENT);
  286         return (0);
  287 }
  288 
  289 static int
  290 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
  291 {
  292         const struct mlx5_crspace_regmap *r;
  293         struct mlx5_fwdump_reg rv, *urv;
  294         uint32_t i, ri;
  295         int error;
  296 
  297         mtx_lock(&mdev->dump_lock);
  298         if (mdev->dump_data == NULL) {
  299                 mtx_unlock(&mdev->dump_lock);
  300                 return (ENOENT);
  301         }
  302         if (fwg->buf == NULL) {
  303                 fwg->reg_filled = mdev->dump_size;
  304                 mtx_unlock(&mdev->dump_lock);
  305                 return (0);
  306         }
  307         if (!mdev->dump_valid) {
  308                 mtx_unlock(&mdev->dump_lock);
  309                 return (ENOENT);
  310         }
  311         mdev->dump_copyout = true;
  312         mtx_unlock(&mdev->dump_lock);
  313 
  314         urv = fwg->buf;
  315         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
  316                 for (ri = 0; ri < r->cnt; ri++) {
  317                         if (i >= fwg->reg_cnt)
  318                                 goto out;
  319                         rv.addr = r->addr + ri * 4;
  320                         rv.val = mdev->dump_data[i];
  321                         error = copyout(&rv, urv, sizeof(rv));
  322                         if (error != 0)
  323                                 return (error);
  324                         urv++;
  325                         i++;
  326                 }
  327         }
  328 out:
  329         fwg->reg_filled = i;
  330         mtx_lock(&mdev->dump_lock);
  331         mdev->dump_copyout = false;
  332         wakeup(&mdev->dump_copyout);
  333         mtx_unlock(&mdev->dump_lock);
  334         return (0);
  335 }
  336 
  337 static int
  338 mlx5_fw_reset(struct mlx5_core_dev *mdev)
  339 {
  340         device_t dev, bus;
  341         int error;
  342 
  343         error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
  344         if (error == 0) {
  345                 dev = mdev->pdev->dev.bsddev;
  346                 bus_topo_lock();
  347                 bus = device_get_parent(dev);
  348                 error = BUS_RESET_CHILD(device_get_parent(bus), bus,
  349                     DEVF_RESET_DETACH);
  350                 bus_topo_unlock();
  351         }
  352         return (error);
  353 }
  354 
  355 static int
  356 mlx5_eeprom_copyout(struct mlx5_core_dev *dev, struct mlx5_eeprom_get *eeprom_info)
  357 {
  358         struct mlx5_eeprom eeprom;
  359         int error;
  360 
  361         eeprom.i2c_addr = MLX5_I2C_ADDR_LOW;
  362         eeprom.device_addr = 0;
  363         eeprom.page_num = MLX5_EEPROM_LOW_PAGE;
  364         eeprom.page_valid = 0;
  365 
  366         /* Read three first bytes to get important info */
  367         error = mlx5_get_eeprom_info(dev, &eeprom);
  368         if (error != 0) {
  369                 mlx5_core_err(dev,
  370                     "Failed reading EEPROM initial information\n");
  371                 return (error);
  372         }
  373         eeprom_info->eeprom_info_page_valid = eeprom.page_valid;
  374         eeprom_info->eeprom_info_out_len = eeprom.len;
  375 
  376         if (eeprom_info->eeprom_info_buf == NULL)
  377                 return (0);
  378         /*
  379          * Allocate needed length buffer and additional space for
  380          * page 0x03
  381          */
  382         eeprom.data = malloc(eeprom.len + MLX5_EEPROM_PAGE_LENGTH,
  383             M_MLX5_EEPROM, M_WAITOK | M_ZERO);
  384 
  385         /* Read the whole eeprom information */
  386         error = mlx5_get_eeprom(dev, &eeprom);
  387         if (error != 0) {
  388                 mlx5_core_err(dev, "Failed reading EEPROM error = %d\n",
  389                     error);
  390                 error = 0;
  391                 /*
  392                  * Continue printing partial information in case of
  393                  * an error
  394                  */
  395         }
  396         error = copyout(eeprom.data, eeprom_info->eeprom_info_buf,
  397             eeprom.len);
  398         free(eeprom.data, M_MLX5_EEPROM);
  399 
  400         return (error);
  401 }
  402 
  403 static int
  404 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
  405     struct thread *td)
  406 {
  407         struct mlx5_core_dev *mdev;
  408         struct mlx5_fwdump_get *fwg;
  409         struct mlx5_tool_addr *devaddr;
  410         struct mlx5_fw_update *fu;
  411         struct firmware fake_fw;
  412         struct mlx5_eeprom_get *eeprom_info;
  413         void *fw_data;
  414         int error;
  415 
  416         error = 0;
  417         switch (cmd) {
  418         case MLX5_FWDUMP_GET:
  419                 if ((fflag & FREAD) == 0) {
  420                         error = EBADF;
  421                         break;
  422                 }
  423                 fwg = (struct mlx5_fwdump_get *)data;
  424                 devaddr = &fwg->devaddr;
  425                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  426                 if (error != 0)
  427                         break;
  428                 error = mlx5_fwdump_copyout(mdev, fwg);
  429                 break;
  430         case MLX5_FWDUMP_RESET:
  431                 if ((fflag & FWRITE) == 0) {
  432                         error = EBADF;
  433                         break;
  434                 }
  435                 devaddr = (struct mlx5_tool_addr *)data;
  436                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  437                 if (error == 0)
  438                         error = mlx5_fwdump_reset(mdev);
  439                 break;
  440         case MLX5_FWDUMP_FORCE:
  441                 if ((fflag & FWRITE) == 0) {
  442                         error = EBADF;
  443                         break;
  444                 }
  445                 devaddr = (struct mlx5_tool_addr *)data;
  446                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  447                 if (error != 0)
  448                         break;
  449                 error = mlx5_fwdump(mdev);
  450                 break;
  451         case MLX5_FW_UPDATE:
  452                 if ((fflag & FWRITE) == 0) {
  453                         error = EBADF;
  454                         break;
  455                 }
  456                 fu = (struct mlx5_fw_update *)data;
  457                 if (fu->img_fw_data_len > 10 * 1024 * 1024) {
  458                         error = EINVAL;
  459                         break;
  460                 }
  461                 devaddr = &fu->devaddr;
  462                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  463                 if (error != 0)
  464                         break;
  465                 fw_data = kmem_malloc(fu->img_fw_data_len, M_WAITOK);
  466                 if (fake_fw.data == NULL) {
  467                         error = ENOMEM;
  468                         break;
  469                 }
  470                 error = copyin(fu->img_fw_data, fw_data, fu->img_fw_data_len);
  471                 if (error == 0) {
  472                         bzero(&fake_fw, sizeof(fake_fw));
  473                         fake_fw.name = "umlx_fw_up";
  474                         fake_fw.datasize = fu->img_fw_data_len;
  475                         fake_fw.version = 1;
  476                         fake_fw.data = fw_data;
  477                         error = -mlx5_firmware_flash(mdev, &fake_fw);
  478                 }
  479                 kmem_free(fw_data, fu->img_fw_data_len);
  480                 break;
  481         case MLX5_FW_RESET:
  482                 if ((fflag & FWRITE) == 0) {
  483                         error = EBADF;
  484                         break;
  485                 }
  486                 devaddr = (struct mlx5_tool_addr *)data;
  487                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  488                 if (error != 0)
  489                         break;
  490                 error = mlx5_fw_reset(mdev);
  491                 break;
  492         case MLX5_EEPROM_GET:
  493                 if ((fflag & FREAD) == 0) {
  494                         error = EBADF;
  495                         break;
  496                 }
  497                 eeprom_info = (struct mlx5_eeprom_get *)data;
  498                 devaddr = &eeprom_info->devaddr;
  499                 error = mlx5_dbsf_to_core(devaddr, &mdev);
  500                 if (error != 0)
  501                         break;
  502                 error = mlx5_eeprom_copyout(mdev, eeprom_info);
  503                 break;
  504         default:
  505                 error = ENOTTY;
  506                 break;
  507         }
  508         return (error);
  509 }
  510 
  511 static struct cdevsw mlx5_ctl_devsw = {
  512         .d_version =    D_VERSION,
  513         .d_ioctl =      mlx5_ctl_ioctl,
  514 };
  515 
  516 static struct cdev *mlx5_ctl_dev;
  517 
  518 int
  519 mlx5_ctl_init(void)
  520 {
  521         struct make_dev_args mda;
  522         int error;
  523 
  524         make_dev_args_init(&mda);
  525         mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
  526         mda.mda_devsw = &mlx5_ctl_devsw;
  527         mda.mda_uid = UID_ROOT;
  528         mda.mda_gid = GID_OPERATOR;
  529         mda.mda_mode = 0640;
  530         error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
  531         return (-error);
  532 }
  533 
  534 void
  535 mlx5_ctl_fini(void)
  536 {
  537 
  538         if (mlx5_ctl_dev != NULL)
  539                 destroy_dev(mlx5_ctl_dev);
  540 
  541 }

Cache object: 4e40d2511d69c45f6b9bb065a5d198fa


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.