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/contrib/dev/iwlwifi/fw/pnvm.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 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
    2 /*
    3  * Copyright(c) 2020-2021 Intel Corporation
    4  */
    5 
    6 #include "iwl-drv.h"
    7 #include "pnvm.h"
    8 #include "iwl-prph.h"
    9 #include "iwl-io.h"
   10 #include "fw/api/commands.h"
   11 #include "fw/api/nvm-reg.h"
   12 #include "fw/api/alive.h"
   13 #include "fw/uefi.h"
   14 
   15 struct iwl_pnvm_section {
   16         __le32 offset;
   17         const u8 data[];
   18 } __packed;
   19 
   20 static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
   21                                  struct iwl_rx_packet *pkt, void *data)
   22 {
   23         struct iwl_trans *trans = (struct iwl_trans *)data;
   24         struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
   25 
   26         IWL_DEBUG_FW(trans,
   27                      "PNVM complete notification received with status 0x%0x\n",
   28                      le32_to_cpu(pnvm_ntf->status));
   29 
   30         return true;
   31 }
   32 
   33 static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
   34                                    size_t len)
   35 {
   36         const struct iwl_ucode_tlv *tlv;
   37         u32 sha1 = 0;
   38         u16 mac_type = 0, rf_id = 0;
   39         u8 *pnvm_data = NULL, *tmp;
   40         bool hw_match = false;
   41         u32 size = 0;
   42         int ret;
   43 
   44         IWL_DEBUG_FW(trans, "Handling PNVM section\n");
   45 
   46         while (len >= sizeof(*tlv)) {
   47                 u32 tlv_len, tlv_type;
   48 
   49                 len -= sizeof(*tlv);
   50                 tlv = (const void *)data;
   51 
   52                 tlv_len = le32_to_cpu(tlv->length);
   53                 tlv_type = le32_to_cpu(tlv->type);
   54 
   55                 if (len < tlv_len) {
   56                         IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
   57                                 len, tlv_len);
   58                         ret = -EINVAL;
   59                         goto out;
   60                 }
   61 
   62                 data += sizeof(*tlv);
   63 
   64                 switch (tlv_type) {
   65                 case IWL_UCODE_TLV_PNVM_VERSION:
   66                         if (tlv_len < sizeof(__le32)) {
   67                                 IWL_DEBUG_FW(trans,
   68                                              "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
   69                                              sizeof(__le32), tlv_len);
   70                                 break;
   71                         }
   72 
   73                         sha1 = le32_to_cpup((const __le32 *)data);
   74 
   75                         IWL_DEBUG_FW(trans,
   76                                      "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
   77                                      sha1);
   78                         break;
   79                 case IWL_UCODE_TLV_HW_TYPE:
   80                         if (tlv_len < 2 * sizeof(__le16)) {
   81                                 IWL_DEBUG_FW(trans,
   82                                              "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
   83                                              2 * sizeof(__le16), tlv_len);
   84                                 break;
   85                         }
   86 
   87                         if (hw_match)
   88                                 break;
   89 
   90                         mac_type = le16_to_cpup((const __le16 *)data);
   91                         rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16)));
   92 
   93                         IWL_DEBUG_FW(trans,
   94                                      "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
   95                                      mac_type, rf_id);
   96 
   97                         if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) &&
   98                             rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id))
   99                                 hw_match = true;
  100                         break;
  101                 case IWL_UCODE_TLV_SEC_RT: {
  102                         const struct iwl_pnvm_section *section = (const void *)data;
  103                         u32 data_len = tlv_len - sizeof(*section);
  104 
  105                         IWL_DEBUG_FW(trans,
  106                                      "Got IWL_UCODE_TLV_SEC_RT len %d\n",
  107                                      tlv_len);
  108 
  109                         /* TODO: remove, this is a deprecated separator */
  110                         if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) {
  111                                 IWL_DEBUG_FW(trans, "Ignoring separator.\n");
  112                                 break;
  113                         }
  114 
  115                         IWL_DEBUG_FW(trans, "Adding data (size %d)\n",
  116                                      data_len);
  117 
  118                         tmp = krealloc(pnvm_data, size + data_len, GFP_KERNEL);
  119                         if (!tmp) {
  120                                 IWL_DEBUG_FW(trans,
  121                                              "Couldn't allocate (more) pnvm_data\n");
  122 
  123                                 ret = -ENOMEM;
  124                                 goto out;
  125                         }
  126 
  127                         pnvm_data = tmp;
  128 
  129                         memcpy(pnvm_data + size, section->data, data_len);
  130 
  131                         size += data_len;
  132 
  133                         break;
  134                 }
  135                 case IWL_UCODE_TLV_PNVM_SKU:
  136                         IWL_DEBUG_FW(trans,
  137                                      "New PNVM section started, stop parsing.\n");
  138                         goto done;
  139                 default:
  140                         IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
  141                                      tlv_type, tlv_len);
  142                         break;
  143                 }
  144 
  145                 len -= ALIGN(tlv_len, 4);
  146                 data += ALIGN(tlv_len, 4);
  147         }
  148 
  149 done:
  150         if (!hw_match) {
  151                 IWL_DEBUG_FW(trans,
  152                              "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",
  153                              CSR_HW_REV_TYPE(trans->hw_rev),
  154                              CSR_HW_RFID_TYPE(trans->hw_rf_id));
  155                 ret = -ENOENT;
  156                 goto out;
  157         }
  158 
  159         if (!size) {
  160                 IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n");
  161                 ret = -ENOENT;
  162                 goto out;
  163         }
  164 
  165         IWL_INFO(trans, "loaded PNVM version %08x\n", sha1);
  166 
  167         ret = iwl_trans_set_pnvm(trans, pnvm_data, size);
  168 out:
  169         kfree(pnvm_data);
  170         return ret;
  171 }
  172 
  173 static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
  174                           size_t len)
  175 {
  176         const struct iwl_ucode_tlv *tlv;
  177 
  178         IWL_DEBUG_FW(trans, "Parsing PNVM file\n");
  179 
  180         while (len >= sizeof(*tlv)) {
  181                 u32 tlv_len, tlv_type;
  182 
  183                 len -= sizeof(*tlv);
  184                 tlv = (const void *)data;
  185 
  186                 tlv_len = le32_to_cpu(tlv->length);
  187                 tlv_type = le32_to_cpu(tlv->type);
  188 
  189                 if (len < tlv_len) {
  190                         IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
  191                                 len, tlv_len);
  192                         return -EINVAL;
  193                 }
  194 
  195                 if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
  196                         const struct iwl_sku_id *sku_id =
  197                                 (const void *)(data + sizeof(*tlv));
  198 
  199                         IWL_DEBUG_FW(trans,
  200                                      "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
  201                                      tlv_len);
  202                         IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
  203                                      le32_to_cpu(sku_id->data[0]),
  204                                      le32_to_cpu(sku_id->data[1]),
  205                                      le32_to_cpu(sku_id->data[2]));
  206 
  207                         data += sizeof(*tlv) + ALIGN(tlv_len, 4);
  208                         len -= ALIGN(tlv_len, 4);
  209 
  210                         if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
  211                             trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
  212                             trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
  213                                 int ret;
  214 
  215                                 ret = iwl_pnvm_handle_section(trans, data, len);
  216                                 if (!ret)
  217                                         return 0;
  218                         } else {
  219                                 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
  220                         }
  221                 } else {
  222                         data += sizeof(*tlv) + ALIGN(tlv_len, 4);
  223                         len -= ALIGN(tlv_len, 4);
  224                 }
  225         }
  226 
  227         return -ENOENT;
  228 }
  229 
  230 static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
  231 {
  232         const struct firmware *pnvm;
  233         char pnvm_name[MAX_PNVM_NAME];
  234         size_t new_len;
  235         int ret;
  236 
  237         iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
  238 
  239         ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
  240         if (ret) {
  241                 IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
  242                              pnvm_name, ret);
  243                 return ret;
  244         }
  245 
  246         new_len = pnvm->size;
  247         *data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
  248         release_firmware(pnvm);
  249 
  250         if (!*data)
  251                 return -ENOMEM;
  252 
  253         *len = new_len;
  254 
  255         return 0;
  256 }
  257 
  258 int iwl_pnvm_load(struct iwl_trans *trans,
  259                   struct iwl_notif_wait_data *notif_wait)
  260 {
  261         u8 *data;
  262         size_t len;
  263         struct pnvm_sku_package *package;
  264         struct iwl_notification_wait pnvm_wait;
  265         static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
  266                                                 PNVM_INIT_COMPLETE_NTFY) };
  267         int ret;
  268 
  269         /* if the SKU_ID is empty, there's nothing to do */
  270         if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
  271                 return 0;
  272 
  273         /*
  274          * If we already loaded (or tried to load) it before, we just
  275          * need to set it again.
  276          */
  277         if (trans->pnvm_loaded) {
  278                 ret = iwl_trans_set_pnvm(trans, NULL, 0);
  279                 if (ret)
  280                         return ret;
  281                 goto skip_parse;
  282         }
  283 
  284         /* First attempt to get the PNVM from BIOS */
  285         package = iwl_uefi_get_pnvm(trans, &len);
  286         if (!IS_ERR_OR_NULL(package)) {
  287                 if (len >= sizeof(*package)) {
  288                         /* we need only the data */
  289                         len -= sizeof(*package);
  290                         data = kmemdup(package->data, len, GFP_KERNEL);
  291                 } else {
  292                         data = NULL;
  293                 }
  294 
  295                 /* free package regardless of whether kmemdup succeeded */
  296                 kfree(package);
  297 
  298                 if (data)
  299                         goto parse;
  300         }
  301 
  302         /* If it's not available, try from the filesystem */
  303         ret = iwl_pnvm_get_from_fs(trans, &data, &len);
  304         if (ret) {
  305                 /*
  306                  * Pretend we've loaded it - at least we've tried and
  307                  * couldn't load it at all, so there's no point in
  308                  * trying again over and over.
  309                  */
  310                 trans->pnvm_loaded = true;
  311 
  312                 goto skip_parse;
  313         }
  314 
  315 parse:
  316         iwl_pnvm_parse(trans, data, len);
  317 
  318         kfree(data);
  319 
  320 skip_parse:
  321         data = NULL;
  322         /* now try to get the reduce power table, if not loaded yet */
  323         if (!trans->reduce_power_loaded) {
  324                 data = iwl_uefi_get_reduced_power(trans, &len);
  325                 if (IS_ERR_OR_NULL(data)) {
  326                         /*
  327                          * Pretend we've loaded it - at least we've tried and
  328                          * couldn't load it at all, so there's no point in
  329                          * trying again over and over.
  330                          */
  331                         trans->reduce_power_loaded = true;
  332 
  333                         goto skip_reduce_power;
  334                 }
  335         }
  336 
  337         ret = iwl_trans_set_reduce_power(trans, data, len);
  338         if (ret)
  339                 IWL_DEBUG_FW(trans,
  340                              "Failed to set reduce power table %d\n",
  341                              ret);
  342         kfree(data);
  343 
  344 skip_reduce_power:
  345         iwl_init_notification_wait(notif_wait, &pnvm_wait,
  346                                    ntf_cmds, ARRAY_SIZE(ntf_cmds),
  347                                    iwl_pnvm_complete_fn, trans);
  348 
  349         /* kick the doorbell */
  350         iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
  351                             UREG_DOORBELL_TO_ISR6_PNVM);
  352 
  353         return iwl_wait_notification(notif_wait, &pnvm_wait,
  354                                      MVM_UCODE_PNVM_TIMEOUT);
  355 }
  356 IWL_EXPORT_SYMBOL(iwl_pnvm_load);

Cache object: bd423f197def8e68b1a3e9a6eea283eb


[ 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.