xref: /linux/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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 
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 
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 
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 
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 
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 
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
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(sizeof(*pnvm_data), GFP_KERNEL);
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
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(sizeof(*pnvm_data), GFP_KERNEL);
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 
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