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

fw_dnld.c (14173B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Marvell NFC driver: Firmware downloader
      4 *
      5 * Copyright (C) 2015, Marvell International Ltd.
      6 */
      7
      8#include <linux/module.h>
      9#include <asm/unaligned.h>
     10#include <linux/firmware.h>
     11#include <linux/nfc.h>
     12#include <net/nfc/nci.h>
     13#include <net/nfc/nci_core.h>
     14#include "nfcmrvl.h"
     15
     16#define FW_DNLD_TIMEOUT			15000
     17
     18#define NCI_OP_PROPRIETARY_BOOT_CMD	nci_opcode_pack(NCI_GID_PROPRIETARY, \
     19							NCI_OP_PROP_BOOT_CMD)
     20
     21/* FW download states */
     22
     23enum {
     24	STATE_RESET = 0,
     25	STATE_INIT,
     26	STATE_SET_REF_CLOCK,
     27	STATE_SET_HI_CONFIG,
     28	STATE_OPEN_LC,
     29	STATE_FW_DNLD,
     30	STATE_CLOSE_LC,
     31	STATE_BOOT
     32};
     33
     34enum {
     35	SUBSTATE_WAIT_COMMAND = 0,
     36	SUBSTATE_WAIT_ACK_CREDIT,
     37	SUBSTATE_WAIT_NACK_CREDIT,
     38	SUBSTATE_WAIT_DATA_CREDIT,
     39};
     40
     41/*
     42 * Patterns for responses
     43 */
     44
     45static const uint8_t nci_pattern_core_reset_ntf[] = {
     46	0x60, 0x00, 0x02, 0xA0, 0x01
     47};
     48
     49static const uint8_t nci_pattern_core_init_rsp[] = {
     50	0x40, 0x01, 0x11
     51};
     52
     53static const uint8_t nci_pattern_core_set_config_rsp[] = {
     54	0x40, 0x02, 0x02, 0x00, 0x00
     55};
     56
     57static const uint8_t nci_pattern_core_conn_create_rsp[] = {
     58	0x40, 0x04, 0x04, 0x00
     59};
     60
     61static const uint8_t nci_pattern_core_conn_close_rsp[] = {
     62	0x40, 0x05, 0x01, 0x00
     63};
     64
     65static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
     66	0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
     67};
     68
     69static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
     70	0x4F, 0x3A, 0x01, 0x00
     71};
     72
     73static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
     74{
     75	struct sk_buff *skb;
     76	struct nci_data_hdr *hdr;
     77
     78	skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
     79	if (!skb)
     80		return NULL;
     81
     82	hdr = skb_put(skb, NCI_DATA_HDR_SIZE);
     83	hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
     84	hdr->rfu = 0;
     85	hdr->plen = plen;
     86
     87	nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
     88	nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
     89
     90	return skb;
     91}
     92
     93static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
     94{
     95	if (priv->fw_dnld.fw) {
     96		release_firmware(priv->fw_dnld.fw);
     97		priv->fw_dnld.fw = NULL;
     98		priv->fw_dnld.header = NULL;
     99		priv->fw_dnld.binary_config = NULL;
    100	}
    101
    102	atomic_set(&priv->ndev->cmd_cnt, 0);
    103
    104	if (timer_pending(&priv->ndev->cmd_timer))
    105		del_timer_sync(&priv->ndev->cmd_timer);
    106
    107	if (timer_pending(&priv->fw_dnld.timer))
    108		del_timer_sync(&priv->fw_dnld.timer);
    109
    110	nfc_info(priv->dev, "FW loading over (%d)]\n", error);
    111
    112	if (error != 0) {
    113		/* failed, halt the chip to avoid power consumption */
    114		nfcmrvl_chip_halt(priv);
    115	}
    116
    117	nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error);
    118}
    119
    120static void fw_dnld_timeout(struct timer_list *t)
    121{
    122	struct nfcmrvl_private *priv = from_timer(priv, t, fw_dnld.timer);
    123
    124	nfc_err(priv->dev, "FW loading timeout");
    125	priv->fw_dnld.state = STATE_RESET;
    126	fw_dnld_over(priv, -ETIMEDOUT);
    127}
    128
    129static int process_state_reset(struct nfcmrvl_private *priv,
    130			       const struct sk_buff *skb)
    131{
    132	if (sizeof(nci_pattern_core_reset_ntf) != skb->len ||
    133	    memcmp(skb->data, nci_pattern_core_reset_ntf,
    134		   sizeof(nci_pattern_core_reset_ntf)))
    135		return -EINVAL;
    136
    137	nfc_info(priv->dev, "BootROM reset, start fw download\n");
    138
    139	/* Start FW download state machine */
    140	priv->fw_dnld.state = STATE_INIT;
    141	nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
    142
    143	return 0;
    144}
    145
    146static int process_state_init(struct nfcmrvl_private *priv,
    147			      const struct sk_buff *skb)
    148{
    149	struct nci_core_set_config_cmd cmd;
    150
    151	if (sizeof(nci_pattern_core_init_rsp) >= skb->len ||
    152	    memcmp(skb->data, nci_pattern_core_init_rsp,
    153		   sizeof(nci_pattern_core_init_rsp)))
    154		return -EINVAL;
    155
    156	cmd.num_params = 1;
    157	cmd.param.id = NFCMRVL_PROP_REF_CLOCK;
    158	cmd.param.len = 4;
    159	memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4);
    160
    161	nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
    162		     &cmd);
    163
    164	priv->fw_dnld.state = STATE_SET_REF_CLOCK;
    165	return 0;
    166}
    167
    168static void create_lc(struct nfcmrvl_private *priv)
    169{
    170	uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 };
    171
    172	priv->fw_dnld.state = STATE_OPEN_LC;
    173	nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param);
    174}
    175
    176static int process_state_set_ref_clock(struct nfcmrvl_private *priv,
    177				       const struct sk_buff *skb)
    178{
    179	struct nci_core_set_config_cmd cmd;
    180
    181	if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
    182	    memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
    183		return -EINVAL;
    184
    185	cmd.num_params = 1;
    186	cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG;
    187
    188	switch (priv->phy) {
    189	case NFCMRVL_PHY_UART:
    190		cmd.param.len = 5;
    191		memcpy(cmd.param.val,
    192		       &priv->fw_dnld.binary_config->uart.baudrate,
    193		       4);
    194		cmd.param.val[4] =
    195			priv->fw_dnld.binary_config->uart.flow_control;
    196		break;
    197	case NFCMRVL_PHY_I2C:
    198		cmd.param.len = 5;
    199		memcpy(cmd.param.val,
    200		       &priv->fw_dnld.binary_config->i2c.clk,
    201		       4);
    202		cmd.param.val[4] = 0;
    203		break;
    204	case NFCMRVL_PHY_SPI:
    205		cmd.param.len = 5;
    206		memcpy(cmd.param.val,
    207		       &priv->fw_dnld.binary_config->spi.clk,
    208		       4);
    209		cmd.param.val[4] = 0;
    210		break;
    211	default:
    212		create_lc(priv);
    213		return 0;
    214	}
    215
    216	priv->fw_dnld.state = STATE_SET_HI_CONFIG;
    217	nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
    218		     &cmd);
    219	return 0;
    220}
    221
    222static int process_state_set_hi_config(struct nfcmrvl_private *priv,
    223				       const struct sk_buff *skb)
    224{
    225	if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
    226	    memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
    227		return -EINVAL;
    228
    229	create_lc(priv);
    230	return 0;
    231}
    232
    233static int process_state_open_lc(struct nfcmrvl_private *priv,
    234				 const struct sk_buff *skb)
    235{
    236	if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len ||
    237	    memcmp(skb->data, nci_pattern_core_conn_create_rsp,
    238		   sizeof(nci_pattern_core_conn_create_rsp)))
    239		return -EINVAL;
    240
    241	priv->fw_dnld.state = STATE_FW_DNLD;
    242	priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
    243	priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset;
    244	return 0;
    245}
    246
    247static int process_state_fw_dnld(struct nfcmrvl_private *priv,
    248				 struct sk_buff *skb)
    249{
    250	uint16_t len;
    251	uint16_t comp_len;
    252	struct sk_buff *out_skb;
    253
    254	switch (priv->fw_dnld.substate) {
    255	case SUBSTATE_WAIT_COMMAND:
    256		/*
    257		 * Command format:
    258		 * B0..2: NCI header
    259		 * B3   : Helper command (0xA5)
    260		 * B4..5: le16 data size
    261		 * B6..7: le16 data size complement (~)
    262		 * B8..N: payload
    263		 */
    264
    265		/* Remove NCI HDR */
    266		skb_pull(skb, 3);
    267		if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
    268			nfc_err(priv->dev, "bad command");
    269			return -EINVAL;
    270		}
    271		skb_pull(skb, 1);
    272		len = get_unaligned_le16(skb->data);
    273		skb_pull(skb, 2);
    274		comp_len = get_unaligned_le16(skb->data);
    275		memcpy(&comp_len, skb->data, 2);
    276		skb_pull(skb, 2);
    277		if (((~len) & 0xFFFF) != comp_len) {
    278			nfc_err(priv->dev, "bad len complement: %x %x %x",
    279				len, comp_len, (~len & 0xFFFF));
    280			out_skb = alloc_lc_skb(priv, 1);
    281			if (!out_skb)
    282				return -ENOMEM;
    283			skb_put_u8(out_skb, 0xBF);
    284			nci_send_frame(priv->ndev, out_skb);
    285			priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
    286			return 0;
    287		}
    288		priv->fw_dnld.chunk_len = len;
    289		out_skb = alloc_lc_skb(priv, 1);
    290		if (!out_skb)
    291			return -ENOMEM;
    292		skb_put_u8(out_skb, HELPER_ACK_PACKET_FORMAT);
    293		nci_send_frame(priv->ndev, out_skb);
    294		priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
    295		break;
    296
    297	case SUBSTATE_WAIT_ACK_CREDIT:
    298		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
    299		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
    300			   skb->len)) {
    301			nfc_err(priv->dev, "bad packet: waiting for credit");
    302			return -EINVAL;
    303		}
    304		if (priv->fw_dnld.chunk_len == 0) {
    305			/* FW Loading is done */
    306			uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
    307
    308			priv->fw_dnld.state = STATE_CLOSE_LC;
    309			nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
    310				     1, &conn_id);
    311		} else {
    312			out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
    313			if (!out_skb)
    314				return -ENOMEM;
    315			skb_put_data(out_skb,
    316				     ((uint8_t *)priv->fw_dnld.fw->data) + priv->fw_dnld.offset,
    317				     priv->fw_dnld.chunk_len);
    318			nci_send_frame(priv->ndev, out_skb);
    319			priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
    320		}
    321		break;
    322
    323	case SUBSTATE_WAIT_DATA_CREDIT:
    324		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
    325		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
    326			    skb->len)) {
    327			nfc_err(priv->dev, "bad packet: waiting for credit");
    328			return -EINVAL;
    329		}
    330		priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
    331		priv->fw_dnld.chunk_len = 0;
    332		priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
    333		break;
    334
    335	case SUBSTATE_WAIT_NACK_CREDIT:
    336		if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
    337		    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
    338			    skb->len)) {
    339			nfc_err(priv->dev, "bad packet: waiting for credit");
    340			return -EINVAL;
    341		}
    342		priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
    343		break;
    344	}
    345	return 0;
    346}
    347
    348static int process_state_close_lc(struct nfcmrvl_private *priv,
    349				  const struct sk_buff *skb)
    350{
    351	if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
    352	    memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
    353		return -EINVAL;
    354
    355	priv->fw_dnld.state = STATE_BOOT;
    356	nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
    357	return 0;
    358}
    359
    360static int process_state_boot(struct nfcmrvl_private *priv,
    361			      const struct sk_buff *skb)
    362{
    363	if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
    364	    memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
    365		return -EINVAL;
    366
    367	/*
    368	 * Update HI config to use the right configuration for the next
    369	 * data exchanges.
    370	 */
    371	priv->if_ops->nci_update_config(priv,
    372					&priv->fw_dnld.binary_config->config);
    373
    374	if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
    375		/*
    376		 * This is the case where an helper was needed and we have
    377		 * uploaded it. Now we have to wait the next RESET NTF to start
    378		 * FW download.
    379		 */
    380		priv->fw_dnld.state = STATE_RESET;
    381		priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
    382		nfc_info(priv->dev, "FW loading: helper loaded");
    383	} else {
    384		nfc_info(priv->dev, "FW loading: firmware loaded");
    385		fw_dnld_over(priv, 0);
    386	}
    387	return 0;
    388}
    389
    390static void fw_dnld_rx_work(struct work_struct *work)
    391{
    392	int ret;
    393	struct sk_buff *skb;
    394	struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
    395						       struct nfcmrvl_fw_dnld,
    396						       rx_work);
    397	struct nfcmrvl_private *priv = container_of(fw_dnld,
    398						    struct nfcmrvl_private,
    399						    fw_dnld);
    400
    401	while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
    402		nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
    403				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
    404		switch (fw_dnld->state) {
    405		case STATE_RESET:
    406			ret = process_state_reset(priv, skb);
    407			break;
    408		case STATE_INIT:
    409			ret = process_state_init(priv, skb);
    410			break;
    411		case STATE_SET_REF_CLOCK:
    412			ret = process_state_set_ref_clock(priv, skb);
    413			break;
    414		case STATE_SET_HI_CONFIG:
    415			ret = process_state_set_hi_config(priv, skb);
    416			break;
    417		case STATE_OPEN_LC:
    418			ret = process_state_open_lc(priv, skb);
    419			break;
    420		case STATE_FW_DNLD:
    421			ret = process_state_fw_dnld(priv, skb);
    422			break;
    423		case STATE_CLOSE_LC:
    424			ret = process_state_close_lc(priv, skb);
    425			break;
    426		case STATE_BOOT:
    427			ret = process_state_boot(priv, skb);
    428			break;
    429		default:
    430			ret = -EFAULT;
    431		}
    432
    433		kfree_skb(skb);
    434
    435		if (ret != 0) {
    436			nfc_err(priv->dev, "FW loading error");
    437			fw_dnld_over(priv, ret);
    438			break;
    439		}
    440	}
    441}
    442
    443int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
    444{
    445	char name[32];
    446
    447	INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
    448	snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
    449		 dev_name(&priv->ndev->nfc_dev->dev));
    450	priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
    451	if (!priv->fw_dnld.rx_wq)
    452		return -ENOMEM;
    453	skb_queue_head_init(&priv->fw_dnld.rx_q);
    454	return 0;
    455}
    456
    457void nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
    458{
    459	destroy_workqueue(priv->fw_dnld.rx_wq);
    460}
    461
    462void nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
    463				struct sk_buff *skb)
    464{
    465	/* Discard command timer */
    466	if (timer_pending(&priv->ndev->cmd_timer))
    467		del_timer_sync(&priv->ndev->cmd_timer);
    468
    469	/* Allow next command */
    470	atomic_set(&priv->ndev->cmd_cnt, 1);
    471
    472	/* Queue and trigger rx work */
    473	skb_queue_tail(&priv->fw_dnld.rx_q, skb);
    474	queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
    475}
    476
    477void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
    478{
    479	fw_dnld_over(priv, -EHOSTDOWN);
    480}
    481
    482int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
    483{
    484	struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
    485	struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
    486	int res;
    487
    488	if (!priv->support_fw_dnld)
    489		return -ENOTSUPP;
    490
    491	if (!firmware_name || !firmware_name[0])
    492		return -EINVAL;
    493
    494	strcpy(fw_dnld->name, firmware_name);
    495
    496	/*
    497	 * Retrieve FW binary file and parse it to initialize FW download
    498	 * state machine.
    499	 */
    500
    501	/* Retrieve FW binary */
    502	res = request_firmware(&fw_dnld->fw, firmware_name,
    503			       &ndev->nfc_dev->dev);
    504	if (res < 0) {
    505		nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
    506		return -ENOENT;
    507	}
    508
    509	fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
    510
    511	if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
    512	    fw_dnld->header->phy != priv->phy) {
    513		nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
    514			firmware_name, fw_dnld->header->magic,
    515			fw_dnld->header->phy);
    516		release_firmware(fw_dnld->fw);
    517		fw_dnld->header = NULL;
    518		return -EINVAL;
    519	}
    520
    521	if (fw_dnld->header->helper.offset != 0) {
    522		nfc_info(priv->dev, "loading helper");
    523		fw_dnld->binary_config = &fw_dnld->header->helper;
    524	} else {
    525		nfc_info(priv->dev, "loading firmware");
    526		fw_dnld->binary_config = &fw_dnld->header->firmware;
    527	}
    528
    529	/* Configure a timer for timeout */
    530	timer_setup(&priv->fw_dnld.timer, fw_dnld_timeout, 0);
    531	mod_timer(&priv->fw_dnld.timer,
    532		  jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT));
    533
    534	/* Ronfigure HI to be sure that it is the bootrom values */
    535	priv->if_ops->nci_update_config(priv,
    536					&fw_dnld->header->bootrom.config);
    537
    538	/* Allow first command */
    539	atomic_set(&priv->ndev->cmd_cnt, 1);
    540
    541	/* First, reset the chip */
    542	priv->fw_dnld.state = STATE_RESET;
    543	nfcmrvl_chip_reset(priv);
    544
    545	/* Now wait for CORE_RESET_NTF or timeout */
    546
    547	return 0;
    548}