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