Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright(c) 2020-2021 Intel Corporation
0004  */
0005 
0006 #include "iwl-drv.h"
0007 #include "pnvm.h"
0008 #include "iwl-prph.h"
0009 #include "iwl-io.h"
0010 #include "fw/api/commands.h"
0011 #include "fw/api/nvm-reg.h"
0012 #include "fw/api/alive.h"
0013 #include "fw/uefi.h"
0014 
0015 struct iwl_pnvm_section {
0016     __le32 offset;
0017     const u8 data[];
0018 } __packed;
0019 
0020 static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
0021                  struct iwl_rx_packet *pkt, void *data)
0022 {
0023     struct iwl_trans *trans = (struct iwl_trans *)data;
0024     struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
0025 
0026     IWL_DEBUG_FW(trans,
0027              "PNVM complete notification received with status 0x%0x\n",
0028              le32_to_cpu(pnvm_ntf->status));
0029 
0030     return true;
0031 }
0032 
0033 static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
0034                    size_t len)
0035 {
0036     const struct iwl_ucode_tlv *tlv;
0037     u32 sha1 = 0;
0038     u16 mac_type = 0, rf_id = 0;
0039     u8 *pnvm_data = NULL, *tmp;
0040     bool hw_match = false;
0041     u32 size = 0;
0042     int ret;
0043 
0044     IWL_DEBUG_FW(trans, "Handling PNVM section\n");
0045 
0046     while (len >= sizeof(*tlv)) {
0047         u32 tlv_len, tlv_type;
0048 
0049         len -= sizeof(*tlv);
0050         tlv = (const void *)data;
0051 
0052         tlv_len = le32_to_cpu(tlv->length);
0053         tlv_type = le32_to_cpu(tlv->type);
0054 
0055         if (len < tlv_len) {
0056             IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
0057                 len, tlv_len);
0058             ret = -EINVAL;
0059             goto out;
0060         }
0061 
0062         data += sizeof(*tlv);
0063 
0064         switch (tlv_type) {
0065         case IWL_UCODE_TLV_PNVM_VERSION:
0066             if (tlv_len < sizeof(__le32)) {
0067                 IWL_DEBUG_FW(trans,
0068                          "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
0069                          sizeof(__le32), tlv_len);
0070                 break;
0071             }
0072 
0073             sha1 = le32_to_cpup((const __le32 *)data);
0074 
0075             IWL_DEBUG_FW(trans,
0076                      "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
0077                      sha1);
0078             break;
0079         case IWL_UCODE_TLV_HW_TYPE:
0080             if (tlv_len < 2 * sizeof(__le16)) {
0081                 IWL_DEBUG_FW(trans,
0082                          "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
0083                          2 * sizeof(__le16), tlv_len);
0084                 break;
0085             }
0086 
0087             if (hw_match)
0088                 break;
0089 
0090             mac_type = le16_to_cpup((const __le16 *)data);
0091             rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16)));
0092 
0093             IWL_DEBUG_FW(trans,
0094                      "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
0095                      mac_type, rf_id);
0096 
0097             if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) &&
0098                 rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id))
0099                 hw_match = true;
0100             break;
0101         case IWL_UCODE_TLV_SEC_RT: {
0102             const struct iwl_pnvm_section *section = (const void *)data;
0103             u32 data_len = tlv_len - sizeof(*section);
0104 
0105             IWL_DEBUG_FW(trans,
0106                      "Got IWL_UCODE_TLV_SEC_RT len %d\n",
0107                      tlv_len);
0108 
0109             /* TODO: remove, this is a deprecated separator */
0110             if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) {
0111                 IWL_DEBUG_FW(trans, "Ignoring separator.\n");
0112                 break;
0113             }
0114 
0115             IWL_DEBUG_FW(trans, "Adding data (size %d)\n",
0116                      data_len);
0117 
0118             tmp = krealloc(pnvm_data, size + data_len, GFP_KERNEL);
0119             if (!tmp) {
0120                 IWL_DEBUG_FW(trans,
0121                          "Couldn't allocate (more) pnvm_data\n");
0122 
0123                 ret = -ENOMEM;
0124                 goto out;
0125             }
0126 
0127             pnvm_data = tmp;
0128 
0129             memcpy(pnvm_data + size, section->data, data_len);
0130 
0131             size += data_len;
0132 
0133             break;
0134         }
0135         case IWL_UCODE_TLV_PNVM_SKU:
0136             IWL_DEBUG_FW(trans,
0137                      "New PNVM section started, stop parsing.\n");
0138             goto done;
0139         default:
0140             IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
0141                      tlv_type, tlv_len);
0142             break;
0143         }
0144 
0145         len -= ALIGN(tlv_len, 4);
0146         data += ALIGN(tlv_len, 4);
0147     }
0148 
0149 done:
0150     if (!hw_match) {
0151         IWL_DEBUG_FW(trans,
0152                  "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",
0153                  CSR_HW_REV_TYPE(trans->hw_rev),
0154                  CSR_HW_RFID_TYPE(trans->hw_rf_id));
0155         ret = -ENOENT;
0156         goto out;
0157     }
0158 
0159     if (!size) {
0160         IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n");
0161         ret = -ENOENT;
0162         goto out;
0163     }
0164 
0165     IWL_INFO(trans, "loaded PNVM version %08x\n", sha1);
0166 
0167     ret = iwl_trans_set_pnvm(trans, pnvm_data, size);
0168 out:
0169     kfree(pnvm_data);
0170     return ret;
0171 }
0172 
0173 static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
0174               size_t len)
0175 {
0176     const struct iwl_ucode_tlv *tlv;
0177 
0178     IWL_DEBUG_FW(trans, "Parsing PNVM file\n");
0179 
0180     while (len >= sizeof(*tlv)) {
0181         u32 tlv_len, tlv_type;
0182 
0183         len -= sizeof(*tlv);
0184         tlv = (const void *)data;
0185 
0186         tlv_len = le32_to_cpu(tlv->length);
0187         tlv_type = le32_to_cpu(tlv->type);
0188 
0189         if (len < tlv_len) {
0190             IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
0191                 len, tlv_len);
0192             return -EINVAL;
0193         }
0194 
0195         if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
0196             const struct iwl_sku_id *sku_id =
0197                 (const void *)(data + sizeof(*tlv));
0198 
0199             IWL_DEBUG_FW(trans,
0200                      "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
0201                      tlv_len);
0202             IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
0203                      le32_to_cpu(sku_id->data[0]),
0204                      le32_to_cpu(sku_id->data[1]),
0205                      le32_to_cpu(sku_id->data[2]));
0206 
0207             data += sizeof(*tlv) + ALIGN(tlv_len, 4);
0208             len -= ALIGN(tlv_len, 4);
0209 
0210             if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
0211                 trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
0212                 trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
0213                 int ret;
0214 
0215                 ret = iwl_pnvm_handle_section(trans, data, len);
0216                 if (!ret)
0217                     return 0;
0218             } else {
0219                 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
0220             }
0221         } else {
0222             data += sizeof(*tlv) + ALIGN(tlv_len, 4);
0223             len -= ALIGN(tlv_len, 4);
0224         }
0225     }
0226 
0227     return -ENOENT;
0228 }
0229 
0230 static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
0231 {
0232     const struct firmware *pnvm;
0233     char pnvm_name[MAX_PNVM_NAME];
0234     size_t new_len;
0235     int ret;
0236 
0237     iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
0238 
0239     ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
0240     if (ret) {
0241         IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
0242                  pnvm_name, ret);
0243         return ret;
0244     }
0245 
0246     new_len = pnvm->size;
0247     *data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
0248     release_firmware(pnvm);
0249 
0250     if (!*data)
0251         return -ENOMEM;
0252 
0253     *len = new_len;
0254 
0255     return 0;
0256 }
0257 
0258 int iwl_pnvm_load(struct iwl_trans *trans,
0259           struct iwl_notif_wait_data *notif_wait)
0260 {
0261     u8 *data;
0262     size_t len;
0263     struct pnvm_sku_package *package;
0264     struct iwl_notification_wait pnvm_wait;
0265     static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
0266                         PNVM_INIT_COMPLETE_NTFY) };
0267     int ret;
0268 
0269     /* if the SKU_ID is empty, there's nothing to do */
0270     if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
0271         return 0;
0272 
0273     /*
0274      * If we already loaded (or tried to load) it before, we just
0275      * need to set it again.
0276      */
0277     if (trans->pnvm_loaded) {
0278         ret = iwl_trans_set_pnvm(trans, NULL, 0);
0279         if (ret)
0280             return ret;
0281         goto skip_parse;
0282     }
0283 
0284     /* First attempt to get the PNVM from BIOS */
0285     package = iwl_uefi_get_pnvm(trans, &len);
0286     if (!IS_ERR_OR_NULL(package)) {
0287         if (len >= sizeof(*package)) {
0288             /* we need only the data */
0289             len -= sizeof(*package);
0290             data = kmemdup(package->data, len, GFP_KERNEL);
0291         } else {
0292             data = NULL;
0293         }
0294 
0295         /* free package regardless of whether kmemdup succeeded */
0296         kfree(package);
0297 
0298         if (data)
0299             goto parse;
0300     }
0301 
0302     /* If it's not available, try from the filesystem */
0303     ret = iwl_pnvm_get_from_fs(trans, &data, &len);
0304     if (ret) {
0305         /*
0306          * Pretend we've loaded it - at least we've tried and
0307          * couldn't load it at all, so there's no point in
0308          * trying again over and over.
0309          */
0310         trans->pnvm_loaded = true;
0311 
0312         goto skip_parse;
0313     }
0314 
0315 parse:
0316     iwl_pnvm_parse(trans, data, len);
0317 
0318     kfree(data);
0319 
0320 skip_parse:
0321     data = NULL;
0322     /* now try to get the reduce power table, if not loaded yet */
0323     if (!trans->reduce_power_loaded) {
0324         data = iwl_uefi_get_reduced_power(trans, &len);
0325         if (IS_ERR_OR_NULL(data)) {
0326             /*
0327              * Pretend we've loaded it - at least we've tried and
0328              * couldn't load it at all, so there's no point in
0329              * trying again over and over.
0330              */
0331             trans->reduce_power_loaded = true;
0332 
0333             goto skip_reduce_power;
0334         }
0335     }
0336 
0337     ret = iwl_trans_set_reduce_power(trans, data, len);
0338     if (ret)
0339         IWL_DEBUG_FW(trans,
0340                  "Failed to set reduce power table %d\n",
0341                  ret);
0342     kfree(data);
0343 
0344 skip_reduce_power:
0345     iwl_init_notification_wait(notif_wait, &pnvm_wait,
0346                    ntf_cmds, ARRAY_SIZE(ntf_cmds),
0347                    iwl_pnvm_complete_fn, trans);
0348 
0349     /* kick the doorbell */
0350     iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
0351                 UREG_DOORBELL_TO_ISR6_PNVM);
0352 
0353     return iwl_wait_notification(notif_wait, &pnvm_wait,
0354                      MVM_UCODE_PNVM_TIMEOUT);
0355 }
0356 IWL_EXPORT_SYMBOL(iwl_pnvm_load);