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 (10988B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * NCI based driver for Samsung S3FWRN5 NFC chip
      4 *
      5 * Copyright (C) 2015 Samsung Electrnoics
      6 * Robert Baldyga <r.baldyga@samsung.com>
      7 */
      8
      9#include <linux/completion.h>
     10#include <linux/firmware.h>
     11#include <crypto/hash.h>
     12#include <crypto/sha1.h>
     13
     14#include "s3fwrn5.h"
     15#include "firmware.h"
     16
     17struct s3fwrn5_fw_version {
     18	__u8 major;
     19	__u8 build1;
     20	__u8 build2;
     21	__u8 target;
     22};
     23
     24static int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info,
     25	struct sk_buff *msg, struct sk_buff **rsp)
     26{
     27	struct s3fwrn5_info *info =
     28		container_of(fw_info, struct s3fwrn5_info, fw_info);
     29	long ret;
     30
     31	reinit_completion(&fw_info->completion);
     32
     33	ret = s3fwrn5_write(info, msg);
     34	if (ret < 0)
     35		return ret;
     36
     37	ret = wait_for_completion_interruptible_timeout(
     38		&fw_info->completion, msecs_to_jiffies(1000));
     39	if (ret < 0)
     40		return ret;
     41	else if (ret == 0)
     42		return -ENXIO;
     43
     44	if (!fw_info->rsp)
     45		return -EINVAL;
     46
     47	*rsp = fw_info->rsp;
     48	fw_info->rsp = NULL;
     49
     50	return 0;
     51}
     52
     53static int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info,
     54	struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len)
     55{
     56	struct s3fwrn5_fw_header hdr;
     57	struct sk_buff *skb;
     58
     59	hdr.type = type | fw_info->parity;
     60	fw_info->parity ^= 0x80;
     61	hdr.code = code;
     62	hdr.len = len;
     63
     64	skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL);
     65	if (!skb)
     66		return -ENOMEM;
     67
     68	skb_put_data(skb, &hdr, S3FWRN5_FW_HDR_SIZE);
     69	if (len)
     70		skb_put_data(skb, data, len);
     71
     72	*msg = skb;
     73
     74	return 0;
     75}
     76
     77static int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info,
     78	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
     79{
     80	struct sk_buff *msg, *rsp = NULL;
     81	struct s3fwrn5_fw_header *hdr;
     82	int ret;
     83
     84	/* Send GET_BOOTINFO command */
     85
     86	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
     87		S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0);
     88	if (ret < 0)
     89		return ret;
     90
     91	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
     92	kfree_skb(msg);
     93	if (ret < 0)
     94		return ret;
     95
     96	hdr = (struct s3fwrn5_fw_header *) rsp->data;
     97	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
     98		ret = -EINVAL;
     99		goto out;
    100	}
    101
    102	memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10);
    103
    104out:
    105	kfree_skb(rsp);
    106	return ret;
    107}
    108
    109static int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info,
    110	const void *hash_data, u16 hash_size,
    111	const void *sig_data, u16 sig_size)
    112{
    113	struct s3fwrn5_fw_cmd_enter_updatemode args;
    114	struct sk_buff *msg, *rsp = NULL;
    115	struct s3fwrn5_fw_header *hdr;
    116	int ret;
    117
    118	/* Send ENTER_UPDATE_MODE command */
    119
    120	args.hashcode_size = hash_size;
    121	args.signature_size = sig_size;
    122
    123	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
    124		S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args));
    125	if (ret < 0)
    126		return ret;
    127
    128	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    129	kfree_skb(msg);
    130	if (ret < 0)
    131		return ret;
    132
    133	hdr = (struct s3fwrn5_fw_header *) rsp->data;
    134	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
    135		ret = -EPROTO;
    136		goto out;
    137	}
    138
    139	kfree_skb(rsp);
    140
    141	/* Send hashcode data */
    142
    143	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
    144		hash_data, hash_size);
    145	if (ret < 0)
    146		return ret;
    147
    148	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    149	kfree_skb(msg);
    150	if (ret < 0)
    151		return ret;
    152
    153	hdr = (struct s3fwrn5_fw_header *) rsp->data;
    154	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
    155		ret = -EPROTO;
    156		goto out;
    157	}
    158
    159	kfree_skb(rsp);
    160
    161	/* Send signature data */
    162
    163	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
    164		sig_data, sig_size);
    165	if (ret < 0)
    166		return ret;
    167
    168	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    169	kfree_skb(msg);
    170	if (ret < 0)
    171		return ret;
    172
    173	hdr = (struct s3fwrn5_fw_header *) rsp->data;
    174	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
    175		ret = -EPROTO;
    176
    177out:
    178	kfree_skb(rsp);
    179	return ret;
    180}
    181
    182static int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info,
    183	u32 base_addr, const void *data)
    184{
    185	struct s3fwrn5_fw_cmd_update_sector args;
    186	struct sk_buff *msg, *rsp = NULL;
    187	struct s3fwrn5_fw_header *hdr;
    188	int ret, i;
    189
    190	/* Send UPDATE_SECTOR command */
    191
    192	args.base_address = base_addr;
    193
    194	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
    195		S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args));
    196	if (ret < 0)
    197		return ret;
    198
    199	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    200	kfree_skb(msg);
    201	if (ret < 0)
    202		return ret;
    203
    204	hdr = (struct s3fwrn5_fw_header *) rsp->data;
    205	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
    206		ret = -EPROTO;
    207		goto err;
    208	}
    209
    210	kfree_skb(rsp);
    211
    212	/* Send data split into 256-byte packets */
    213
    214	for (i = 0; i < 16; ++i) {
    215		ret = s3fwrn5_fw_prep_msg(fw_info, &msg,
    216			S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256);
    217		if (ret < 0)
    218			break;
    219
    220		ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    221		kfree_skb(msg);
    222		if (ret < 0)
    223			break;
    224
    225		hdr = (struct s3fwrn5_fw_header *) rsp->data;
    226		if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
    227			ret = -EPROTO;
    228			goto err;
    229		}
    230
    231		kfree_skb(rsp);
    232	}
    233
    234	return ret;
    235
    236err:
    237	kfree_skb(rsp);
    238	return ret;
    239}
    240
    241static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info)
    242{
    243	struct sk_buff *msg, *rsp = NULL;
    244	struct s3fwrn5_fw_header *hdr;
    245	int ret;
    246
    247	/* Send COMPLETE_UPDATE_MODE command */
    248
    249	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
    250		S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0);
    251	if (ret < 0)
    252		return ret;
    253
    254	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
    255	kfree_skb(msg);
    256	if (ret < 0)
    257		return ret;
    258
    259	hdr = (struct s3fwrn5_fw_header *) rsp->data;
    260	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
    261		ret = -EPROTO;
    262
    263	kfree_skb(rsp);
    264
    265	return ret;
    266}
    267
    268/*
    269 * Firmware header structure:
    270 *
    271 * 0x00 - 0x0B : Date and time string (w/o NUL termination)
    272 * 0x10 - 0x13 : Firmware version
    273 * 0x14 - 0x17 : Signature address
    274 * 0x18 - 0x1B : Signature size
    275 * 0x1C - 0x1F : Firmware image address
    276 * 0x20 - 0x23 : Firmware sectors count
    277 * 0x24 - 0x27 : Custom signature address
    278 * 0x28 - 0x2B : Custom signature size
    279 */
    280
    281#define S3FWRN5_FW_IMAGE_HEADER_SIZE 44
    282
    283int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info)
    284{
    285	struct s3fwrn5_fw_image *fw = &fw_info->fw;
    286	u32 sig_off;
    287	u32 image_off;
    288	u32 custom_sig_off;
    289	int ret;
    290
    291	ret = request_firmware(&fw->fw, fw_info->fw_name,
    292		&fw_info->ndev->nfc_dev->dev);
    293	if (ret < 0)
    294		return ret;
    295
    296	if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) {
    297		release_firmware(fw->fw);
    298		return -EINVAL;
    299	}
    300
    301	memcpy(fw->date, fw->fw->data + 0x00, 12);
    302	fw->date[12] = '\0';
    303
    304	memcpy(&fw->version, fw->fw->data + 0x10, 4);
    305
    306	memcpy(&sig_off, fw->fw->data + 0x14, 4);
    307	fw->sig = fw->fw->data + sig_off;
    308	memcpy(&fw->sig_size, fw->fw->data + 0x18, 4);
    309
    310	memcpy(&image_off, fw->fw->data + 0x1C, 4);
    311	fw->image = fw->fw->data + image_off;
    312	memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4);
    313
    314	memcpy(&custom_sig_off, fw->fw->data + 0x24, 4);
    315	fw->custom_sig = fw->fw->data + custom_sig_off;
    316	memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4);
    317
    318	return 0;
    319}
    320
    321static void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info)
    322{
    323	release_firmware(fw_info->fw.fw);
    324}
    325
    326static int s3fwrn5_fw_get_base_addr(
    327	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr)
    328{
    329	int i;
    330	static const struct {
    331		u8 version[4];
    332		u32 base_addr;
    333	} match[] = {
    334		{{0x05, 0x00, 0x00, 0x00}, 0x00005000},
    335		{{0x05, 0x00, 0x00, 0x01}, 0x00003000},
    336		{{0x05, 0x00, 0x00, 0x02}, 0x00003000},
    337		{{0x05, 0x00, 0x00, 0x03}, 0x00003000},
    338		{{0x05, 0x00, 0x00, 0x05}, 0x00003000}
    339	};
    340
    341	for (i = 0; i < ARRAY_SIZE(match); ++i)
    342		if (bootinfo->hw_version[0] == match[i].version[0] &&
    343			bootinfo->hw_version[1] == match[i].version[1] &&
    344			bootinfo->hw_version[3] == match[i].version[3]) {
    345			*base_addr = match[i].base_addr;
    346			return 0;
    347		}
    348
    349	return -EINVAL;
    350}
    351
    352static inline bool
    353s3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
    354{
    355	return !!bootinfo->hw_version[2];
    356}
    357
    358int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info)
    359{
    360	struct device *dev = &fw_info->ndev->nfc_dev->dev;
    361	struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo;
    362	int ret;
    363
    364	/* Get bootloader info */
    365
    366	ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo);
    367	if (ret < 0) {
    368		dev_err(dev, "Failed to get bootinfo, ret=%02x\n", ret);
    369		goto err;
    370	}
    371
    372	/* Match hardware version to obtain firmware base address */
    373
    374	ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr);
    375	if (ret < 0) {
    376		dev_err(dev, "Unknown hardware version\n");
    377		goto err;
    378	}
    379
    380	fw_info->sector_size = bootinfo.sector_size;
    381
    382	fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ?
    383		fw_info->fw.custom_sig_size : fw_info->fw.sig_size;
    384	fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ?
    385		fw_info->fw.custom_sig : fw_info->fw.sig;
    386
    387	return 0;
    388
    389err:
    390	s3fwrn5_fw_release_firmware(fw_info);
    391	return ret;
    392}
    393
    394bool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version)
    395{
    396	struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version;
    397	struct s3fwrn5_fw_version *old = (void *) &version;
    398
    399	if (new->major > old->major)
    400		return true;
    401	if (new->build1 > old->build1)
    402		return true;
    403	if (new->build2 > old->build2)
    404		return true;
    405
    406	return false;
    407}
    408
    409int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
    410{
    411	struct device *dev = &fw_info->ndev->nfc_dev->dev;
    412	struct s3fwrn5_fw_image *fw = &fw_info->fw;
    413	u8 hash_data[SHA1_DIGEST_SIZE];
    414	struct crypto_shash *tfm;
    415	u32 image_size, off;
    416	int ret;
    417
    418	image_size = fw_info->sector_size * fw->image_sectors;
    419
    420	/* Compute SHA of firmware data */
    421
    422	tfm = crypto_alloc_shash("sha1", 0, 0);
    423	if (IS_ERR(tfm)) {
    424		dev_err(dev, "Cannot allocate shash (code=%pe)\n", tfm);
    425		return PTR_ERR(tfm);
    426	}
    427
    428	ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data);
    429
    430	crypto_free_shash(tfm);
    431	if (ret) {
    432		dev_err(dev, "Cannot compute hash (code=%d)\n", ret);
    433		return ret;
    434	}
    435
    436	/* Firmware update process */
    437
    438	dev_info(dev, "Firmware update: %s\n", fw_info->fw_name);
    439
    440	ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data,
    441		SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size);
    442	if (ret < 0) {
    443		dev_err(dev, "Unable to enter update mode\n");
    444		return ret;
    445	}
    446
    447	for (off = 0; off < image_size; off += fw_info->sector_size) {
    448		ret = s3fwrn5_fw_update_sector(fw_info,
    449			fw_info->base_addr + off, fw->image + off);
    450		if (ret < 0) {
    451			dev_err(dev, "Firmware update error (code=%d)\n", ret);
    452			return ret;
    453		}
    454	}
    455
    456	ret = s3fwrn5_fw_complete_update_mode(fw_info);
    457	if (ret < 0) {
    458		dev_err(dev, "Unable to complete update mode\n");
    459		return ret;
    460	}
    461
    462	dev_info(dev, "Firmware update: success\n");
    463
    464	return ret;
    465}
    466
    467void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name)
    468{
    469	fw_info->parity = 0x00;
    470	fw_info->rsp = NULL;
    471	fw_info->fw.fw = NULL;
    472	strcpy(fw_info->fw_name, fw_name);
    473	init_completion(&fw_info->completion);
    474}
    475
    476void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info)
    477{
    478	s3fwrn5_fw_release_firmware(fw_info);
    479}
    480
    481int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
    482{
    483	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
    484	struct s3fwrn5_fw_info *fw_info = &info->fw_info;
    485
    486	if (WARN_ON(fw_info->rsp)) {
    487		kfree_skb(skb);
    488		return -EINVAL;
    489	}
    490
    491	fw_info->rsp = skb;
    492
    493	complete(&fw_info->completion);
    494
    495	return 0;
    496}