// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2021-2025 Intel Corporation
*/
#include "iwl-drv.h"
#include "pnvm.h"
#include "iwl-prph.h"
#include "iwl-io.h"
#include "fw/uefi.h"
#include "fw/api/alive.h"
#include <linux/efi.h>
#include "fw/runtime.h"
#define IWL_EFI_WIFI_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \
0xb2, 0xec, 0xf5, 0xa3, \
0x59, 0x4f, 0x4a, 0xea)
#define IWL_EFI_WIFI_BT_GUID EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, \
0x8d, 0x03, 0x77, 0x2e, \
0xcc, 0x3d, 0xa5, 0x31)
struct iwl_uefi_pnvm_mem_desc {
__le32 addr;
__le32 size;
const u8 data[];
} __packed;
static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid,
unsigned long *data_size)
{
efi_status_t status;
void *data;
if (!data_size)
return ERR_PTR(-EINVAL);
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return ERR_PTR(-ENODEV);
/* first call with NULL data to get the exact entry size */
*data_size = 0;
status = efi.get_variable(name, guid, NULL, data_size, NULL);
if (status != EFI_BUFFER_TOO_SMALL || !*data_size)
return ERR_PTR(-EIO);
data = kmalloc(*data_size, GFP_KERNEL);
if (!data)
return ERR_PTR(-ENOMEM);
status = efi.get_variable(name, guid, NULL, data_size, data);
if (status != EFI_SUCCESS) {
kfree(data);
return ERR_PTR(-ENOENT);
}
return data;
}
void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
{
unsigned long package_size;
void *data;
*len = 0;
data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_WIFI_GUID,
&package_size);
if (IS_ERR(data)) {
IWL_DEBUG_FW(trans,
"PNVM UEFI variable not found 0x%lx (len %lu)\n",
PTR_ERR(data), package_size);
return data;
}
IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
*len = package_size;
return data;
}
static void *
iwl_uefi_get_verified_variable_guid(struct iwl_trans *trans,
efi_guid_t *guid,
efi_char16_t *uefi_var_name,
char *var_name,
unsigned int expected_size,
unsigned long *size)
{
void *var;
unsigned long var_size;
var = iwl_uefi_get_variable(uefi_var_name, guid, &var_size);
if (IS_ERR(var)) {
IWL_DEBUG_RADIO(trans,
"%s UEFI variable not found 0x%lx\n", var_name,
PTR_ERR(var));
return var;
}
if (var_size < expected_size) {
IWL_DEBUG_RADIO(trans,
"Invalid %s UEFI variable len (%lu)\n",
var_name, var_size);
kfree(var);
return ERR_PTR(-EINVAL);
}
IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name,
var_size);
if (size)
*size = var_size;
return var;
}
static void *
iwl_uefi_get_verified_variable(struct iwl_trans *trans,
efi_char16_t *uefi_var_name,
char *var_name,
unsigned int expected_size,
unsigned long *size)
{
return iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_GUID,
uefi_var_name, var_name,
expected_size, size);
}
int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
u32 tlv_len, struct iwl_pnvm_image *pnvm_data)
{
const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data;
u32 data_len;
if (tlv_len < sizeof(*desc)) {