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

pnvm.c (8344B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * Copyright(c) 2020-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#include "fw/api/commands.h"
     11#include "fw/api/nvm-reg.h"
     12#include "fw/api/alive.h"
     13#include "fw/uefi.h"
     14
     15struct iwl_pnvm_section {
     16	__le32 offset;
     17	const u8 data[];
     18} __packed;
     19
     20static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
     21				 struct iwl_rx_packet *pkt, void *data)
     22{
     23	struct iwl_trans *trans = (struct iwl_trans *)data;
     24	struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;
     25
     26	IWL_DEBUG_FW(trans,
     27		     "PNVM complete notification received with status 0x%0x\n",
     28		     le32_to_cpu(pnvm_ntf->status));
     29
     30	return true;
     31}
     32
     33static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data,
     34				   size_t len)
     35{
     36	const struct iwl_ucode_tlv *tlv;
     37	u32 sha1 = 0;
     38	u16 mac_type = 0, rf_id = 0;
     39	u8 *pnvm_data = NULL, *tmp;
     40	bool hw_match = false;
     41	u32 size = 0;
     42	int ret;
     43
     44	IWL_DEBUG_FW(trans, "Handling PNVM section\n");
     45
     46	while (len >= sizeof(*tlv)) {
     47		u32 tlv_len, tlv_type;
     48
     49		len -= sizeof(*tlv);
     50		tlv = (const void *)data;
     51
     52		tlv_len = le32_to_cpu(tlv->length);
     53		tlv_type = le32_to_cpu(tlv->type);
     54
     55		if (len < tlv_len) {
     56			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
     57				len, tlv_len);
     58			ret = -EINVAL;
     59			goto out;
     60		}
     61
     62		data += sizeof(*tlv);
     63
     64		switch (tlv_type) {
     65		case IWL_UCODE_TLV_PNVM_VERSION:
     66			if (tlv_len < sizeof(__le32)) {
     67				IWL_DEBUG_FW(trans,
     68					     "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n",
     69					     sizeof(__le32), tlv_len);
     70				break;
     71			}
     72
     73			sha1 = le32_to_cpup((const __le32 *)data);
     74
     75			IWL_DEBUG_FW(trans,
     76				     "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n",
     77				     sha1);
     78			break;
     79		case IWL_UCODE_TLV_HW_TYPE:
     80			if (tlv_len < 2 * sizeof(__le16)) {
     81				IWL_DEBUG_FW(trans,
     82					     "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n",
     83					     2 * sizeof(__le16), tlv_len);
     84				break;
     85			}
     86
     87			if (hw_match)
     88				break;
     89
     90			mac_type = le16_to_cpup((const __le16 *)data);
     91			rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16)));
     92
     93			IWL_DEBUG_FW(trans,
     94				     "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n",
     95				     mac_type, rf_id);
     96
     97			if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) &&
     98			    rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id))
     99				hw_match = true;
    100			break;
    101		case IWL_UCODE_TLV_SEC_RT: {
    102			const struct iwl_pnvm_section *section = (const void *)data;
    103			u32 data_len = tlv_len - sizeof(*section);
    104
    105			IWL_DEBUG_FW(trans,
    106				     "Got IWL_UCODE_TLV_SEC_RT len %d\n",
    107				     tlv_len);
    108
    109			/* TODO: remove, this is a deprecated separator */
    110			if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) {
    111				IWL_DEBUG_FW(trans, "Ignoring separator.\n");
    112				break;
    113			}
    114
    115			IWL_DEBUG_FW(trans, "Adding data (size %d)\n",
    116				     data_len);
    117
    118			tmp = krealloc(pnvm_data, size + data_len, GFP_KERNEL);
    119			if (!tmp) {
    120				IWL_DEBUG_FW(trans,
    121					     "Couldn't allocate (more) pnvm_data\n");
    122
    123				ret = -ENOMEM;
    124				goto out;
    125			}
    126
    127			pnvm_data = tmp;
    128
    129			memcpy(pnvm_data + size, section->data, data_len);
    130
    131			size += data_len;
    132
    133			break;
    134		}
    135		case IWL_UCODE_TLV_PNVM_SKU:
    136			IWL_DEBUG_FW(trans,
    137				     "New PNVM section started, stop parsing.\n");
    138			goto done;
    139		default:
    140			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
    141				     tlv_type, tlv_len);
    142			break;
    143		}
    144
    145		len -= ALIGN(tlv_len, 4);
    146		data += ALIGN(tlv_len, 4);
    147	}
    148
    149done:
    150	if (!hw_match) {
    151		IWL_DEBUG_FW(trans,
    152			     "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n",
    153			     CSR_HW_REV_TYPE(trans->hw_rev),
    154			     CSR_HW_RFID_TYPE(trans->hw_rf_id));
    155		ret = -ENOENT;
    156		goto out;
    157	}
    158
    159	if (!size) {
    160		IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n");
    161		ret = -ENOENT;
    162		goto out;
    163	}
    164
    165	IWL_INFO(trans, "loaded PNVM version %08x\n", sha1);
    166
    167	ret = iwl_trans_set_pnvm(trans, pnvm_data, size);
    168out:
    169	kfree(pnvm_data);
    170	return ret;
    171}
    172
    173static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
    174			  size_t len)
    175{
    176	const struct iwl_ucode_tlv *tlv;
    177
    178	IWL_DEBUG_FW(trans, "Parsing PNVM file\n");
    179
    180	while (len >= sizeof(*tlv)) {
    181		u32 tlv_len, tlv_type;
    182
    183		len -= sizeof(*tlv);
    184		tlv = (const void *)data;
    185
    186		tlv_len = le32_to_cpu(tlv->length);
    187		tlv_type = le32_to_cpu(tlv->type);
    188
    189		if (len < tlv_len) {
    190			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
    191				len, tlv_len);
    192			return -EINVAL;
    193		}
    194
    195		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
    196			const struct iwl_sku_id *sku_id =
    197				(const void *)(data + sizeof(*tlv));
    198
    199			IWL_DEBUG_FW(trans,
    200				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
    201				     tlv_len);
    202			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
    203				     le32_to_cpu(sku_id->data[0]),
    204				     le32_to_cpu(sku_id->data[1]),
    205				     le32_to_cpu(sku_id->data[2]));
    206
    207			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
    208			len -= ALIGN(tlv_len, 4);
    209
    210			if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
    211			    trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
    212			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
    213				int ret;
    214
    215				ret = iwl_pnvm_handle_section(trans, data, len);
    216				if (!ret)
    217					return 0;
    218			} else {
    219				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
    220			}
    221		} else {
    222			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
    223			len -= ALIGN(tlv_len, 4);
    224		}
    225	}
    226
    227	return -ENOENT;
    228}
    229
    230static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
    231{
    232	const struct firmware *pnvm;
    233	char pnvm_name[MAX_PNVM_NAME];
    234	size_t new_len;
    235	int ret;
    236
    237	iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
    238
    239	ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
    240	if (ret) {
    241		IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
    242			     pnvm_name, ret);
    243		return ret;
    244	}
    245
    246	new_len = pnvm->size;
    247	*data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
    248	release_firmware(pnvm);
    249
    250	if (!*data)
    251		return -ENOMEM;
    252
    253	*len = new_len;
    254
    255	return 0;
    256}
    257
    258int iwl_pnvm_load(struct iwl_trans *trans,
    259		  struct iwl_notif_wait_data *notif_wait)
    260{
    261	u8 *data;
    262	size_t len;
    263	struct pnvm_sku_package *package;
    264	struct iwl_notification_wait pnvm_wait;
    265	static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
    266						PNVM_INIT_COMPLETE_NTFY) };
    267	int ret;
    268
    269	/* if the SKU_ID is empty, there's nothing to do */
    270	if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
    271		return 0;
    272
    273	/*
    274	 * If we already loaded (or tried to load) it before, we just
    275	 * need to set it again.
    276	 */
    277	if (trans->pnvm_loaded) {
    278		ret = iwl_trans_set_pnvm(trans, NULL, 0);
    279		if (ret)
    280			return ret;
    281		goto skip_parse;
    282	}
    283
    284	/* First attempt to get the PNVM from BIOS */
    285	package = iwl_uefi_get_pnvm(trans, &len);
    286	if (!IS_ERR_OR_NULL(package)) {
    287		if (len >= sizeof(*package)) {
    288			/* we need only the data */
    289			len -= sizeof(*package);
    290			data = kmemdup(package->data, len, GFP_KERNEL);
    291		} else {
    292			data = NULL;
    293		}
    294
    295		/* free package regardless of whether kmemdup succeeded */
    296		kfree(package);
    297
    298		if (data)
    299			goto parse;
    300	}
    301
    302	/* If it's not available, try from the filesystem */
    303	ret = iwl_pnvm_get_from_fs(trans, &data, &len);
    304	if (ret) {
    305		/*
    306		 * Pretend we've loaded it - at least we've tried and
    307		 * couldn't load it at all, so there's no point in
    308		 * trying again over and over.
    309		 */
    310		trans->pnvm_loaded = true;
    311
    312		goto skip_parse;
    313	}
    314
    315parse:
    316	iwl_pnvm_parse(trans, data, len);
    317
    318	kfree(data);
    319
    320skip_parse:
    321	data = NULL;
    322	/* now try to get the reduce power table, if not loaded yet */
    323	if (!trans->reduce_power_loaded) {
    324		data = iwl_uefi_get_reduced_power(trans, &len);
    325		if (IS_ERR_OR_NULL(data)) {
    326			/*
    327			 * Pretend we've loaded it - at least we've tried and
    328			 * couldn't load it at all, so there's no point in
    329			 * trying again over and over.
    330			 */
    331			trans->reduce_power_loaded = true;
    332
    333			goto skip_reduce_power;
    334		}
    335	}
    336
    337	ret = iwl_trans_set_reduce_power(trans, data, len);
    338	if (ret)
    339		IWL_DEBUG_FW(trans,
    340			     "Failed to set reduce power table %d\n",
    341			     ret);
    342	kfree(data);
    343
    344skip_reduce_power:
    345	iwl_init_notification_wait(notif_wait, &pnvm_wait,
    346				   ntf_cmds, ARRAY_SIZE(ntf_cmds),
    347				   iwl_pnvm_complete_fn, trans);
    348
    349	/* kick the doorbell */
    350	iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
    351			    UREG_DOORBELL_TO_ISR6_PNVM);
    352
    353	return iwl_wait_notification(notif_wait, &pnvm_wait,
    354				     MVM_UCODE_PNVM_TIMEOUT);
    355}
    356IWL_EXPORT_SYMBOL(iwl_pnvm_load);