Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright(c) 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 
0011 #include "fw/uefi.h"
0012 #include "fw/api/alive.h"
0013 #include <linux/efi.h>
0014 #include "fw/runtime.h"
0015 
0016 #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,   \
0017                   0xb2, 0xec, 0xf5, 0xa3,   \
0018                   0x59, 0x4f, 0x4a, 0xea)
0019 
0020 void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
0021 {
0022     void *data;
0023     unsigned long package_size;
0024     efi_status_t status;
0025 
0026     *len = 0;
0027 
0028     if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
0029         return ERR_PTR(-ENODEV);
0030 
0031     /*
0032      * TODO: we hardcode a maximum length here, because reading
0033      * from the UEFI is not working.  To implement this properly,
0034      * we have to call efivar_entry_size().
0035      */
0036     package_size = IWL_HARDCODED_PNVM_SIZE;
0037 
0038     data = kmalloc(package_size, GFP_KERNEL);
0039     if (!data)
0040         return ERR_PTR(-ENOMEM);
0041 
0042     status = efi.get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID,
0043                   NULL, &package_size, data);
0044     if (status != EFI_SUCCESS) {
0045         IWL_DEBUG_FW(trans,
0046                  "PNVM UEFI variable not found 0x%lx (len %lu)\n",
0047                  status, package_size);
0048         kfree(data);
0049         return ERR_PTR(-ENOENT);
0050     }
0051 
0052     IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
0053     *len = package_size;
0054 
0055     return data;
0056 }
0057 
0058 static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
0059                        const u8 *data, size_t len)
0060 {
0061     const struct iwl_ucode_tlv *tlv;
0062     u8 *reduce_power_data = NULL, *tmp;
0063     u32 size = 0;
0064 
0065     IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
0066 
0067     while (len >= sizeof(*tlv)) {
0068         u32 tlv_len, tlv_type;
0069 
0070         len -= sizeof(*tlv);
0071         tlv = (const void *)data;
0072 
0073         tlv_len = le32_to_cpu(tlv->length);
0074         tlv_type = le32_to_cpu(tlv->type);
0075 
0076         if (len < tlv_len) {
0077             IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
0078                 len, tlv_len);
0079             kfree(reduce_power_data);
0080             reduce_power_data = ERR_PTR(-EINVAL);
0081             goto out;
0082         }
0083 
0084         data += sizeof(*tlv);
0085 
0086         switch (tlv_type) {
0087         case IWL_UCODE_TLV_MEM_DESC: {
0088             IWL_DEBUG_FW(trans,
0089                      "Got IWL_UCODE_TLV_MEM_DESC len %d\n",
0090                      tlv_len);
0091 
0092             IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len);
0093 
0094             tmp = krealloc(reduce_power_data, size + tlv_len, GFP_KERNEL);
0095             if (!tmp) {
0096                 IWL_DEBUG_FW(trans,
0097                          "Couldn't allocate (more) reduce_power_data\n");
0098 
0099                 kfree(reduce_power_data);
0100                 reduce_power_data = ERR_PTR(-ENOMEM);
0101                 goto out;
0102             }
0103 
0104             reduce_power_data = tmp;
0105 
0106             memcpy(reduce_power_data + size, data, tlv_len);
0107 
0108             size += tlv_len;
0109 
0110             break;
0111         }
0112         case IWL_UCODE_TLV_PNVM_SKU:
0113             IWL_DEBUG_FW(trans,
0114                      "New REDUCE_POWER section started, stop parsing.\n");
0115             goto done;
0116         default:
0117             IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
0118                      tlv_type, tlv_len);
0119             break;
0120         }
0121 
0122         len -= ALIGN(tlv_len, 4);
0123         data += ALIGN(tlv_len, 4);
0124     }
0125 
0126 done:
0127     if (!size) {
0128         IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
0129         /* Better safe than sorry, but 'reduce_power_data' should
0130          * always be NULL if !size.
0131          */
0132         kfree(reduce_power_data);
0133         reduce_power_data = ERR_PTR(-ENOENT);
0134         goto out;
0135     }
0136 
0137     IWL_INFO(trans, "loaded REDUCE_POWER\n");
0138 
0139 out:
0140     return reduce_power_data;
0141 }
0142 
0143 static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
0144                      const u8 *data, size_t len)
0145 {
0146     const struct iwl_ucode_tlv *tlv;
0147     void *sec_data;
0148 
0149     IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
0150 
0151     while (len >= sizeof(*tlv)) {
0152         u32 tlv_len, tlv_type;
0153 
0154         len -= sizeof(*tlv);
0155         tlv = (const void *)data;
0156 
0157         tlv_len = le32_to_cpu(tlv->length);
0158         tlv_type = le32_to_cpu(tlv->type);
0159 
0160         if (len < tlv_len) {
0161             IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
0162                 len, tlv_len);
0163             return ERR_PTR(-EINVAL);
0164         }
0165 
0166         if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
0167             const struct iwl_sku_id *sku_id =
0168                 (const void *)(data + sizeof(*tlv));
0169 
0170             IWL_DEBUG_FW(trans,
0171                      "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
0172                      tlv_len);
0173             IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
0174                      le32_to_cpu(sku_id->data[0]),
0175                      le32_to_cpu(sku_id->data[1]),
0176                      le32_to_cpu(sku_id->data[2]));
0177 
0178             data += sizeof(*tlv) + ALIGN(tlv_len, 4);
0179             len -= ALIGN(tlv_len, 4);
0180 
0181             if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
0182                 trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
0183                 trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
0184                 sec_data = iwl_uefi_reduce_power_section(trans,
0185                                      data,
0186                                      len);
0187                 if (!IS_ERR(sec_data))
0188                     return sec_data;
0189             } else {
0190                 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
0191             }
0192         } else {
0193             data += sizeof(*tlv) + ALIGN(tlv_len, 4);
0194             len -= ALIGN(tlv_len, 4);
0195         }
0196     }
0197 
0198     return ERR_PTR(-ENOENT);
0199 }
0200 
0201 void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
0202 {
0203     struct pnvm_sku_package *package;
0204     void *data = NULL;
0205     unsigned long package_size;
0206     efi_status_t status;
0207 
0208     *len = 0;
0209 
0210     if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
0211         return ERR_PTR(-ENODEV);
0212 
0213     /*
0214      * TODO: we hardcode a maximum length here, because reading
0215      * from the UEFI is not working.  To implement this properly,
0216      * we have to call efivar_entry_size().
0217      */
0218     package_size = IWL_HARDCODED_REDUCE_POWER_SIZE;
0219 
0220     package = kmalloc(package_size, GFP_KERNEL);
0221     if (!package)
0222         return ERR_PTR(-ENOMEM);
0223 
0224     status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID,
0225                   NULL, &package_size, data);
0226     if (status != EFI_SUCCESS) {
0227         IWL_DEBUG_FW(trans,
0228                  "Reduced Power UEFI variable not found 0x%lx (len %lu)\n",
0229                  status, package_size);
0230         kfree(package);
0231         return ERR_PTR(-ENOENT);
0232     }
0233 
0234     IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
0235              package_size);
0236     *len = package_size;
0237 
0238     IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
0239              package->rev, package->total_size, package->n_skus);
0240 
0241     data = iwl_uefi_reduce_power_parse(trans, package->data,
0242                        *len - sizeof(*package));
0243 
0244     kfree(package);
0245 
0246     return data;
0247 }
0248 
0249 #ifdef CONFIG_ACPI
0250 static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
0251                    struct iwl_fw_runtime *fwrt)
0252 {
0253     int i, j;
0254 
0255     if (sgom_data->revision != 1)
0256         return -EINVAL;
0257 
0258     memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
0259            sizeof(fwrt->sgom_table.offset_map));
0260 
0261     for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
0262         for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
0263             /* since each byte is composed of to values, */
0264             /* one for each letter, */
0265             /* extract and check each of them separately */
0266             u8 value = fwrt->sgom_table.offset_map[i][j];
0267             u8 low = value & 0xF;
0268             u8 high = (value & 0xF0) >> 4;
0269 
0270             if (high > fwrt->geo_num_profiles)
0271                 high = 0;
0272             if (low > fwrt->geo_num_profiles)
0273                 low = 0;
0274             fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
0275         }
0276     }
0277 
0278     fwrt->sgom_enabled = true;
0279     return 0;
0280 }
0281 
0282 void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
0283                  struct iwl_fw_runtime *fwrt)
0284 {
0285     struct uefi_cnv_wlan_sgom_data *data;
0286     unsigned long package_size;
0287     efi_status_t status;
0288     int ret;
0289 
0290     if (!fwrt->geo_enabled ||
0291         !efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
0292         return;
0293 
0294     /* TODO: we hardcode a maximum length here, because reading
0295      * from the UEFI is not working.  To implement this properly,
0296      * we have to call efivar_entry_size().
0297      */
0298     package_size = IWL_HARDCODED_SGOM_SIZE;
0299 
0300     data = kmalloc(package_size, GFP_KERNEL);
0301     if (!data)
0302         return;
0303 
0304     status = efi.get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID,
0305                   NULL, &package_size, data);
0306     if (status != EFI_SUCCESS) {
0307         IWL_DEBUG_FW(trans,
0308                  "SGOM UEFI variable not found 0x%lx\n", status);
0309         goto out_free;
0310     }
0311 
0312     IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n",
0313              package_size);
0314 
0315     ret = iwl_uefi_sgom_parse(data, fwrt);
0316     if (ret < 0)
0317         IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
0318 
0319 out_free:
0320     kfree(data);
0321 
0322 }
0323 IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
0324 #endif /* CONFIG_ACPI */