10
10
#include "fw/api/commands.h"
11
11
#include "fw/api/nvm-reg.h"
12
12
#include "fw/api/alive.h"
13
+ #include <linux/efi.h>
13
14
14
15
struct iwl_pnvm_section {
15
16
__le32 offset ;
@@ -219,6 +220,88 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
219
220
return - ENOENT ;
220
221
}
221
222
223
+ /*
224
+ * This is known to be broken on v4.19 and to work on v5.4. Until we
225
+ * figure out why this is the case and how to make it work, simply
226
+ * disable the feature in old kernels.
227
+ */
228
+ #if defined(CONFIG_EFI )
229
+
230
+ #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \
231
+ 0xb2, 0xec, 0xf5, 0xa3, \
232
+ 0x59, 0x4f, 0x4a, 0xea)
233
+
234
+ #define IWL_UEFI_OEM_PNVM_NAME L"UefiCnvWlanOemSignedPnvm"
235
+
236
+ #define IWL_HARDCODED_PNVM_SIZE 4096
237
+
238
+ struct pnvm_sku_package {
239
+ u8 rev ;
240
+ u8 reserved1 [3 ];
241
+ u32 total_size ;
242
+ u8 n_skus ;
243
+ u8 reserved2 [11 ];
244
+ u8 data [];
245
+ };
246
+
247
+ static int iwl_pnvm_get_from_efi (struct iwl_trans * trans ,
248
+ u8 * * data , size_t * len )
249
+ {
250
+ struct efivar_entry * pnvm_efivar ;
251
+ struct pnvm_sku_package * package ;
252
+ unsigned long package_size ;
253
+ int err ;
254
+
255
+ pnvm_efivar = kzalloc (sizeof (* pnvm_efivar ), GFP_KERNEL );
256
+ if (!pnvm_efivar )
257
+ return - ENOMEM ;
258
+
259
+ memcpy (& pnvm_efivar -> var .VariableName , IWL_UEFI_OEM_PNVM_NAME ,
260
+ sizeof (IWL_UEFI_OEM_PNVM_NAME ));
261
+ pnvm_efivar -> var .VendorGuid = IWL_EFI_VAR_GUID ;
262
+
263
+ /*
264
+ * TODO: we hardcode a maximum length here, because reading
265
+ * from the UEFI is not working. To implement this properly,
266
+ * we have to call efivar_entry_size().
267
+ */
268
+ package_size = IWL_HARDCODED_PNVM_SIZE ;
269
+
270
+ package = kmalloc (package_size , GFP_KERNEL );
271
+ if (!package ) {
272
+ err = - ENOMEM ;
273
+ goto out ;
274
+ }
275
+
276
+ err = efivar_entry_get (pnvm_efivar , NULL , & package_size , package );
277
+ if (err ) {
278
+ IWL_DEBUG_FW (trans ,
279
+ "PNVM UEFI variable not found %d (len %zd)\n" ,
280
+ err , package_size );
281
+ goto out ;
282
+ }
283
+
284
+ IWL_DEBUG_FW (trans , "Read PNVM fro UEFI with size %zd\n" , package_size );
285
+
286
+ * data = kmemdup (package -> data , * len , GFP_KERNEL );
287
+ if (!* data )
288
+ err = - ENOMEM ;
289
+ * len = package_size - sizeof (* package );
290
+
291
+ out :
292
+ kfree (package );
293
+ kfree (pnvm_efivar );
294
+
295
+ return err ;
296
+ }
297
+ #else /* CONFIG_EFI */
298
+ static inline int iwl_pnvm_get_from_efi (struct iwl_trans * trans ,
299
+ u8 * * data , size_t * len )
300
+ {
301
+ return - EOPNOTSUPP ;
302
+ }
303
+ #endif /* CONFIG_EFI */
304
+
222
305
static int iwl_pnvm_get_from_fs (struct iwl_trans * trans , u8 * * data , size_t * len )
223
306
{
224
307
const struct firmware * pnvm ;
@@ -277,7 +360,12 @@ int iwl_pnvm_load(struct iwl_trans *trans,
277
360
goto skip_parse ;
278
361
}
279
362
280
- /* Try to load the PNVM from the filesystem */
363
+ /* First attempt to get the PNVM from BIOS */
364
+ ret = iwl_pnvm_get_from_efi (trans , & data , & len );
365
+ if (!ret )
366
+ goto parse ;
367
+
368
+ /* If it's not available, try from the filesystem */
281
369
ret = iwl_pnvm_get_from_fs (trans , & data , & len );
282
370
if (ret ) {
283
371
/*
@@ -290,6 +378,7 @@ int iwl_pnvm_load(struct iwl_trans *trans,
290
378
goto skip_parse ;
291
379
}
292
380
381
+ parse :
293
382
iwl_pnvm_parse (trans , data , len );
294
383
295
384
kfree (data );
0 commit comments