cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

uefi.c (8538B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * Copyright(c) 2021 Intel Corporation
      4 */
      5
      6#include "iwl-drv.h"
      7#include "pnvm.h"
      8#include "iwl-prph.h"
      9#include "iwl-io.h"
     10
     11#include "fw/uefi.h"
     12#include "fw/api/alive.h"
     13#include <linux/efi.h>
     14#include "fw/runtime.h"
     15
     16#define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,	\
     17				  0xb2, 0xec, 0xf5, 0xa3,	\
     18				  0x59, 0x4f, 0x4a, 0xea)
     19
     20void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
     21{
     22	struct efivar_entry *pnvm_efivar;
     23	void *data;
     24	unsigned long package_size;
     25	int err;
     26
     27	*len = 0;
     28
     29	pnvm_efivar = kzalloc(sizeof(*pnvm_efivar), GFP_KERNEL);
     30	if (!pnvm_efivar)
     31		return ERR_PTR(-ENOMEM);
     32
     33	memcpy(&pnvm_efivar->var.VariableName, IWL_UEFI_OEM_PNVM_NAME,
     34	       sizeof(IWL_UEFI_OEM_PNVM_NAME));
     35	pnvm_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
     36
     37	/*
     38	 * TODO: we hardcode a maximum length here, because reading
     39	 * from the UEFI is not working.  To implement this properly,
     40	 * we have to call efivar_entry_size().
     41	 */
     42	package_size = IWL_HARDCODED_PNVM_SIZE;
     43
     44	data = kmalloc(package_size, GFP_KERNEL);
     45	if (!data) {
     46		data = ERR_PTR(-ENOMEM);
     47		goto out;
     48	}
     49
     50	err = efivar_entry_get(pnvm_efivar, NULL, &package_size, data);
     51	if (err) {
     52		IWL_DEBUG_FW(trans,
     53			     "PNVM UEFI variable not found %d (len %lu)\n",
     54			     err, package_size);
     55		kfree(data);
     56		data = ERR_PTR(err);
     57		goto out;
     58	}
     59
     60	IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
     61	*len = package_size;
     62
     63out:
     64	kfree(pnvm_efivar);
     65
     66	return data;
     67}
     68
     69static void *iwl_uefi_reduce_power_section(struct iwl_trans *trans,
     70					   const u8 *data, size_t len)
     71{
     72	const struct iwl_ucode_tlv *tlv;
     73	u8 *reduce_power_data = NULL, *tmp;
     74	u32 size = 0;
     75
     76	IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
     77
     78	while (len >= sizeof(*tlv)) {
     79		u32 tlv_len, tlv_type;
     80
     81		len -= sizeof(*tlv);
     82		tlv = (const void *)data;
     83
     84		tlv_len = le32_to_cpu(tlv->length);
     85		tlv_type = le32_to_cpu(tlv->type);
     86
     87		if (len < tlv_len) {
     88			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
     89				len, tlv_len);
     90			kfree(reduce_power_data);
     91			reduce_power_data = ERR_PTR(-EINVAL);
     92			goto out;
     93		}
     94
     95		data += sizeof(*tlv);
     96
     97		switch (tlv_type) {
     98		case IWL_UCODE_TLV_MEM_DESC: {
     99			IWL_DEBUG_FW(trans,
    100				     "Got IWL_UCODE_TLV_MEM_DESC len %d\n",
    101				     tlv_len);
    102
    103			IWL_DEBUG_FW(trans, "Adding data (size %d)\n", tlv_len);
    104
    105			tmp = krealloc(reduce_power_data, size + tlv_len, GFP_KERNEL);
    106			if (!tmp) {
    107				IWL_DEBUG_FW(trans,
    108					     "Couldn't allocate (more) reduce_power_data\n");
    109
    110				kfree(reduce_power_data);
    111				reduce_power_data = ERR_PTR(-ENOMEM);
    112				goto out;
    113			}
    114
    115			reduce_power_data = tmp;
    116
    117			memcpy(reduce_power_data + size, data, tlv_len);
    118
    119			size += tlv_len;
    120
    121			break;
    122		}
    123		case IWL_UCODE_TLV_PNVM_SKU:
    124			IWL_DEBUG_FW(trans,
    125				     "New REDUCE_POWER section started, stop parsing.\n");
    126			goto done;
    127		default:
    128			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
    129				     tlv_type, tlv_len);
    130			break;
    131		}
    132
    133		len -= ALIGN(tlv_len, 4);
    134		data += ALIGN(tlv_len, 4);
    135	}
    136
    137done:
    138	if (!size) {
    139		IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
    140		/* Better safe than sorry, but 'reduce_power_data' should
    141		 * always be NULL if !size.
    142		 */
    143		kfree(reduce_power_data);
    144		reduce_power_data = ERR_PTR(-ENOENT);
    145		goto out;
    146	}
    147
    148	IWL_INFO(trans, "loaded REDUCE_POWER\n");
    149
    150out:
    151	return reduce_power_data;
    152}
    153
    154static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
    155					 const u8 *data, size_t len)
    156{
    157	const struct iwl_ucode_tlv *tlv;
    158	void *sec_data;
    159
    160	IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
    161
    162	while (len >= sizeof(*tlv)) {
    163		u32 tlv_len, tlv_type;
    164
    165		len -= sizeof(*tlv);
    166		tlv = (const void *)data;
    167
    168		tlv_len = le32_to_cpu(tlv->length);
    169		tlv_type = le32_to_cpu(tlv->type);
    170
    171		if (len < tlv_len) {
    172			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
    173				len, tlv_len);
    174			return ERR_PTR(-EINVAL);
    175		}
    176
    177		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
    178			const struct iwl_sku_id *sku_id =
    179				(const void *)(data + sizeof(*tlv));
    180
    181			IWL_DEBUG_FW(trans,
    182				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
    183				     tlv_len);
    184			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
    185				     le32_to_cpu(sku_id->data[0]),
    186				     le32_to_cpu(sku_id->data[1]),
    187				     le32_to_cpu(sku_id->data[2]));
    188
    189			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
    190			len -= ALIGN(tlv_len, 4);
    191
    192			if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
    193			    trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
    194			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
    195				sec_data = iwl_uefi_reduce_power_section(trans,
    196									 data,
    197									 len);
    198				if (!IS_ERR(sec_data))
    199					return sec_data;
    200			} else {
    201				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
    202			}
    203		} else {
    204			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
    205			len -= ALIGN(tlv_len, 4);
    206		}
    207	}
    208
    209	return ERR_PTR(-ENOENT);
    210}
    211
    212void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
    213{
    214	struct efivar_entry *reduce_power_efivar;
    215	struct pnvm_sku_package *package;
    216	void *data = NULL;
    217	unsigned long package_size;
    218	int err;
    219
    220	*len = 0;
    221
    222	reduce_power_efivar = kzalloc(sizeof(*reduce_power_efivar), GFP_KERNEL);
    223	if (!reduce_power_efivar)
    224		return ERR_PTR(-ENOMEM);
    225
    226	memcpy(&reduce_power_efivar->var.VariableName, IWL_UEFI_REDUCED_POWER_NAME,
    227	       sizeof(IWL_UEFI_REDUCED_POWER_NAME));
    228	reduce_power_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
    229
    230	/*
    231	 * TODO: we hardcode a maximum length here, because reading
    232	 * from the UEFI is not working.  To implement this properly,
    233	 * we have to call efivar_entry_size().
    234	 */
    235	package_size = IWL_HARDCODED_REDUCE_POWER_SIZE;
    236
    237	package = kmalloc(package_size, GFP_KERNEL);
    238	if (!package) {
    239		package = ERR_PTR(-ENOMEM);
    240		goto out;
    241	}
    242
    243	err = efivar_entry_get(reduce_power_efivar, NULL, &package_size, package);
    244	if (err) {
    245		IWL_DEBUG_FW(trans,
    246			     "Reduced Power UEFI variable not found %d (len %lu)\n",
    247			     err, package_size);
    248		kfree(package);
    249		data = ERR_PTR(err);
    250		goto out;
    251	}
    252
    253	IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
    254		     package_size);
    255	*len = package_size;
    256
    257	IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
    258		     package->rev, package->total_size, package->n_skus);
    259
    260	data = iwl_uefi_reduce_power_parse(trans, package->data,
    261					   *len - sizeof(*package));
    262
    263	kfree(package);
    264
    265out:
    266	kfree(reduce_power_efivar);
    267
    268	return data;
    269}
    270
    271#ifdef CONFIG_ACPI
    272static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
    273			       struct iwl_fw_runtime *fwrt)
    274{
    275	int i, j;
    276
    277	if (sgom_data->revision != 1)
    278		return -EINVAL;
    279
    280	memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
    281	       sizeof(fwrt->sgom_table.offset_map));
    282
    283	for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
    284		for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
    285			/* since each byte is composed of to values, */
    286			/* one for each letter, */
    287			/* extract and check each of them separately */
    288			u8 value = fwrt->sgom_table.offset_map[i][j];
    289			u8 low = value & 0xF;
    290			u8 high = (value & 0xF0) >> 4;
    291
    292			if (high > fwrt->geo_num_profiles)
    293				high = 0;
    294			if (low > fwrt->geo_num_profiles)
    295				low = 0;
    296			fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
    297		}
    298	}
    299
    300	fwrt->sgom_enabled = true;
    301	return 0;
    302}
    303
    304void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
    305			     struct iwl_fw_runtime *fwrt)
    306{
    307	struct efivar_entry *sgom_efivar;
    308	struct uefi_cnv_wlan_sgom_data *data;
    309	unsigned long package_size;
    310	int err, ret;
    311
    312	if (!fwrt->geo_enabled)
    313		return;
    314
    315	sgom_efivar = kzalloc(sizeof(*sgom_efivar), GFP_KERNEL);
    316	if (!sgom_efivar)
    317		return;
    318
    319	memcpy(&sgom_efivar->var.VariableName, IWL_UEFI_SGOM_NAME,
    320	       sizeof(IWL_UEFI_SGOM_NAME));
    321	sgom_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
    322
    323	/* TODO: we hardcode a maximum length here, because reading
    324	 * from the UEFI is not working.  To implement this properly,
    325	 * we have to call efivar_entry_size().
    326	 */
    327	package_size = IWL_HARDCODED_SGOM_SIZE;
    328
    329	data = kmalloc(package_size, GFP_KERNEL);
    330	if (!data) {
    331		data = ERR_PTR(-ENOMEM);
    332		goto out;
    333	}
    334
    335	err = efivar_entry_get(sgom_efivar, NULL, &package_size, data);
    336	if (err) {
    337		IWL_DEBUG_FW(trans,
    338			     "SGOM UEFI variable not found %d\n", err);
    339		goto out_free;
    340	}
    341
    342	IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n",
    343		     package_size);
    344
    345	ret = iwl_uefi_sgom_parse(data, fwrt);
    346	if (ret < 0)
    347		IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
    348
    349out_free:
    350	kfree(data);
    351
    352out:
    353	kfree(sgom_efivar);
    354}
    355IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
    356#endif /* CONFIG_ACPI */