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

iwl-phy-db.c (10517B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * Copyright (C) 2005-2014, 2020-2021 Intel Corporation
      4 * Copyright (C) 2016 Intel Deutschland GmbH
      5 */
      6#include <linux/slab.h>
      7#include <linux/string.h>
      8#include <linux/export.h>
      9
     10#include "iwl-drv.h"
     11#include "iwl-phy-db.h"
     12#include "iwl-debug.h"
     13#include "iwl-op-mode.h"
     14#include "iwl-trans.h"
     15
     16struct iwl_phy_db_entry {
     17	u16	size;
     18	u8	*data;
     19};
     20
     21/**
     22 * struct iwl_phy_db - stores phy configuration and calibration data.
     23 *
     24 * @cfg: phy configuration.
     25 * @calib_nch: non channel specific calibration data.
     26 * @n_group_papd: number of entries in papd channel group.
     27 * @calib_ch_group_papd: calibration data related to papd channel group.
     28 * @n_group_txp: number of entries in tx power channel group.
     29 * @calib_ch_group_txp: calibration data related to tx power chanel group.
     30 * @trans: transport layer
     31 */
     32struct iwl_phy_db {
     33	struct iwl_phy_db_entry	cfg;
     34	struct iwl_phy_db_entry	calib_nch;
     35	int n_group_papd;
     36	struct iwl_phy_db_entry	*calib_ch_group_papd;
     37	int n_group_txp;
     38	struct iwl_phy_db_entry	*calib_ch_group_txp;
     39
     40	struct iwl_trans *trans;
     41};
     42
     43enum iwl_phy_db_section_type {
     44	IWL_PHY_DB_CFG = 1,
     45	IWL_PHY_DB_CALIB_NCH,
     46	IWL_PHY_DB_UNUSED,
     47	IWL_PHY_DB_CALIB_CHG_PAPD,
     48	IWL_PHY_DB_CALIB_CHG_TXP,
     49	IWL_PHY_DB_MAX
     50};
     51
     52#define PHY_DB_CMD 0x6c
     53
     54/* for parsing of tx power channel group data that comes from the firmware*/
     55struct iwl_phy_db_chg_txp {
     56	__le32 space;
     57	__le16 max_channel_idx;
     58} __packed;
     59
     60struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
     61{
     62	struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
     63					    GFP_KERNEL);
     64
     65	if (!phy_db)
     66		return phy_db;
     67
     68	phy_db->trans = trans;
     69
     70	phy_db->n_group_txp = -1;
     71	phy_db->n_group_papd = -1;
     72
     73	/* TODO: add default values of the phy db. */
     74	return phy_db;
     75}
     76IWL_EXPORT_SYMBOL(iwl_phy_db_init);
     77
     78/*
     79 * get phy db section: returns a pointer to a phy db section specified by
     80 * type and channel group id.
     81 */
     82static struct iwl_phy_db_entry *
     83iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
     84		       enum iwl_phy_db_section_type type,
     85		       u16 chg_id)
     86{
     87	if (!phy_db || type >= IWL_PHY_DB_MAX)
     88		return NULL;
     89
     90	switch (type) {
     91	case IWL_PHY_DB_CFG:
     92		return &phy_db->cfg;
     93	case IWL_PHY_DB_CALIB_NCH:
     94		return &phy_db->calib_nch;
     95	case IWL_PHY_DB_CALIB_CHG_PAPD:
     96		if (chg_id >= phy_db->n_group_papd)
     97			return NULL;
     98		return &phy_db->calib_ch_group_papd[chg_id];
     99	case IWL_PHY_DB_CALIB_CHG_TXP:
    100		if (chg_id >= phy_db->n_group_txp)
    101			return NULL;
    102		return &phy_db->calib_ch_group_txp[chg_id];
    103	default:
    104		return NULL;
    105	}
    106	return NULL;
    107}
    108
    109static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
    110				    enum iwl_phy_db_section_type type,
    111				    u16 chg_id)
    112{
    113	struct iwl_phy_db_entry *entry =
    114				iwl_phy_db_get_section(phy_db, type, chg_id);
    115	if (!entry)
    116		return;
    117
    118	kfree(entry->data);
    119	entry->data = NULL;
    120	entry->size = 0;
    121}
    122
    123void iwl_phy_db_free(struct iwl_phy_db *phy_db)
    124{
    125	int i;
    126
    127	if (!phy_db)
    128		return;
    129
    130	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
    131	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
    132
    133	for (i = 0; i < phy_db->n_group_papd; i++)
    134		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
    135	kfree(phy_db->calib_ch_group_papd);
    136
    137	for (i = 0; i < phy_db->n_group_txp; i++)
    138		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
    139	kfree(phy_db->calib_ch_group_txp);
    140
    141	kfree(phy_db);
    142}
    143IWL_EXPORT_SYMBOL(iwl_phy_db_free);
    144
    145int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
    146			   struct iwl_rx_packet *pkt)
    147{
    148	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
    149	struct iwl_calib_res_notif_phy_db *phy_db_notif =
    150			(struct iwl_calib_res_notif_phy_db *)pkt->data;
    151	enum iwl_phy_db_section_type type;
    152	u16 size;
    153	struct iwl_phy_db_entry *entry;
    154	u16 chg_id = 0;
    155
    156	if (pkt_len < sizeof(*phy_db_notif))
    157		return -EINVAL;
    158
    159	type = le16_to_cpu(phy_db_notif->type);
    160	size = le16_to_cpu(phy_db_notif->length);
    161
    162	if (pkt_len < sizeof(*phy_db_notif) + size)
    163		return -EINVAL;
    164
    165	if (!phy_db)
    166		return -EINVAL;
    167
    168	if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
    169		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
    170		if (phy_db && !phy_db->calib_ch_group_papd) {
    171			/*
    172			 * Firmware sends the largest index first, so we can use
    173			 * it to know how much we should allocate.
    174			 */
    175			phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
    176							      sizeof(struct iwl_phy_db_entry),
    177							      GFP_ATOMIC);
    178			if (!phy_db->calib_ch_group_papd)
    179				return -ENOMEM;
    180			phy_db->n_group_papd = chg_id + 1;
    181		}
    182	} else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
    183		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
    184		if (phy_db && !phy_db->calib_ch_group_txp) {
    185			/*
    186			 * Firmware sends the largest index first, so we can use
    187			 * it to know how much we should allocate.
    188			 */
    189			phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
    190							     sizeof(struct iwl_phy_db_entry),
    191							     GFP_ATOMIC);
    192			if (!phy_db->calib_ch_group_txp)
    193				return -ENOMEM;
    194			phy_db->n_group_txp = chg_id + 1;
    195		}
    196	}
    197
    198	entry = iwl_phy_db_get_section(phy_db, type, chg_id);
    199	if (!entry)
    200		return -EINVAL;
    201
    202	kfree(entry->data);
    203	entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
    204	if (!entry->data) {
    205		entry->size = 0;
    206		return -ENOMEM;
    207	}
    208
    209	entry->size = size;
    210
    211	IWL_DEBUG_INFO(phy_db->trans,
    212		       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
    213		       __func__, __LINE__, type, size);
    214
    215	return 0;
    216}
    217IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
    218
    219static int is_valid_channel(u16 ch_id)
    220{
    221	if (ch_id <= 14 ||
    222	    (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
    223	    (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
    224	    (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
    225		return 1;
    226	return 0;
    227}
    228
    229static u8 ch_id_to_ch_index(u16 ch_id)
    230{
    231	if (WARN_ON(!is_valid_channel(ch_id)))
    232		return 0xff;
    233
    234	if (ch_id <= 14)
    235		return ch_id - 1;
    236	if (ch_id <= 64)
    237		return (ch_id + 20) / 4;
    238	if (ch_id <= 140)
    239		return (ch_id - 12) / 4;
    240	return (ch_id - 13) / 4;
    241}
    242
    243
    244static u16 channel_id_to_papd(u16 ch_id)
    245{
    246	if (WARN_ON(!is_valid_channel(ch_id)))
    247		return 0xff;
    248
    249	if (1 <= ch_id && ch_id <= 14)
    250		return 0;
    251	if (36 <= ch_id && ch_id <= 64)
    252		return 1;
    253	if (100 <= ch_id && ch_id <= 140)
    254		return 2;
    255	return 3;
    256}
    257
    258static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
    259{
    260	struct iwl_phy_db_chg_txp *txp_chg;
    261	int i;
    262	u8 ch_index = ch_id_to_ch_index(ch_id);
    263	if (ch_index == 0xff)
    264		return 0xff;
    265
    266	for (i = 0; i < phy_db->n_group_txp; i++) {
    267		txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
    268		if (!txp_chg)
    269			return 0xff;
    270		/*
    271		 * Looking for the first channel group that its max channel is
    272		 * higher then wanted channel.
    273		 */
    274		if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
    275			return i;
    276	}
    277	return 0xff;
    278}
    279static
    280int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
    281				u32 type, u8 **data, u16 *size, u16 ch_id)
    282{
    283	struct iwl_phy_db_entry *entry;
    284	u16 ch_group_id = 0;
    285
    286	if (!phy_db)
    287		return -EINVAL;
    288
    289	/* find wanted channel group */
    290	if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
    291		ch_group_id = channel_id_to_papd(ch_id);
    292	else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
    293		ch_group_id = channel_id_to_txp(phy_db, ch_id);
    294
    295	entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
    296	if (!entry)
    297		return -EINVAL;
    298
    299	*data = entry->data;
    300	*size = entry->size;
    301
    302	IWL_DEBUG_INFO(phy_db->trans,
    303		       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
    304		       __func__, __LINE__, type, *size);
    305
    306	return 0;
    307}
    308
    309static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
    310			       u16 length, void *data)
    311{
    312	struct iwl_phy_db_cmd phy_db_cmd;
    313	struct iwl_host_cmd cmd = {
    314		.id = PHY_DB_CMD,
    315	};
    316
    317	IWL_DEBUG_INFO(phy_db->trans,
    318		       "Sending PHY-DB hcmd of type %d, of length %d\n",
    319		       type, length);
    320
    321	/* Set phy db cmd variables */
    322	phy_db_cmd.type = cpu_to_le16(type);
    323	phy_db_cmd.length = cpu_to_le16(length);
    324
    325	/* Set hcmd variables */
    326	cmd.data[0] = &phy_db_cmd;
    327	cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
    328	cmd.data[1] = data;
    329	cmd.len[1] = length;
    330	cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
    331
    332	return iwl_trans_send_cmd(phy_db->trans, &cmd);
    333}
    334
    335static int iwl_phy_db_send_all_channel_groups(
    336					struct iwl_phy_db *phy_db,
    337					enum iwl_phy_db_section_type type,
    338					u8 max_ch_groups)
    339{
    340	u16 i;
    341	int err;
    342	struct iwl_phy_db_entry *entry;
    343
    344	/* Send all the  channel specific groups to operational fw */
    345	for (i = 0; i < max_ch_groups; i++) {
    346		entry = iwl_phy_db_get_section(phy_db,
    347					       type,
    348					       i);
    349		if (!entry)
    350			return -EINVAL;
    351
    352		if (!entry->size)
    353			continue;
    354
    355		/* Send the requested PHY DB section */
    356		err = iwl_send_phy_db_cmd(phy_db,
    357					  type,
    358					  entry->size,
    359					  entry->data);
    360		if (err) {
    361			IWL_ERR(phy_db->trans,
    362				"Can't SEND phy_db section %d (%d), err %d\n",
    363				type, i, err);
    364			return err;
    365		}
    366
    367		IWL_DEBUG_INFO(phy_db->trans,
    368			       "Sent PHY_DB HCMD, type = %d num = %d\n",
    369			       type, i);
    370	}
    371
    372	return 0;
    373}
    374
    375int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
    376{
    377	u8 *data = NULL;
    378	u16 size = 0;
    379	int err;
    380
    381	IWL_DEBUG_INFO(phy_db->trans,
    382		       "Sending phy db data and configuration to runtime image\n");
    383
    384	/* Send PHY DB CFG section */
    385	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
    386					  &data, &size, 0);
    387	if (err) {
    388		IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
    389		return err;
    390	}
    391
    392	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
    393	if (err) {
    394		IWL_ERR(phy_db->trans,
    395			"Cannot send HCMD of  Phy DB cfg section\n");
    396		return err;
    397	}
    398
    399	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
    400					  &data, &size, 0);
    401	if (err) {
    402		IWL_ERR(phy_db->trans,
    403			"Cannot get Phy DB non specific channel section\n");
    404		return err;
    405	}
    406
    407	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
    408	if (err) {
    409		IWL_ERR(phy_db->trans,
    410			"Cannot send HCMD of Phy DB non specific channel section\n");
    411		return err;
    412	}
    413
    414	/* Send all the TXP channel specific data */
    415	err = iwl_phy_db_send_all_channel_groups(phy_db,
    416						 IWL_PHY_DB_CALIB_CHG_PAPD,
    417						 phy_db->n_group_papd);
    418	if (err) {
    419		IWL_ERR(phy_db->trans,
    420			"Cannot send channel specific PAPD groups\n");
    421		return err;
    422	}
    423
    424	/* Send all the TXP channel specific data */
    425	err = iwl_phy_db_send_all_channel_groups(phy_db,
    426						 IWL_PHY_DB_CALIB_CHG_TXP,
    427						 phy_db->n_group_txp);
    428	if (err) {
    429		IWL_ERR(phy_db->trans,
    430			"Cannot send channel specific TX power groups\n");
    431		return err;
    432	}
    433
    434	IWL_DEBUG_INFO(phy_db->trans,
    435		       "Finished sending phy db non channel data\n");
    436	return 0;
    437}
    438IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);