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