1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3 * Copyright(c) 2021 Intel Corporation
12 #include "fw/api/alive.h"
13 #include <linux/efi.h>
15 #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \
16 0xb2, 0xec, 0xf5, 0xa3, \
17 0x59, 0x4f, 0x4a, 0xea)
19 void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
21 struct efivar_entry *pnvm_efivar;
23 unsigned long package_size;
28 pnvm_efivar = kzalloc(sizeof(*pnvm_efivar), GFP_KERNEL);
30 return ERR_PTR(-ENOMEM);
32 memcpy(&pnvm_efivar->var.VariableName, IWL_UEFI_OEM_PNVM_NAME,
33 sizeof(IWL_UEFI_OEM_PNVM_NAME));
34 pnvm_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
37 * TODO: we hardcode a maximum length here, because reading
38 * from the UEFI is not working. To implement this properly,
39 * we have to call efivar_entry_size().
41 package_size = IWL_HARDCODED_PNVM_SIZE;
43 data = kmalloc(package_size, GFP_KERNEL);
45 data = ERR_PTR(-ENOMEM);
49 err = efivar_entry_get(pnvm_efivar, NULL, &package_size, data);
52 "PNVM UEFI variable not found %d (len %zd)\n",
59 IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %zd\n", package_size);
68 static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
69 const u8 *data, size_t len)
71 struct iwl_ucode_tlv *tlv;
72 u8 *reduce_power_data = NULL, *tmp;
75 IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
77 while (len >= sizeof(*tlv)) {
78 u32 tlv_len, tlv_type;
83 tlv_len = le32_to_cpu(tlv->length);
84 tlv_type = le32_to_cpu(tlv->type);
87 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
89 reduce_power_data = ERR_PTR(-EINVAL);
96 case IWL_UCODE_TLV_MEM_DESC: {
98 "Got IWL_UCODE_TLV_MEM_DESC len %d\n",
101 IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len);
103 tmp = krealloc(reduce_power_data, size + tlv_len, GFP_KERNEL);
106 "Couldn't allocate (more) reduce_power_data\n");
108 reduce_power_data = ERR_PTR(-ENOMEM);
112 reduce_power_data = tmp;
114 memcpy(reduce_power_data + size, data, tlv_len);
120 case IWL_UCODE_TLV_PNVM_SKU:
122 "New REDUCE_POWER section started, stop parsing.\n");
125 IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
130 len -= ALIGN(tlv_len, 4);
131 data += ALIGN(tlv_len, 4);
136 IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
137 reduce_power_data = ERR_PTR(-ENOENT);
141 IWL_INFO(trans, "loaded REDUCE_POWER\n");
144 return reduce_power_data;
147 static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
148 const u8 *data, size_t len)
150 struct iwl_ucode_tlv *tlv;
153 IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
155 while (len >= sizeof(*tlv)) {
156 u32 tlv_len, tlv_type;
161 tlv_len = le32_to_cpu(tlv->length);
162 tlv_type = le32_to_cpu(tlv->type);
165 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
167 return ERR_PTR(-EINVAL);
170 if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
171 struct iwl_sku_id *sku_id =
172 (void *)(data + sizeof(*tlv));
175 "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
177 IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
178 le32_to_cpu(sku_id->data[0]),
179 le32_to_cpu(sku_id->data[1]),
180 le32_to_cpu(sku_id->data[2]));
182 data += sizeof(*tlv) + ALIGN(tlv_len, 4);
183 len -= ALIGN(tlv_len, 4);
185 if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
186 trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
187 trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
188 sec_data = iwl_uefi_reduce_power_section(trans,
191 if (!IS_ERR(sec_data))
194 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
197 data += sizeof(*tlv) + ALIGN(tlv_len, 4);
198 len -= ALIGN(tlv_len, 4);
202 return ERR_PTR(-ENOENT);
205 void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
207 struct efivar_entry *reduce_power_efivar;
208 struct pnvm_sku_package *package;
210 unsigned long package_size;
215 reduce_power_efivar = kzalloc(sizeof(*reduce_power_efivar), GFP_KERNEL);
216 if (!reduce_power_efivar)
217 return ERR_PTR(-ENOMEM);
219 memcpy(&reduce_power_efivar->var.VariableName, IWL_UEFI_REDUCED_POWER_NAME,
220 sizeof(IWL_UEFI_REDUCED_POWER_NAME));
221 reduce_power_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
224 * TODO: we hardcode a maximum length here, because reading
225 * from the UEFI is not working. To implement this properly,
226 * we have to call efivar_entry_size().
228 package_size = IWL_HARDCODED_REDUCE_POWER_SIZE;
230 package = kmalloc(package_size, GFP_KERNEL);
232 package = ERR_PTR(-ENOMEM);
236 err = efivar_entry_get(reduce_power_efivar, NULL, &package_size, package);
239 "Reduced Power UEFI variable not found %d (len %lu)\n",
246 IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
250 IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
251 package->rev, package->total_size, package->n_skus);
253 data = iwl_uefi_reduce_power_parse(trans, package->data,
254 *len - sizeof(*package));
259 kfree(reduce_power_efivar);