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

mei_phy.c (8504B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2013, Intel Corporation.
      4 *
      5 * MEI Library for mei bus nfc device access
      6 */
      7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      8
      9#include <linux/module.h>
     10#include <linux/slab.h>
     11#include <linux/nfc.h>
     12
     13#include "mei_phy.h"
     14
     15struct mei_nfc_hdr {
     16	u8 cmd;
     17	u8 status;
     18	u16 req_id;
     19	u32 reserved;
     20	u16 data_size;
     21} __packed;
     22
     23struct mei_nfc_cmd {
     24	struct mei_nfc_hdr hdr;
     25	u8 sub_command;
     26	u8 data[];
     27} __packed;
     28
     29struct mei_nfc_reply {
     30	struct mei_nfc_hdr hdr;
     31	u8 sub_command;
     32	u8 reply_status;
     33	u8 data[];
     34} __packed;
     35
     36struct mei_nfc_if_version {
     37	u8 radio_version_sw[3];
     38	u8 reserved[3];
     39	u8 radio_version_hw[3];
     40	u8 i2c_addr;
     41	u8 fw_ivn;
     42	u8 vendor_id;
     43	u8 radio_type;
     44} __packed;
     45
     46struct mei_nfc_connect {
     47	u8 fw_ivn;
     48	u8 vendor_id;
     49} __packed;
     50
     51struct mei_nfc_connect_resp {
     52	u8 fw_ivn;
     53	u8 vendor_id;
     54	u16 me_major;
     55	u16 me_minor;
     56	u16 me_hotfix;
     57	u16 me_build;
     58} __packed;
     59
     60
     61#define MEI_NFC_CMD_MAINTENANCE 0x00
     62#define MEI_NFC_CMD_HCI_SEND 0x01
     63#define MEI_NFC_CMD_HCI_RECV 0x02
     64
     65#define MEI_NFC_SUBCMD_CONNECT    0x00
     66#define MEI_NFC_SUBCMD_IF_VERSION 0x01
     67
     68#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
     69
     70#define MEI_DUMP_SKB_IN(info, skb)				\
     71do {								\
     72	pr_debug("%s:\n", info);				\
     73	print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET,	\
     74			16, 1, (skb)->data, (skb)->len, false);	\
     75} while (0)
     76
     77#define MEI_DUMP_SKB_OUT(info, skb)				\
     78do {								\
     79	pr_debug("%s:\n", info);				\
     80	print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET,	\
     81			16, 1, (skb)->data, (skb)->len, false);	\
     82} while (0)
     83
     84#define MEI_DUMP_NFC_HDR(info, _hdr)                                \
     85do {                                                                \
     86	pr_debug("%s:\n", info);                                    \
     87	pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n",  \
     88		 (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id,       \
     89		 (_hdr)->reserved, (_hdr)->data_size);              \
     90} while (0)
     91
     92static int mei_nfc_if_version(struct nfc_mei_phy *phy)
     93{
     94
     95	struct mei_nfc_cmd cmd;
     96	struct mei_nfc_reply *reply = NULL;
     97	struct mei_nfc_if_version *version;
     98	size_t if_version_length;
     99	int bytes_recv, r;
    100
    101	memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
    102	cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
    103	cmd.hdr.data_size = 1;
    104	cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
    105
    106	MEI_DUMP_NFC_HDR("version", &cmd.hdr);
    107	r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
    108	if (r < 0) {
    109		pr_err("Could not send IF version cmd\n");
    110		return r;
    111	}
    112
    113	/* to be sure on the stack we alloc memory */
    114	if_version_length = sizeof(struct mei_nfc_reply) +
    115		sizeof(struct mei_nfc_if_version);
    116
    117	reply = kzalloc(if_version_length, GFP_KERNEL);
    118	if (!reply)
    119		return -ENOMEM;
    120
    121	bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
    122	if (bytes_recv < 0 || bytes_recv < if_version_length) {
    123		pr_err("Could not read IF version\n");
    124		r = -EIO;
    125		goto err;
    126	}
    127
    128	version = (struct mei_nfc_if_version *)reply->data;
    129
    130	phy->fw_ivn = version->fw_ivn;
    131	phy->vendor_id = version->vendor_id;
    132	phy->radio_type = version->radio_type;
    133
    134err:
    135	kfree(reply);
    136	return r;
    137}
    138
    139static int mei_nfc_connect(struct nfc_mei_phy *phy)
    140{
    141	struct mei_nfc_cmd *cmd, *reply;
    142	struct mei_nfc_connect *connect;
    143	struct mei_nfc_connect_resp *connect_resp;
    144	size_t connect_length, connect_resp_length;
    145	int bytes_recv, r;
    146
    147	connect_length = sizeof(struct mei_nfc_cmd) +
    148			sizeof(struct mei_nfc_connect);
    149
    150	connect_resp_length = sizeof(struct mei_nfc_cmd) +
    151			sizeof(struct mei_nfc_connect_resp);
    152
    153	cmd = kzalloc(connect_length, GFP_KERNEL);
    154	if (!cmd)
    155		return -ENOMEM;
    156	connect = (struct mei_nfc_connect *)cmd->data;
    157
    158	reply = kzalloc(connect_resp_length, GFP_KERNEL);
    159	if (!reply) {
    160		kfree(cmd);
    161		return -ENOMEM;
    162	}
    163
    164	connect_resp = (struct mei_nfc_connect_resp *)reply->data;
    165
    166	cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE;
    167	cmd->hdr.data_size = 3;
    168	cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
    169	connect->fw_ivn = phy->fw_ivn;
    170	connect->vendor_id = phy->vendor_id;
    171
    172	MEI_DUMP_NFC_HDR("connect request", &cmd->hdr);
    173	r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length);
    174	if (r < 0) {
    175		pr_err("Could not send connect cmd %d\n", r);
    176		goto err;
    177	}
    178
    179	bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply,
    180				    connect_resp_length);
    181	if (bytes_recv < 0) {
    182		r = bytes_recv;
    183		pr_err("Could not read connect response %d\n", r);
    184		goto err;
    185	}
    186
    187	MEI_DUMP_NFC_HDR("connect reply", &reply->hdr);
    188
    189	pr_info("IVN 0x%x Vendor ID 0x%x\n",
    190		 connect_resp->fw_ivn, connect_resp->vendor_id);
    191
    192	pr_info("ME FW %d.%d.%d.%d\n",
    193		connect_resp->me_major, connect_resp->me_minor,
    194		connect_resp->me_hotfix, connect_resp->me_build);
    195
    196	r = 0;
    197
    198err:
    199	kfree(reply);
    200	kfree(cmd);
    201
    202	return r;
    203}
    204
    205static int mei_nfc_send(struct nfc_mei_phy *phy, const u8 *buf, size_t length)
    206{
    207	struct mei_nfc_hdr *hdr;
    208	u8 *mei_buf;
    209	int err;
    210
    211	err = -ENOMEM;
    212	mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
    213	if (!mei_buf)
    214		goto out;
    215
    216	hdr = (struct mei_nfc_hdr *)mei_buf;
    217	hdr->cmd = MEI_NFC_CMD_HCI_SEND;
    218	hdr->status = 0;
    219	hdr->req_id = phy->req_id;
    220	hdr->reserved = 0;
    221	hdr->data_size = length;
    222
    223	MEI_DUMP_NFC_HDR("send", hdr);
    224
    225	memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
    226	err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE);
    227	if (err < 0)
    228		goto out;
    229
    230	if (!wait_event_interruptible_timeout(phy->send_wq,
    231				phy->recv_req_id == phy->req_id, HZ)) {
    232		pr_err("NFC MEI command timeout\n");
    233		err = -ETIME;
    234	} else {
    235		phy->req_id++;
    236	}
    237out:
    238	kfree(mei_buf);
    239	return err;
    240}
    241
    242/*
    243 * Writing a frame must not return the number of written bytes.
    244 * It must return either zero for success, or <0 for error.
    245 * In addition, it must not alter the skb
    246 */
    247static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb)
    248{
    249	struct nfc_mei_phy *phy = phy_id;
    250	int r;
    251
    252	MEI_DUMP_SKB_OUT("mei frame sent", skb);
    253
    254	r = mei_nfc_send(phy, skb->data, skb->len);
    255	if (r > 0)
    256		r = 0;
    257
    258	return r;
    259}
    260
    261static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length)
    262{
    263	struct mei_nfc_hdr *hdr;
    264	int received_length;
    265
    266	received_length = mei_cldev_recv(phy->cldev, buf, length);
    267	if (received_length < 0)
    268		return received_length;
    269
    270	hdr = (struct mei_nfc_hdr *) buf;
    271
    272	MEI_DUMP_NFC_HDR("receive", hdr);
    273	if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
    274		phy->recv_req_id = hdr->req_id;
    275		wake_up(&phy->send_wq);
    276
    277		return 0;
    278	}
    279
    280	return received_length;
    281}
    282
    283
    284static void nfc_mei_rx_cb(struct mei_cl_device *cldev)
    285{
    286	struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
    287	struct sk_buff *skb;
    288	int reply_size;
    289
    290	if (!phy)
    291		return;
    292
    293	if (phy->hard_fault != 0)
    294		return;
    295
    296	skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
    297	if (!skb)
    298		return;
    299
    300	reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ);
    301	if (reply_size < MEI_NFC_HEADER_SIZE) {
    302		kfree_skb(skb);
    303		return;
    304	}
    305
    306	skb_put(skb, reply_size);
    307	skb_pull(skb, MEI_NFC_HEADER_SIZE);
    308
    309	MEI_DUMP_SKB_IN("mei frame read", skb);
    310
    311	nfc_hci_recv_frame(phy->hdev, skb);
    312}
    313
    314static int nfc_mei_phy_enable(void *phy_id)
    315{
    316	int r;
    317	struct nfc_mei_phy *phy = phy_id;
    318
    319	if (phy->powered == 1)
    320		return 0;
    321
    322	r = mei_cldev_enable(phy->cldev);
    323	if (r < 0) {
    324		pr_err("Could not enable device %d\n", r);
    325		return r;
    326	}
    327
    328	r = mei_nfc_if_version(phy);
    329	if (r < 0) {
    330		pr_err("Could not enable device %d\n", r);
    331		goto err;
    332	}
    333
    334	r = mei_nfc_connect(phy);
    335	if (r < 0) {
    336		pr_err("Could not connect to device %d\n", r);
    337		goto err;
    338	}
    339
    340	r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb);
    341	if (r) {
    342		pr_err("Event cb registration failed %d\n", r);
    343		goto err;
    344	}
    345
    346	phy->powered = 1;
    347
    348	return 0;
    349
    350err:
    351	phy->powered = 0;
    352	mei_cldev_disable(phy->cldev);
    353	return r;
    354}
    355
    356static void nfc_mei_phy_disable(void *phy_id)
    357{
    358	struct nfc_mei_phy *phy = phy_id;
    359
    360	mei_cldev_disable(phy->cldev);
    361
    362	phy->powered = 0;
    363}
    364
    365const struct nfc_phy_ops mei_phy_ops = {
    366	.write = nfc_mei_phy_write,
    367	.enable = nfc_mei_phy_enable,
    368	.disable = nfc_mei_phy_disable,
    369};
    370EXPORT_SYMBOL_GPL(mei_phy_ops);
    371
    372struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev)
    373{
    374	struct nfc_mei_phy *phy;
    375
    376	phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL);
    377	if (!phy)
    378		return NULL;
    379
    380	phy->cldev = cldev;
    381	init_waitqueue_head(&phy->send_wq);
    382	mei_cldev_set_drvdata(cldev, phy);
    383
    384	return phy;
    385}
    386EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc);
    387
    388void nfc_mei_phy_free(struct nfc_mei_phy *phy)
    389{
    390	mei_cldev_disable(phy->cldev);
    391	kfree(phy);
    392}
    393EXPORT_SYMBOL_GPL(nfc_mei_phy_free);
    394
    395MODULE_LICENSE("GPL");
    396MODULE_DESCRIPTION("mei bus NFC device interface");