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