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

firmware.c (7711B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Generic driver for NXP NCI NFC chips
      4 *
      5 * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
      6 *
      7 * Author: Clément Perrochaud <clement.perrochaud@nxp.com>
      8 *
      9 * Derived from PN544 device driver:
     10 * Copyright (C) 2012  Intel Corporation. All rights reserved.
     11 */
     12
     13#include <linux/completion.h>
     14#include <linux/firmware.h>
     15#include <linux/nfc.h>
     16#include <asm/unaligned.h>
     17
     18#include "nxp-nci.h"
     19
     20/* Crypto operations can take up to 30 seconds */
     21#define NXP_NCI_FW_ANSWER_TIMEOUT	msecs_to_jiffies(30000)
     22
     23#define NXP_NCI_FW_CMD_RESET		0xF0
     24#define NXP_NCI_FW_CMD_GETVERSION	0xF1
     25#define NXP_NCI_FW_CMD_CHECKINTEGRITY	0xE0
     26#define NXP_NCI_FW_CMD_WRITE		0xC0
     27#define NXP_NCI_FW_CMD_READ		0xA2
     28#define NXP_NCI_FW_CMD_GETSESSIONSTATE	0xF2
     29#define NXP_NCI_FW_CMD_LOG		0xA7
     30#define NXP_NCI_FW_CMD_FORCE		0xD0
     31#define NXP_NCI_FW_CMD_GET_DIE_ID	0xF4
     32
     33#define NXP_NCI_FW_CHUNK_FLAG	0x0400
     34
     35#define NXP_NCI_FW_RESULT_OK				0x00
     36#define NXP_NCI_FW_RESULT_INVALID_ADDR			0x01
     37#define NXP_NCI_FW_RESULT_GENERIC_ERROR			0x02
     38#define NXP_NCI_FW_RESULT_UNKNOWN_CMD			0x0B
     39#define NXP_NCI_FW_RESULT_ABORTED_CMD			0x0C
     40#define NXP_NCI_FW_RESULT_PLL_ERROR			0x0D
     41#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR		0x1E
     42#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR		0x1F
     43#define NXP_NCI_FW_RESULT_MEM_BSY			0x20
     44#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR		0x21
     45#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR	0x24
     46#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR		0x28
     47#define NXP_NCI_FW_RESULT_SFWU_DEGRADED			0x2A
     48#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK		0x2D
     49#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK		0x2E
     50#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5	0xC5
     51
     52void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result)
     53{
     54	struct nxp_nci_fw_info *fw_info = &info->fw_info;
     55	int r;
     56
     57	if (info->phy_ops->set_mode) {
     58		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
     59		if (r < 0 && result == 0)
     60			result = -r;
     61	}
     62
     63	info->mode = NXP_NCI_MODE_COLD;
     64
     65	if (fw_info->fw) {
     66		release_firmware(fw_info->fw);
     67		fw_info->fw = NULL;
     68	}
     69
     70	nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result);
     71}
     72
     73/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */
     74static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len)
     75{
     76	u16 crc = 0xffff;
     77
     78	while (len--) {
     79		crc = ((crc >> 8) | (crc << 8)) ^ *buffer++;
     80		crc ^= (crc & 0xff) >> 4;
     81		crc ^= (crc & 0xff) << 12;
     82		crc ^= (crc & 0xff) << 5;
     83	}
     84
     85	return crc;
     86}
     87
     88static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info)
     89{
     90	struct nxp_nci_fw_info *fw_info = &info->fw_info;
     91	u16 header, crc;
     92	struct sk_buff *skb;
     93	size_t chunk_len;
     94	size_t remaining_len;
     95	int r;
     96
     97	skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL);
     98	if (!skb)
     99		return -ENOMEM;
    100
    101	chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN;
    102	remaining_len = fw_info->frame_size - fw_info->written;
    103
    104	if (remaining_len > chunk_len) {
    105		header = NXP_NCI_FW_CHUNK_FLAG;
    106	} else {
    107		chunk_len = remaining_len;
    108		header = 0x0000;
    109	}
    110
    111	header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK;
    112	put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN));
    113
    114	skb_put_data(skb, fw_info->data + fw_info->written, chunk_len);
    115
    116	crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN);
    117	put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN));
    118
    119	r = info->phy_ops->write(info->phy_id, skb);
    120	if (r >= 0)
    121		r = chunk_len;
    122
    123	kfree_skb(skb);
    124
    125	return r;
    126}
    127
    128static int nxp_nci_fw_send(struct nxp_nci_info *info)
    129{
    130	struct nxp_nci_fw_info *fw_info = &info->fw_info;
    131	long completion_rc;
    132	int r;
    133
    134	reinit_completion(&fw_info->cmd_completion);
    135
    136	if (fw_info->written == 0) {
    137		fw_info->frame_size = get_unaligned_be16(fw_info->data) &
    138				      NXP_NCI_FW_FRAME_LEN_MASK;
    139		fw_info->data += NXP_NCI_FW_HDR_LEN;
    140		fw_info->size -= NXP_NCI_FW_HDR_LEN;
    141	}
    142
    143	if (fw_info->frame_size > fw_info->size)
    144		return -EMSGSIZE;
    145
    146	r = nxp_nci_fw_send_chunk(info);
    147	if (r < 0)
    148		return r;
    149
    150	fw_info->written += r;
    151
    152	if (*fw_info->data == NXP_NCI_FW_CMD_RESET) {
    153		fw_info->cmd_result = 0;
    154		if (fw_info->fw)
    155			schedule_work(&fw_info->work);
    156	} else {
    157		completion_rc = wait_for_completion_interruptible_timeout(
    158			&fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT);
    159		if (completion_rc == 0)
    160			return -ETIMEDOUT;
    161	}
    162
    163	return 0;
    164}
    165
    166void nxp_nci_fw_work(struct work_struct *work)
    167{
    168	struct nxp_nci_info *info;
    169	struct nxp_nci_fw_info *fw_info;
    170	int r;
    171
    172	fw_info = container_of(work, struct nxp_nci_fw_info, work);
    173	info = container_of(fw_info, struct nxp_nci_info, fw_info);
    174
    175	mutex_lock(&info->info_lock);
    176
    177	r = fw_info->cmd_result;
    178	if (r < 0)
    179		goto exit_work;
    180
    181	if (fw_info->written == fw_info->frame_size) {
    182		fw_info->data += fw_info->frame_size;
    183		fw_info->size -= fw_info->frame_size;
    184		fw_info->written = 0;
    185	}
    186
    187	if (fw_info->size > 0)
    188		r = nxp_nci_fw_send(info);
    189
    190exit_work:
    191	if (r < 0 || fw_info->size == 0)
    192		nxp_nci_fw_work_complete(info, r);
    193	mutex_unlock(&info->info_lock);
    194}
    195
    196int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name)
    197{
    198	struct nxp_nci_info *info = nci_get_drvdata(ndev);
    199	struct nxp_nci_fw_info *fw_info = &info->fw_info;
    200	int r;
    201
    202	mutex_lock(&info->info_lock);
    203
    204	if (!info->phy_ops->set_mode || !info->phy_ops->write) {
    205		r = -ENOTSUPP;
    206		goto fw_download_exit;
    207	}
    208
    209	if (!firmware_name || firmware_name[0] == '\0') {
    210		r = -EINVAL;
    211		goto fw_download_exit;
    212	}
    213
    214	strcpy(fw_info->name, firmware_name);
    215
    216	r = request_firmware(&fw_info->fw, firmware_name,
    217			     ndev->nfc_dev->dev.parent);
    218	if (r < 0)
    219		goto fw_download_exit;
    220
    221	r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW);
    222	if (r < 0) {
    223		release_firmware(fw_info->fw);
    224		goto fw_download_exit;
    225	}
    226
    227	info->mode = NXP_NCI_MODE_FW;
    228
    229	fw_info->data = fw_info->fw->data;
    230	fw_info->size = fw_info->fw->size;
    231	fw_info->written = 0;
    232	fw_info->frame_size = 0;
    233	fw_info->cmd_result = 0;
    234
    235	schedule_work(&fw_info->work);
    236
    237fw_download_exit:
    238	mutex_unlock(&info->info_lock);
    239	return r;
    240}
    241
    242static int nxp_nci_fw_read_status(u8 stat)
    243{
    244	switch (stat) {
    245	case NXP_NCI_FW_RESULT_OK:
    246		return 0;
    247	case NXP_NCI_FW_RESULT_INVALID_ADDR:
    248		return -EINVAL;
    249	case NXP_NCI_FW_RESULT_UNKNOWN_CMD:
    250		return -EINVAL;
    251	case NXP_NCI_FW_RESULT_ABORTED_CMD:
    252		return -EMSGSIZE;
    253	case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR:
    254		return -EADDRNOTAVAIL;
    255	case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR:
    256		return -ENOBUFS;
    257	case NXP_NCI_FW_RESULT_MEM_BSY:
    258		return -ENOKEY;
    259	case NXP_NCI_FW_RESULT_SIGNATURE_ERROR:
    260		return -EKEYREJECTED;
    261	case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR:
    262		return -EALREADY;
    263	case NXP_NCI_FW_RESULT_PROTOCOL_ERROR:
    264		return -EPROTO;
    265	case NXP_NCI_FW_RESULT_SFWU_DEGRADED:
    266		return -EHWPOISON;
    267	case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK:
    268		return 0;
    269	case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK:
    270		return 0;
    271	case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5:
    272		return -EINVAL;
    273	default:
    274		return -EIO;
    275	}
    276}
    277
    278static u16 nxp_nci_fw_check_crc(struct sk_buff *skb)
    279{
    280	u16 crc, frame_crc;
    281	size_t len = skb->len - NXP_NCI_FW_CRC_LEN;
    282
    283	crc = nxp_nci_fw_crc(skb->data, len);
    284	frame_crc = get_unaligned_be16(skb->data + len);
    285
    286	return (crc ^ frame_crc);
    287}
    288
    289void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
    290{
    291	struct nxp_nci_info *info = nci_get_drvdata(ndev);
    292	struct nxp_nci_fw_info *fw_info = &info->fw_info;
    293
    294	complete(&fw_info->cmd_completion);
    295
    296	if (skb) {
    297		if (nxp_nci_fw_check_crc(skb) != 0x00)
    298			fw_info->cmd_result = -EBADMSG;
    299		else
    300			fw_info->cmd_result = nxp_nci_fw_read_status(*(u8 *)skb_pull(skb, NXP_NCI_FW_HDR_LEN));
    301		kfree_skb(skb);
    302	} else {
    303		fw_info->cmd_result = -EIO;
    304	}
    305
    306	if (fw_info->fw)
    307		schedule_work(&fw_info->work);
    308}
    309EXPORT_SYMBOL(nxp_nci_fw_recv_frame);