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/uefi.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) 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 
   11 #include "fw/uefi.h"
   12 #include "fw/api/alive.h"
   13 #include <linux/efi.h>
   14 #include "fw/runtime.h"
   15 
   16 #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,   \
   17                                   0xb2, 0xec, 0xf5, 0xa3,       \
   18                                   0x59, 0x4f, 0x4a, 0xea)
   19 
   20 void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
   21 {
   22         struct efivar_entry *pnvm_efivar;
   23         void *data;
   24         unsigned long package_size;
   25         int err;
   26 
   27         *len = 0;
   28 
   29         pnvm_efivar = kzalloc(sizeof(*pnvm_efivar), GFP_KERNEL);
   30         if (!pnvm_efivar)
   31                 return ERR_PTR(-ENOMEM);
   32 
   33         memcpy(&pnvm_efivar->var.VariableName, IWL_UEFI_OEM_PNVM_NAME,
   34                sizeof(IWL_UEFI_OEM_PNVM_NAME));
   35         pnvm_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
   36 
   37         /*
   38          * TODO: we hardcode a maximum length here, because reading
   39          * from the UEFI is not working.  To implement this properly,
   40          * we have to call efivar_entry_size().
   41          */
   42         package_size = IWL_HARDCODED_PNVM_SIZE;
   43 
   44         data = kmalloc(package_size, GFP_KERNEL);
   45         if (!data) {
   46                 data = ERR_PTR(-ENOMEM);
   47                 goto out;
   48         }
   49 
   50         err = efivar_entry_get(pnvm_efivar, NULL, &package_size, data);
   51         if (err) {
   52                 IWL_DEBUG_FW(trans,
   53                              "PNVM UEFI variable not found %d (len %lu)\n",
   54                              err, package_size);
   55                 kfree(data);
   56                 data = ERR_PTR(err);
   57                 goto out;
   58         }
   59 
   60         IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
   61         *len = package_size;
   62 
   63 out:
   64         kfree(pnvm_efivar);
   65 
   66         return data;
   67 }
   68 
   69 static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
   70                                            const u8 *data, size_t len)
   71 {
   72         const struct iwl_ucode_tlv *tlv;
   73         u8 *reduce_power_data = NULL, *tmp;
   74         u32 size = 0;
   75 
   76         IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
   77 
   78         while (len >= sizeof(*tlv)) {
   79                 u32 tlv_len, tlv_type;
   80 
   81                 len -= sizeof(*tlv);
   82                 tlv = (const void *)data;
   83 
   84                 tlv_len = le32_to_cpu(tlv->length);
   85                 tlv_type = le32_to_cpu(tlv->type);
   86 
   87                 if (len < tlv_len) {
   88                         IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
   89                                 len, tlv_len);
   90                         kfree(reduce_power_data);
   91                         reduce_power_data = ERR_PTR(-EINVAL);
   92                         goto out;
   93                 }
   94 
   95                 data += sizeof(*tlv);
   96 
   97                 switch (tlv_type) {
   98                 case IWL_UCODE_TLV_MEM_DESC: {
   99                         IWL_DEBUG_FW(trans,
  100                                      "Got IWL_UCODE_TLV_MEM_DESC len %d\n",
  101                                      tlv_len);
  102 
  103                         IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len);
  104 
  105                         tmp = krealloc(reduce_power_data, size + tlv_len, GFP_KERNEL);
  106                         if (!tmp) {
  107                                 IWL_DEBUG_FW(trans,
  108                                              "Couldn't allocate (more) reduce_power_data\n");
  109 
  110                                 kfree(reduce_power_data);
  111                                 reduce_power_data = ERR_PTR(-ENOMEM);
  112                                 goto out;
  113                         }
  114 
  115                         reduce_power_data = tmp;
  116 
  117                         memcpy(reduce_power_data + size, data, tlv_len);
  118 
  119                         size += tlv_len;
  120 
  121                         break;
  122                 }
  123                 case IWL_UCODE_TLV_PNVM_SKU:
  124                         IWL_DEBUG_FW(trans,
  125                                      "New REDUCE_POWER section started, stop parsing.\n");
  126                         goto done;
  127                 default:
  128                         IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
  129                                      tlv_type, tlv_len);
  130                         break;
  131                 }
  132 
  133                 len -= ALIGN(tlv_len, 4);
  134                 data += ALIGN(tlv_len, 4);
  135         }
  136 
  137 done:
  138         if (!size) {
  139                 IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
  140                 /* Better safe than sorry, but 'reduce_power_data' should
  141                  * always be NULL if !size.
  142                  */
  143                 kfree(reduce_power_data);
  144                 reduce_power_data = ERR_PTR(-ENOENT);
  145                 goto out;
  146         }
  147 
  148         IWL_INFO(trans, "loaded REDUCE_POWER\n");
  149 
  150 out:
  151         return reduce_power_data;
  152 }
  153 
  154 static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
  155                                          const u8 *data, size_t len)
  156 {
  157         const struct iwl_ucode_tlv *tlv;
  158         void *sec_data;
  159 
  160         IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
  161 
  162         while (len >= sizeof(*tlv)) {
  163                 u32 tlv_len, tlv_type;
  164 
  165                 len -= sizeof(*tlv);
  166                 tlv = (const void *)data;
  167 
  168                 tlv_len = le32_to_cpu(tlv->length);
  169                 tlv_type = le32_to_cpu(tlv->type);
  170 
  171                 if (len < tlv_len) {
  172                         IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
  173                                 len, tlv_len);
  174                         return ERR_PTR(-EINVAL);
  175                 }
  176 
  177                 if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
  178                         const struct iwl_sku_id *sku_id =
  179                                 (const void *)(data + sizeof(*tlv));
  180 
  181                         IWL_DEBUG_FW(trans,
  182                                      "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
  183                                      tlv_len);
  184                         IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
  185                                      le32_to_cpu(sku_id->data[0]),
  186                                      le32_to_cpu(sku_id->data[1]),
  187                                      le32_to_cpu(sku_id->data[2]));
  188 
  189                         data += sizeof(*tlv) + ALIGN(tlv_len, 4);
  190                         len -= ALIGN(tlv_len, 4);
  191 
  192                         if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
  193                             trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
  194                             trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
  195                                 sec_data = iwl_uefi_reduce_power_section(trans,
  196                                                                          data,
  197                                                                          len);
  198                                 if (!IS_ERR(sec_data))
  199                                         return sec_data;
  200                         } else {
  201                                 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
  202                         }
  203                 } else {
  204                         data += sizeof(*tlv) + ALIGN(tlv_len, 4);
  205                         len -= ALIGN(tlv_len, 4);
  206                 }
  207         }
  208 
  209         return ERR_PTR(-ENOENT);
  210 }
  211 
  212 void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
  213 {
  214         struct efivar_entry *reduce_power_efivar;
  215         struct pnvm_sku_package *package;
  216         void *data = NULL;
  217         unsigned long package_size;
  218         int err;
  219 
  220         *len = 0;
  221 
  222         reduce_power_efivar = kzalloc(sizeof(*reduce_power_efivar), GFP_KERNEL);
  223         if (!reduce_power_efivar)
  224                 return ERR_PTR(-ENOMEM);
  225 
  226         memcpy(&reduce_power_efivar->var.VariableName, IWL_UEFI_REDUCED_POWER_NAME,
  227                sizeof(IWL_UEFI_REDUCED_POWER_NAME));
  228         reduce_power_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
  229 
  230         /*
  231          * TODO: we hardcode a maximum length here, because reading
  232          * from the UEFI is not working.  To implement this properly,
  233          * we have to call efivar_entry_size().
  234          */
  235         package_size = IWL_HARDCODED_REDUCE_POWER_SIZE;
  236 
  237         package = kmalloc(package_size, GFP_KERNEL);
  238         if (!package) {
  239                 package = ERR_PTR(-ENOMEM);
  240                 goto out;
  241         }
  242 
  243         err = efivar_entry_get(reduce_power_efivar, NULL, &package_size, package);
  244         if (err) {
  245                 IWL_DEBUG_FW(trans,
  246                              "Reduced Power UEFI variable not found %d (len %lu)\n",
  247                              err, package_size);
  248                 kfree(package);
  249                 data = ERR_PTR(err);
  250                 goto out;
  251         }
  252 
  253         IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
  254                      package_size);
  255         *len = package_size;
  256 
  257         IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
  258                      package->rev, package->total_size, package->n_skus);
  259 
  260         data = iwl_uefi_reduce_power_parse(trans, package->data,
  261                                            *len - sizeof(*package));
  262 
  263         kfree(package);
  264 
  265 out:
  266         kfree(reduce_power_efivar);
  267 
  268         return data;
  269 }
  270 
  271 #ifdef CONFIG_ACPI
  272 static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
  273                                struct iwl_fw_runtime *fwrt)
  274 {
  275         int i, j;
  276 
  277         if (sgom_data->revision != 1)
  278                 return -EINVAL;
  279 
  280         memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
  281                sizeof(fwrt->sgom_table.offset_map));
  282 
  283         for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
  284                 for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
  285                         /* since each byte is composed of to values, */
  286                         /* one for each letter, */
  287                         /* extract and check each of them separately */
  288                         u8 value = fwrt->sgom_table.offset_map[i][j];
  289                         u8 low = value & 0xF;
  290                         u8 high = (value & 0xF0) >> 4;
  291 
  292                         if (high > fwrt->geo_num_profiles)
  293                                 high = 0;
  294                         if (low > fwrt->geo_num_profiles)
  295                                 low = 0;
  296                         fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
  297                 }
  298         }
  299 
  300         fwrt->sgom_enabled = true;
  301         return 0;
  302 }
  303 
  304 void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
  305                              struct iwl_fw_runtime *fwrt)
  306 {
  307         struct efivar_entry *sgom_efivar;
  308         struct uefi_cnv_wlan_sgom_data *data;
  309         unsigned long package_size;
  310         int err, ret;
  311 
  312         if (!fwrt->geo_enabled)
  313                 return;
  314 
  315         sgom_efivar = kzalloc(sizeof(*sgom_efivar), GFP_KERNEL);
  316         if (!sgom_efivar)
  317                 return;
  318 
  319         memcpy(&sgom_efivar->var.VariableName, IWL_UEFI_SGOM_NAME,
  320                sizeof(IWL_UEFI_SGOM_NAME));
  321         sgom_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
  322 
  323         /* TODO: we hardcode a maximum length here, because reading
  324          * from the UEFI is not working.  To implement this properly,
  325          * we have to call efivar_entry_size().
  326          */
  327         package_size = IWL_HARDCODED_SGOM_SIZE;
  328 
  329         data = kmalloc(package_size, GFP_KERNEL);
  330         if (!data) {
  331                 data = ERR_PTR(-ENOMEM);
  332                 goto out;
  333         }
  334 
  335         err = efivar_entry_get(sgom_efivar, NULL, &package_size, data);
  336         if (err) {
  337                 IWL_DEBUG_FW(trans,
  338                              "SGOM UEFI variable not found %d\n", err);
  339                 goto out_free;
  340         }
  341 
  342         IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n",
  343                      package_size);
  344 
  345         ret = iwl_uefi_sgom_parse(data, fwrt);
  346         if (ret < 0)
  347                 IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
  348 
  349 out_free:
  350         kfree(data);
  351 
  352 out:
  353         kfree(sgom_efivar);
  354 }
  355 IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
  356 #endif /* CONFIG_ACPI */

Cache object: 17229aa62aad39ced03681694214693e


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