xref: /linux/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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,u8 ** data,size_t * len)240 static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
241 {
242 	const struct firmware *pnvm;
243 	char pnvm_name[MAX_PNVM_NAME];
244 	size_t new_len;
245 	int ret;
246 
247 	iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
248 
249 	ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
250 	if (ret) {
251 		IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
252 			     pnvm_name, ret);
253 		return ret;
254 	}
255 
256 	new_len = pnvm->size;
257 	*data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
258 	release_firmware(pnvm);
259 
260 	if (!*data)
261 		return -ENOMEM;
262 
263 	*len = new_len;
264 
265 	return 0;
266 }
267 
iwl_get_pnvm_image(struct iwl_trans * trans_p,size_t * len,__le32 sku_id[3],const struct iwl_fw * fw)268 static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
269 				    __le32 sku_id[3], const struct iwl_fw *fw)
270 {
271 	struct pnvm_sku_package *package;
272 	u8 *image = NULL;
273 
274 	/* Get PNVM from BIOS for non-Intel SKU */
275 	if (sku_id[2]) {
276 		package = iwl_uefi_get_pnvm(trans_p, len);
277 		if (!IS_ERR_OR_NULL(package)) {
278 			if (*len >= sizeof(*package)) {
279 				/* we need only the data */
280 				*len -= sizeof(*package);
281 				image = kvmemdup(package->data,
282 						 *len, GFP_KERNEL);
283 			}
284 			/*
285 			 * free package regardless of whether kmemdup
286 			 * succeeded
287 			 */
288 			kfree(package);
289 			if (image)
290 				return image;
291 		}
292 	}
293 
294 	if (fw->pnvm_data) {
295 		*len = fw->pnvm_size;
296 
297 		return fw->pnvm_data;
298 	}
299 
300 	/* If it's not available, or for Intel SKU, try from the filesystem */
301 	if (iwl_pnvm_get_from_fs(trans_p, &image, len))
302 		return NULL;
303 	return image;
304 }
305 
306 static void
iwl_pnvm_load_pnvm_to_trans(struct iwl_trans * trans,const struct iwl_fw * fw,__le32 sku_id[3])307 iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
308 			    const struct iwl_fw *fw,
309 			    __le32 sku_id[3])
310 {
311 	struct iwl_pnvm_image *pnvm_data = NULL;
312 	const u8 *data = NULL;
313 	size_t length;
314 	int ret;
315 
316 	/* failed to get/parse the image in the past, no use trying again */
317 	if (trans->fail_to_parse_pnvm_image)
318 		return;
319 
320 	if (trans->pnvm_loaded)
321 		goto set;
322 
323 	data = iwl_get_pnvm_image(trans, &length, sku_id, fw);
324 	if (!data) {
325 		trans->fail_to_parse_pnvm_image = true;
326 		return;
327 	}
328 
329 	pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL);
330 	if (!pnvm_data)
331 		goto free;
332 
333 	ret = iwl_pnvm_parse(trans, data, length, pnvm_data, sku_id);
334 	if (ret) {
335 		trans->fail_to_parse_pnvm_image = true;
336 		goto free;
337 	}
338 
339 	ret = iwl_trans_load_pnvm(trans, pnvm_data, &fw->ucode_capa);
340 	if (ret)
341 		goto free;
342 	IWL_DEBUG_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version);
343 
344 set:
345 	iwl_trans_set_pnvm(trans, &fw->ucode_capa);
346 free:
347 	/* free only if it was allocated, i.e. not just embedded PNVM data */
348 	if (data != fw->pnvm_data)
349 		kvfree(data);
350 	kfree(pnvm_data);
351 }
352 
353 static void
iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans * trans,const struct iwl_ucode_capabilities * capa,__le32 sku_id[3])354 iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans,
355 				    const struct iwl_ucode_capabilities *capa,
356 				    __le32 sku_id[3])
357 {
358 	struct iwl_pnvm_image *pnvm_data = NULL;
359 	u8 *data = NULL;
360 	size_t length;
361 	int ret;
362 
363 	if (trans->failed_to_load_reduce_power_image)
364 		return;
365 
366 	if (trans->reduce_power_loaded)
367 		goto set;
368 
369 	data = iwl_uefi_get_reduced_power(trans, &length);
370 	if (IS_ERR(data)) {
371 		trans->failed_to_load_reduce_power_image = true;
372 		return;
373 	}
374 
375 	pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL);
376 	if (!pnvm_data)
377 		goto free;
378 
379 	ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data,
380 					  sku_id);
381 	if (ret) {
382 		trans->failed_to_load_reduce_power_image = true;
383 		goto free;
384 	}
385 
386 	ret = iwl_trans_load_reduce_power(trans, pnvm_data, capa);
387 	if (ret) {
388 		IWL_DEBUG_FW(trans,
389 			     "Failed to load reduce power table %d\n",
390 			     ret);
391 		trans->failed_to_load_reduce_power_image = true;
392 		goto free;
393 	}
394 
395 set:
396 	iwl_trans_set_reduce_power(trans, capa);
397 free:
398 	kfree(data);
399 	kfree(pnvm_data);
400 }
401 
iwl_pnvm_load(struct iwl_trans * trans,struct iwl_notif_wait_data * notif_wait,const struct iwl_fw * fw,__le32 sku_id[3])402 int iwl_pnvm_load(struct iwl_trans *trans,
403 		  struct iwl_notif_wait_data *notif_wait,
404 		  const struct iwl_fw *fw, __le32 sku_id[3])
405 {
406 	struct iwl_notification_wait pnvm_wait;
407 	static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
408 						PNVM_INIT_COMPLETE_NTFY) };
409 
410 	/* if the SKU_ID is empty, there's nothing to do */
411 	if (!sku_id[0] && !sku_id[1] && !sku_id[2])
412 		return 0;
413 
414 	iwl_pnvm_load_pnvm_to_trans(trans, fw, sku_id);
415 	iwl_pnvm_load_reduce_power_to_trans(trans, &fw->ucode_capa, sku_id);
416 
417 	iwl_init_notification_wait(notif_wait, &pnvm_wait,
418 				   ntf_cmds, ARRAY_SIZE(ntf_cmds),
419 				   iwl_pnvm_complete_fn, trans);
420 
421 	/* kick the doorbell */
422 	iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
423 			    UREG_DOORBELL_TO_ISR6_PNVM);
424 
425 	return iwl_wait_notification(notif_wait, &pnvm_wait,
426 				     MVM_UCODE_PNVM_TIMEOUT);
427 }
428 IWL_EXPORT_SYMBOL(iwl_pnvm_load);
429