1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright(c) 2020-2026 Intel Corporation
4 */
5
6 #include "iwl-drv.h"
7 #include "pnvm.h"
8 #include "iwl-prph.h"
9 #include "iwl-io.h"
10 #include "fw/api/commands.h"
11 #include "fw/api/nvm-reg.h"
12 #include "fw/api/alive.h"
13 #include "fw/uefi.h"
14 #include "fw/img.h"
15 #include "fw/dbg.h"
16
17 #define IWL_PNVM_REDUCED_CAP_BIT BIT(25)
18
19 struct iwl_pnvm_section {
20 __le32 offset;
21 const u8 data[];
22 } __packed;
23
iwl_pnvm_complete_fn(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * data)24 static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
25 struct iwl_rx_packet *pkt, void *data)
26 {
27 struct iwl_trans *trans = (struct iwl_trans *)data;
28 struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
29
30 if (IWL_FW_CHECK(trans,
31 iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf),
32 "Bad notif len: %d\n",
33 iwl_rx_packet_payload_len(pkt)))
34 return true;
35
36 IWL_DEBUG_FW(trans,
37 "PNVM complete notification received with status 0x%0x\n",
38 le32_to_cpu(pnvm_ntf->status));
39
40 return true;
41 }
42
iwl_pnvm_handle_section(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)43 static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
44 size_t len,
45 struct iwl_pnvm_image *pnvm_data)
46 {
47 const struct iwl_ucode_tlv *tlv;
48 u32 sha1 = 0;
49 u16 mac_type = 0, rf_id = 0;
50 bool hw_match = false;
51
52 IWL_DEBUG_FW(trans, "Handling PNVM section\n");
53
54 memset(pnvm_data, 0, sizeof(*pnvm_data));
55
56 while (len >= sizeof(*tlv)) {
57 u32 tlv_len, tlv_type;
58
59 len -= sizeof(*tlv);
60 tlv = (const void *)data;
61
62 tlv_len = le32_to_cpu(tlv->length);
63 tlv_type = le32_to_cpu(tlv->type);
64
65 if (len < tlv_len) {
66 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
67 len, tlv_len);
68 return -EINVAL;
69 }
70
71 data += sizeof(*tlv);
72
73 switch (tlv_type) {
74 case IWL_UCODE_TLV_PNVM_VERSION:
75 if (tlv_len < sizeof(__le32)) {
76 IWL_DEBUG_FW(trans,
77 "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
78 sizeof(__le32), tlv_len);
79 break;
80 }
81
82 sha1 = le32_to_cpup((const __le32 *)data);
83
84 IWL_DEBUG_FW(trans,
85 "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
86 sha1);
87 pnvm_data->version = sha1;
88 break;
89 case IWL_UCODE_TLV_HW_TYPE:
90 if (tlv_len < 2 * sizeof(__le16)) {
91 IWL_DEBUG_FW(trans,
92 "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
93 2 * sizeof(__le16), tlv_len);
94 break;
95 }
96
97 if (hw_match)
98 break;
99
100 mac_type = le16_to_cpup((const __le16 *)data);
101 rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16)));
102
103 IWL_DEBUG_FW(trans,
104 "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
105 mac_type, rf_id);
106
107 if (mac_type == CSR_HW_REV_TYPE(trans->info.hw_rev) &&
108 rf_id == CSR_HW_RFID_TYPE(trans->info.hw_rf_id))
109 hw_match = true;
110 break;
111 case IWL_UCODE_TLV_SEC_RT: {
112 const struct iwl_pnvm_section *section = (const void *)data;
113 u32 data_len = tlv_len - sizeof(*section);
114
115 IWL_DEBUG_FW(trans,
116 "Got IWL_UCODE_TLV_SEC_RT len %d\n",
117 tlv_len);
118
119 /* TODO: remove, this is a deprecated separator */
120 if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) {
121 IWL_DEBUG_FW(trans, "Ignoring separator.\n");
122 break;
123 }
124
125 if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {
126 IWL_DEBUG_FW(trans,
127 "too many payloads to allocate in DRAM.\n");
128 return -EINVAL;
129 }
130
131 IWL_DEBUG_FW(trans, "Adding data (size %d)\n",
132 data_len);
133
134 pnvm_data->chunks[pnvm_data->n_chunks].data = section->data;
135 pnvm_data->chunks[pnvm_data->n_chunks].len = data_len;
136 pnvm_data->n_chunks++;
137
138 break;
139 }
140 case IWL_UCODE_TLV_MEM_DESC:
141 if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len,
142 pnvm_data))
143 return -EINVAL;
144 break;
145 case IWL_UCODE_TLV_PNVM_SKU:
146 IWL_DEBUG_FW(trans,
147 "New PNVM section started, stop parsing.\n");
148 goto done;
149 default:
150 IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
151 tlv_type, tlv_len);
152 break;
153 }
154
155 len -= ALIGN(tlv_len, 4);
156 data += ALIGN(tlv_len, 4);
157 }
158
159 done:
160 if (!hw_match) {
161 IWL_DEBUG_FW(trans,
162 "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",
163 CSR_HW_REV_TYPE(trans->info.hw_rev),
164 CSR_HW_RFID_TYPE(trans->info.hw_rf_id));
165 return -ENOENT;
166 }
167
168 if (!pnvm_data->n_chunks) {
169 IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n");
170 return -ENOENT;
171 }
172
173 return 0;
174 }
175
iwl_pnvm_parse(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data,__le32 sku_id[3])176 static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
177 size_t len,
178 struct iwl_pnvm_image *pnvm_data,
179 __le32 sku_id[3])
180 {
181 const struct iwl_ucode_tlv *tlv;
182
183 IWL_DEBUG_FW(trans, "Parsing PNVM file\n");
184
185 while (len >= sizeof(*tlv)) {
186 u32 tlv_len, tlv_type;
187 u32 rf_type;
188
189 len -= sizeof(*tlv);
190 tlv = (const void *)data;
191
192 tlv_len = le32_to_cpu(tlv->length);
193 tlv_type = le32_to_cpu(tlv->type);
194
195 if (len < tlv_len) {
196 IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
197 len, tlv_len);
198 return -EINVAL;
199 }
200
201 if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
202 const struct iwl_sku_id *tlv_sku_id =
203 (const void *)(data + sizeof(*tlv));
204
205 IWL_DEBUG_FW(trans,
206 "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
207 tlv_len);
208 if (tlv_len < sizeof(*tlv_sku_id)) {
209 IWL_ERR(trans, "invalid PNVM SKU TLV len: %u\n",
210 tlv_len);
211 return -EINVAL;
212 }
213
214 IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
215 le32_to_cpu(tlv_sku_id->data[0]),
216 le32_to_cpu(tlv_sku_id->data[1]),
217 le32_to_cpu(tlv_sku_id->data[2]));
218
219 data += sizeof(*tlv) + ALIGN(tlv_len, 4);
220 len -= ALIGN(tlv_len, 4);
221
222 trans->reduced_cap_sku = false;
223 rf_type = CSR_HW_RFID_TYPE(trans->info.hw_rf_id);
224 if ((sku_id[0] & cpu_to_le32(IWL_PNVM_REDUCED_CAP_BIT)) &&
225 rf_type == IWL_CFG_RF_TYPE_FM)
226 trans->reduced_cap_sku = true;
227
228 IWL_DEBUG_FW(trans,
229 "Reduced SKU device %d\n",
230 trans->reduced_cap_sku);
231
232 if (sku_id[0] == tlv_sku_id->data[0] &&
233 sku_id[1] == tlv_sku_id->data[1] &&
234 sku_id[2] == tlv_sku_id->data[2]) {
235 int ret;
236
237 ret = iwl_pnvm_handle_section(trans, data, len,
238 pnvm_data);
239 if (!ret)
240 return 0;
241 } else {
242 IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
243 }
244 } else {
245 data += sizeof(*tlv) + ALIGN(tlv_len, 4);
246 len -= ALIGN(tlv_len, 4);
247 }
248 }
249
250 return -ENOENT;
251 }
252
iwl_pnvm_get_from_fs(struct iwl_trans * trans,size_t * len)253 static u8 *iwl_pnvm_get_from_fs(struct iwl_trans *trans, size_t *len)
254 {
255 const struct firmware *pnvm;
256 char pnvm_name[MAX_PNVM_NAME];
257 size_t new_len;
258 u8 *data;
259 int ret;
260
261 iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
262
263 ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
264 if (ret) {
265 IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
266 pnvm_name, ret);
267 return NULL;
268 }
269
270 new_len = pnvm->size;
271 data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
272 release_firmware(pnvm);
273
274 if (!data)
275 return NULL;
276
277 *len = new_len;
278
279 return data;
280 }
281
282 /**
283 * enum iwl_pnvm_source - different PNVM possible sources
284 *
285 * @IWL_PNVM_SOURCE_NONE: No PNVM.
286 * @IWL_PNVM_SOURCE_BIOS: PNVM should be read from BIOS.
287 * @IWL_PNVM_SOURCE_EXTERNAL: read .pnvm external file
288 * @IWL_PNVM_SOURCE_EMBEDDED: PNVM is embedded in the .ucode file.
289 */
290 enum iwl_pnvm_source {
291 IWL_PNVM_SOURCE_NONE,
292 IWL_PNVM_SOURCE_BIOS,
293 IWL_PNVM_SOURCE_EXTERNAL,
294 IWL_PNVM_SOURCE_EMBEDDED
295 };
296
iwl_select_pnvm_source(struct iwl_trans * trans,bool intel_sku)297 static enum iwl_pnvm_source iwl_select_pnvm_source(struct iwl_trans *trans,
298 bool intel_sku)
299 {
300
301 /* Get PNVM from BIOS for non-Intel SKU */
302 if (!intel_sku)
303 return IWL_PNVM_SOURCE_BIOS;
304
305 /* Before those devices, PNVM didn't exist at all */
306 if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
307 return IWL_PNVM_SOURCE_NONE;
308
309 /* After those devices, we moved to embedded PNVM */
310 if (trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_AX210)
311 return IWL_PNVM_SOURCE_EMBEDDED;
312
313 /* For IWL_DEVICE_FAMILY_AX210, depends on the CRF */
314 if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_GF)
315 return IWL_PNVM_SOURCE_EXTERNAL;
316
317 return IWL_PNVM_SOURCE_NONE;
318 }
319
iwl_get_pnvm_image(struct iwl_trans * trans_p,size_t * len,__le32 sku_id[3],const struct iwl_fw * fw)320 static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
321 __le32 sku_id[3], const struct iwl_fw *fw)
322 {
323 struct pnvm_sku_package *package;
324 enum iwl_pnvm_source pnvm_src =
325 iwl_select_pnvm_source(trans_p, sku_id[2] == 0);
326 u8 *image = NULL;
327
328 IWL_DEBUG_FW(trans_p, "PNVM source %d\n", pnvm_src);
329
330 if (pnvm_src == IWL_PNVM_SOURCE_NONE)
331 return NULL;
332
333 if (pnvm_src == IWL_PNVM_SOURCE_BIOS) {
334 package = iwl_uefi_get_pnvm(trans_p, len);
335 if (!IS_ERR_OR_NULL(package)) {
336 if (*len >= sizeof(*package)) {
337 /* we need only the data */
338 *len -= sizeof(*package);
339 image = kvmemdup(package->data,
340 *len, GFP_KERNEL);
341 }
342 /*
343 * free package regardless of whether kmemdup
344 * succeeded
345 */
346 kfree(package);
347 if (image)
348 return image;
349 }
350
351 /* PNVM doesn't exist in BIOS. Find the fallback source */
352 pnvm_src = iwl_select_pnvm_source(trans_p, true);
353 IWL_DEBUG_FW(trans_p, "PNVM in BIOS doesn't exist, try %d\n",
354 pnvm_src);
355 }
356
357 if (pnvm_src == IWL_PNVM_SOURCE_EXTERNAL) {
358 image = iwl_pnvm_get_from_fs(trans_p, len);
359 if (image)
360 return image;
361 }
362
363 if (pnvm_src == IWL_PNVM_SOURCE_EMBEDDED && fw->pnvm_data) {
364 *len = fw->pnvm_size;
365 return fw->pnvm_data;
366 }
367
368 IWL_ERR(trans_p, "Couldn't get PNVM from required source: %d\n", pnvm_src);
369 return NULL;
370 }
371
372 static void
iwl_pnvm_load_pnvm_to_trans(struct iwl_trans * trans,const struct iwl_fw * fw,__le32 sku_id[3])373 iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
374 const struct iwl_fw *fw,
375 __le32 sku_id[3])
376 {
377 struct iwl_pnvm_image *pnvm_data = NULL;
378 const u8 *data = NULL;
379 size_t length;
380 int ret;
381
382 /* failed to get/parse the image in the past, no use trying again */
383 if (trans->fail_to_parse_pnvm_image)
384 return;
385
386 if (trans->pnvm_loaded)
387 goto set;
388
389 data = iwl_get_pnvm_image(trans, &length, sku_id, fw);
390 if (!data) {
391 trans->fail_to_parse_pnvm_image = true;
392 return;
393 }
394
395 pnvm_data = kzalloc_obj(*pnvm_data);
396 if (!pnvm_data)
397 goto free;
398
399 ret = iwl_pnvm_parse(trans, data, length, pnvm_data, sku_id);
400 if (ret) {
401 trans->fail_to_parse_pnvm_image = true;
402 goto free;
403 }
404
405 ret = iwl_trans_load_pnvm(trans, pnvm_data, &fw->ucode_capa);
406 if (ret)
407 goto free;
408 IWL_DEBUG_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version);
409
410 set:
411 iwl_trans_set_pnvm(trans, &fw->ucode_capa);
412 free:
413 /* free only if it was allocated, i.e. not just embedded PNVM data */
414 if (data != fw->pnvm_data)
415 kvfree(data);
416 kfree(pnvm_data);
417 }
418
419 static void
iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans * trans,const struct iwl_ucode_capabilities * capa,__le32 sku_id[3])420 iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans,
421 const struct iwl_ucode_capabilities *capa,
422 __le32 sku_id[3])
423 {
424 struct iwl_pnvm_image *pnvm_data = NULL;
425 u8 *data = NULL;
426 size_t length;
427 int ret;
428
429 if (trans->failed_to_load_reduce_power_image)
430 return;
431
432 if (trans->reduce_power_loaded)
433 goto set;
434
435 data = iwl_uefi_get_reduced_power(trans, &length);
436 if (IS_ERR(data)) {
437 trans->failed_to_load_reduce_power_image = true;
438 return;
439 }
440
441 pnvm_data = kzalloc_obj(*pnvm_data);
442 if (!pnvm_data)
443 goto free;
444
445 ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data,
446 sku_id);
447 if (ret) {
448 trans->failed_to_load_reduce_power_image = true;
449 goto free;
450 }
451
452 ret = iwl_trans_load_reduce_power(trans, pnvm_data, capa);
453 if (ret) {
454 IWL_DEBUG_FW(trans,
455 "Failed to load reduce power table %d\n",
456 ret);
457 trans->failed_to_load_reduce_power_image = true;
458 goto free;
459 }
460
461 set:
462 iwl_trans_set_reduce_power(trans, capa);
463 free:
464 kfree(data);
465 kfree(pnvm_data);
466 }
467
iwl_pnvm_load(struct iwl_trans * trans,struct iwl_notif_wait_data * notif_wait,const struct iwl_fw * fw,__le32 sku_id[3])468 int iwl_pnvm_load(struct iwl_trans *trans,
469 struct iwl_notif_wait_data *notif_wait,
470 const struct iwl_fw *fw, __le32 sku_id[3])
471 {
472 struct iwl_notification_wait pnvm_wait;
473 static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
474 PNVM_INIT_COMPLETE_NTFY) };
475
476 /* if the SKU_ID is empty, there's nothing to do */
477 if (!sku_id[0] && !sku_id[1] && !sku_id[2])
478 return 0;
479
480 iwl_pnvm_load_pnvm_to_trans(trans, fw, sku_id);
481 iwl_pnvm_load_reduce_power_to_trans(trans, &fw->ucode_capa, sku_id);
482
483 iwl_init_notification_wait(notif_wait, &pnvm_wait,
484 ntf_cmds, ARRAY_SIZE(ntf_cmds),
485 iwl_pnvm_complete_fn, trans);
486
487 /* kick the doorbell */
488 iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
489 UREG_DOORBELL_TO_ISR6_PNVM);
490
491 return iwl_wait_notification(notif_wait, &pnvm_wait,
492 MVM_UCODE_PNVM_TIMEOUT);
493 }
494 IWL_EXPORT_SYMBOL(iwl_pnvm_load);
495