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

bmi.c (13142B)


      1/*
      2 * Copyright (c) 2004-2011 Atheros Communications Inc.
      3 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
      4 *
      5 * Permission to use, copy, modify, and/or distribute this software for any
      6 * purpose with or without fee is hereby granted, provided that the above
      7 * copyright notice and this permission notice appear in all copies.
      8 *
      9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16 */
     17
     18#include "core.h"
     19#include "hif-ops.h"
     20#include "target.h"
     21#include "debug.h"
     22
     23int ath6kl_bmi_done(struct ath6kl *ar)
     24{
     25	int ret;
     26	u32 cid = BMI_DONE;
     27
     28	if (ar->bmi.done_sent) {
     29		ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
     30		return 0;
     31	}
     32
     33	ar->bmi.done_sent = true;
     34
     35	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
     36	if (ret) {
     37		ath6kl_err("Unable to send bmi done: %d\n", ret);
     38		return ret;
     39	}
     40
     41	return 0;
     42}
     43
     44int ath6kl_bmi_get_target_info(struct ath6kl *ar,
     45			       struct ath6kl_bmi_target_info *targ_info)
     46{
     47	int ret;
     48	u32 cid = BMI_GET_TARGET_INFO;
     49
     50	if (ar->bmi.done_sent) {
     51		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
     52		return -EACCES;
     53	}
     54
     55	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
     56	if (ret) {
     57		ath6kl_err("Unable to send get target info: %d\n", ret);
     58		return ret;
     59	}
     60
     61	if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
     62		ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
     63					  sizeof(*targ_info));
     64	} else {
     65		ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
     66				sizeof(targ_info->version));
     67	}
     68
     69	if (ret) {
     70		ath6kl_err("Unable to recv target info: %d\n", ret);
     71		return ret;
     72	}
     73
     74	if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
     75		/* Determine how many bytes are in the Target's targ_info */
     76		ret = ath6kl_hif_bmi_read(ar,
     77				   (u8 *)&targ_info->byte_count,
     78				   sizeof(targ_info->byte_count));
     79		if (ret) {
     80			ath6kl_err("unable to read target info byte count: %d\n",
     81				   ret);
     82			return ret;
     83		}
     84
     85		/*
     86		 * The target's targ_info doesn't match the host's targ_info.
     87		 * We need to do some backwards compatibility to make this work.
     88		 */
     89		if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
     90			WARN_ON(1);
     91			return -EINVAL;
     92		}
     93
     94		/* Read the remainder of the targ_info */
     95		ret = ath6kl_hif_bmi_read(ar,
     96				   ((u8 *)targ_info) +
     97				   sizeof(targ_info->byte_count),
     98				   sizeof(*targ_info) -
     99				   sizeof(targ_info->byte_count));
    100
    101		if (ret) {
    102			ath6kl_err("Unable to read target info (%d bytes): %d\n",
    103				   targ_info->byte_count, ret);
    104			return ret;
    105		}
    106	}
    107
    108	ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
    109		   targ_info->version, targ_info->type);
    110
    111	return 0;
    112}
    113
    114int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
    115{
    116	u32 cid = BMI_READ_MEMORY;
    117	int ret;
    118	u32 offset;
    119	u32 len_remain, rx_len;
    120	u16 size;
    121
    122	if (ar->bmi.done_sent) {
    123		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    124		return -EACCES;
    125	}
    126
    127	size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
    128	if (size > ar->bmi.max_cmd_size) {
    129		WARN_ON(1);
    130		return -EINVAL;
    131	}
    132	memset(ar->bmi.cmd_buf, 0, size);
    133
    134	ath6kl_dbg(ATH6KL_DBG_BMI,
    135		   "bmi read memory: device: addr: 0x%x, len: %d\n",
    136		   addr, len);
    137
    138	len_remain = len;
    139
    140	while (len_remain) {
    141		rx_len = (len_remain < ar->bmi.max_data_size) ?
    142					len_remain : ar->bmi.max_data_size;
    143		offset = 0;
    144		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    145		offset += sizeof(cid);
    146		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    147		offset += sizeof(addr);
    148		memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
    149		offset += sizeof(len);
    150
    151		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    152		if (ret) {
    153			ath6kl_err("Unable to write to the device: %d\n",
    154				   ret);
    155			return ret;
    156		}
    157		ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
    158		if (ret) {
    159			ath6kl_err("Unable to read from the device: %d\n",
    160				   ret);
    161			return ret;
    162		}
    163		memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
    164		len_remain -= rx_len; addr += rx_len;
    165	}
    166
    167	return 0;
    168}
    169
    170int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
    171{
    172	u32 cid = BMI_WRITE_MEMORY;
    173	int ret;
    174	u32 offset;
    175	u32 len_remain, tx_len;
    176	const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
    177	u8 aligned_buf[400];
    178	u8 *src;
    179
    180	if (ar->bmi.done_sent) {
    181		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    182		return -EACCES;
    183	}
    184
    185	if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
    186		WARN_ON(1);
    187		return -EINVAL;
    188	}
    189
    190	if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
    191		return -E2BIG;
    192
    193	memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
    194
    195	ath6kl_dbg(ATH6KL_DBG_BMI,
    196		   "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
    197
    198	len_remain = len;
    199	while (len_remain) {
    200		src = &buf[len - len_remain];
    201
    202		if (len_remain < (ar->bmi.max_data_size - header)) {
    203			if (len_remain & 3) {
    204				/* align it with 4 bytes */
    205				len_remain = len_remain +
    206					     (4 - (len_remain & 3));
    207				memcpy(aligned_buf, src, len_remain);
    208				src = aligned_buf;
    209			}
    210			tx_len = len_remain;
    211		} else {
    212			tx_len = (ar->bmi.max_data_size - header);
    213		}
    214
    215		offset = 0;
    216		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    217		offset += sizeof(cid);
    218		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    219		offset += sizeof(addr);
    220		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
    221		offset += sizeof(tx_len);
    222		memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
    223		offset += tx_len;
    224
    225		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    226		if (ret) {
    227			ath6kl_err("Unable to write to the device: %d\n",
    228				   ret);
    229			return ret;
    230		}
    231		len_remain -= tx_len; addr += tx_len;
    232	}
    233
    234	return 0;
    235}
    236
    237int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
    238{
    239	u32 cid = BMI_EXECUTE;
    240	int ret;
    241	u32 offset;
    242	u16 size;
    243
    244	if (ar->bmi.done_sent) {
    245		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    246		return -EACCES;
    247	}
    248
    249	size = sizeof(cid) + sizeof(addr) + sizeof(param);
    250	if (size > ar->bmi.max_cmd_size) {
    251		WARN_ON(1);
    252		return -EINVAL;
    253	}
    254	memset(ar->bmi.cmd_buf, 0, size);
    255
    256	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
    257		   addr, *param);
    258
    259	offset = 0;
    260	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    261	offset += sizeof(cid);
    262	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    263	offset += sizeof(addr);
    264	memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
    265	offset += sizeof(*param);
    266
    267	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    268	if (ret) {
    269		ath6kl_err("Unable to write to the device: %d\n", ret);
    270		return ret;
    271	}
    272
    273	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
    274	if (ret) {
    275		ath6kl_err("Unable to read from the device: %d\n", ret);
    276		return ret;
    277	}
    278
    279	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
    280
    281	return 0;
    282}
    283
    284int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
    285{
    286	u32 cid = BMI_SET_APP_START;
    287	int ret;
    288	u32 offset;
    289	u16 size;
    290
    291	if (ar->bmi.done_sent) {
    292		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    293		return -EACCES;
    294	}
    295
    296	size = sizeof(cid) + sizeof(addr);
    297	if (size > ar->bmi.max_cmd_size) {
    298		WARN_ON(1);
    299		return -EINVAL;
    300	}
    301	memset(ar->bmi.cmd_buf, 0, size);
    302
    303	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
    304
    305	offset = 0;
    306	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    307	offset += sizeof(cid);
    308	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    309	offset += sizeof(addr);
    310
    311	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    312	if (ret) {
    313		ath6kl_err("Unable to write to the device: %d\n", ret);
    314		return ret;
    315	}
    316
    317	return 0;
    318}
    319
    320int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
    321{
    322	u32 cid = BMI_READ_SOC_REGISTER;
    323	int ret;
    324	u32 offset;
    325	u16 size;
    326
    327	if (ar->bmi.done_sent) {
    328		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    329		return -EACCES;
    330	}
    331
    332	size = sizeof(cid) + sizeof(addr);
    333	if (size > ar->bmi.max_cmd_size) {
    334		WARN_ON(1);
    335		return -EINVAL;
    336	}
    337	memset(ar->bmi.cmd_buf, 0, size);
    338
    339	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
    340
    341	offset = 0;
    342	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    343	offset += sizeof(cid);
    344	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    345	offset += sizeof(addr);
    346
    347	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    348	if (ret) {
    349		ath6kl_err("Unable to write to the device: %d\n", ret);
    350		return ret;
    351	}
    352
    353	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
    354	if (ret) {
    355		ath6kl_err("Unable to read from the device: %d\n", ret);
    356		return ret;
    357	}
    358	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
    359
    360	return 0;
    361}
    362
    363int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
    364{
    365	u32 cid = BMI_WRITE_SOC_REGISTER;
    366	int ret;
    367	u32 offset;
    368	u16 size;
    369
    370	if (ar->bmi.done_sent) {
    371		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    372		return -EACCES;
    373	}
    374
    375	size = sizeof(cid) + sizeof(addr) + sizeof(param);
    376	if (size > ar->bmi.max_cmd_size) {
    377		WARN_ON(1);
    378		return -EINVAL;
    379	}
    380	memset(ar->bmi.cmd_buf, 0, size);
    381
    382	ath6kl_dbg(ATH6KL_DBG_BMI,
    383		   "bmi write SOC reg: addr: 0x%x, param: %d\n",
    384		    addr, param);
    385
    386	offset = 0;
    387	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    388	offset += sizeof(cid);
    389	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    390	offset += sizeof(addr);
    391	memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
    392	offset += sizeof(param);
    393
    394	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    395	if (ret) {
    396		ath6kl_err("Unable to write to the device: %d\n", ret);
    397		return ret;
    398	}
    399
    400	return 0;
    401}
    402
    403int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
    404{
    405	u32 cid = BMI_LZ_DATA;
    406	int ret;
    407	u32 offset;
    408	u32 len_remain, tx_len;
    409	const u32 header = sizeof(cid) + sizeof(len);
    410	u16 size;
    411
    412	if (ar->bmi.done_sent) {
    413		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    414		return -EACCES;
    415	}
    416
    417	size = ar->bmi.max_data_size + header;
    418	if (size > ar->bmi.max_cmd_size) {
    419		WARN_ON(1);
    420		return -EINVAL;
    421	}
    422	memset(ar->bmi.cmd_buf, 0, size);
    423
    424	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
    425		   len);
    426
    427	len_remain = len;
    428	while (len_remain) {
    429		tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
    430			  len_remain : (ar->bmi.max_data_size - header);
    431
    432		offset = 0;
    433		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    434		offset += sizeof(cid);
    435		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
    436		offset += sizeof(tx_len);
    437		memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
    438		       tx_len);
    439		offset += tx_len;
    440
    441		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    442		if (ret) {
    443			ath6kl_err("Unable to write to the device: %d\n",
    444				   ret);
    445			return ret;
    446		}
    447
    448		len_remain -= tx_len;
    449	}
    450
    451	return 0;
    452}
    453
    454int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
    455{
    456	u32 cid = BMI_LZ_STREAM_START;
    457	int ret;
    458	u32 offset;
    459	u16 size;
    460
    461	if (ar->bmi.done_sent) {
    462		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
    463		return -EACCES;
    464	}
    465
    466	size = sizeof(cid) + sizeof(addr);
    467	if (size > ar->bmi.max_cmd_size) {
    468		WARN_ON(1);
    469		return -EINVAL;
    470	}
    471	memset(ar->bmi.cmd_buf, 0, size);
    472
    473	ath6kl_dbg(ATH6KL_DBG_BMI,
    474		   "bmi LZ stream start: addr: 0x%x)\n",
    475		    addr);
    476
    477	offset = 0;
    478	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
    479	offset += sizeof(cid);
    480	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
    481	offset += sizeof(addr);
    482
    483	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
    484	if (ret) {
    485		ath6kl_err("Unable to start LZ stream to the device: %d\n",
    486			   ret);
    487		return ret;
    488	}
    489
    490	return 0;
    491}
    492
    493int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
    494{
    495	int ret;
    496	u32 last_word = 0;
    497	u32 last_word_offset = len & ~0x3;
    498	u32 unaligned_bytes = len & 0x3;
    499
    500	ret = ath6kl_bmi_lz_stream_start(ar, addr);
    501	if (ret)
    502		return ret;
    503
    504	if (unaligned_bytes) {
    505		/* copy the last word into a zero padded buffer */
    506		memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
    507	}
    508
    509	ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
    510	if (ret)
    511		return ret;
    512
    513	if (unaligned_bytes)
    514		ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
    515
    516	if (!ret) {
    517		/* Close compressed stream and open a new (fake) one.
    518		 * This serves mainly to flush Target caches. */
    519		ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
    520	}
    521	return ret;
    522}
    523
    524void ath6kl_bmi_reset(struct ath6kl *ar)
    525{
    526	ar->bmi.done_sent = false;
    527}
    528
    529int ath6kl_bmi_init(struct ath6kl *ar)
    530{
    531	if (WARN_ON(ar->bmi.max_data_size == 0))
    532		return -EINVAL;
    533
    534	/* cmd + addr + len + data_size */
    535	ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
    536
    537	ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL);
    538	if (!ar->bmi.cmd_buf)
    539		return -ENOMEM;
    540
    541	return 0;
    542}
    543
    544void ath6kl_bmi_cleanup(struct ath6kl *ar)
    545{
    546	kfree(ar->bmi.cmd_buf);
    547	ar->bmi.cmd_buf = NULL;
    548}