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

trans.c (5083B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
      3
      4#include <linux/types.h>
      5#include <linux/export.h>
      6#include <linux/slab.h>
      7
      8#include "core.h"
      9#include "commands.h"
     10#include "event.h"
     11#include "bus.h"
     12
     13#define QTNF_DEF_SYNC_CMD_TIMEOUT	(5 * HZ)
     14
     15int qtnf_trans_send_cmd_with_resp(struct qtnf_bus *bus, struct sk_buff *cmd_skb,
     16				  struct sk_buff **response_skb)
     17{
     18	struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd;
     19	struct qlink_cmd *cmd = (void *)cmd_skb->data;
     20	int ret = 0;
     21	long status;
     22	bool resp_not_handled = true;
     23	struct sk_buff *resp_skb = NULL;
     24
     25	if (unlikely(!response_skb)) {
     26		dev_kfree_skb(cmd_skb);
     27		return -EFAULT;
     28	}
     29
     30	spin_lock(&ctl_node->resp_lock);
     31	ctl_node->seq_num++;
     32	cmd->seq_num = cpu_to_le16(ctl_node->seq_num);
     33	WARN(ctl_node->resp_skb, "qtnfmac: response skb not empty\n");
     34	ctl_node->waiting_for_resp = true;
     35	spin_unlock(&ctl_node->resp_lock);
     36
     37	ret = qtnf_bus_control_tx(bus, cmd_skb);
     38	dev_kfree_skb(cmd_skb);
     39
     40	if (unlikely(ret))
     41		goto out;
     42
     43	status = wait_for_completion_interruptible_timeout(
     44						&ctl_node->cmd_resp_completion,
     45						QTNF_DEF_SYNC_CMD_TIMEOUT);
     46
     47	spin_lock(&ctl_node->resp_lock);
     48	resp_not_handled = ctl_node->waiting_for_resp;
     49	resp_skb = ctl_node->resp_skb;
     50	ctl_node->resp_skb = NULL;
     51	ctl_node->waiting_for_resp = false;
     52	spin_unlock(&ctl_node->resp_lock);
     53
     54	if (unlikely(status <= 0)) {
     55		if (status == 0) {
     56			ret = -ETIMEDOUT;
     57			pr_err("response timeout\n");
     58		} else {
     59			ret = -EINTR;
     60			pr_debug("interrupted\n");
     61		}
     62	}
     63
     64	if (unlikely(!resp_skb || resp_not_handled)) {
     65		if (!ret)
     66			ret = -EFAULT;
     67
     68		goto out;
     69	}
     70
     71	ret = 0;
     72	*response_skb = resp_skb;
     73
     74out:
     75	if (unlikely(resp_skb && resp_not_handled))
     76		dev_kfree_skb(resp_skb);
     77
     78	return ret;
     79}
     80
     81static void qtnf_trans_signal_cmdresp(struct qtnf_bus *bus, struct sk_buff *skb)
     82{
     83	struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd;
     84	const struct qlink_resp *resp = (const struct qlink_resp *)skb->data;
     85	const u16 recvd_seq_num = le16_to_cpu(resp->seq_num);
     86
     87	spin_lock(&ctl_node->resp_lock);
     88
     89	if (unlikely(!ctl_node->waiting_for_resp)) {
     90		pr_err("unexpected response\n");
     91		goto out_err;
     92	}
     93
     94	if (unlikely(recvd_seq_num != ctl_node->seq_num)) {
     95		pr_err("seq num mismatch\n");
     96		goto out_err;
     97	}
     98
     99	ctl_node->resp_skb = skb;
    100	ctl_node->waiting_for_resp = false;
    101
    102	spin_unlock(&ctl_node->resp_lock);
    103
    104	complete(&ctl_node->cmd_resp_completion);
    105	return;
    106
    107out_err:
    108	spin_unlock(&ctl_node->resp_lock);
    109	dev_kfree_skb(skb);
    110}
    111
    112static int qtnf_trans_event_enqueue(struct qtnf_bus *bus, struct sk_buff *skb)
    113{
    114	struct qtnf_qlink_transport *trans = &bus->trans;
    115
    116	if (likely(skb_queue_len(&trans->event_queue) <
    117		   trans->event_queue_max_len)) {
    118		skb_queue_tail(&trans->event_queue, skb);
    119		queue_work(bus->workqueue, &bus->event_work);
    120	} else {
    121		pr_warn("event dropped due to queue overflow\n");
    122		dev_kfree_skb(skb);
    123		return -1;
    124	}
    125
    126	return 0;
    127}
    128
    129void qtnf_trans_init(struct qtnf_bus *bus)
    130{
    131	struct qtnf_qlink_transport *trans = &bus->trans;
    132
    133	init_completion(&trans->curr_cmd.cmd_resp_completion);
    134	spin_lock_init(&trans->curr_cmd.resp_lock);
    135
    136	spin_lock(&trans->curr_cmd.resp_lock);
    137	trans->curr_cmd.seq_num = 0;
    138	trans->curr_cmd.waiting_for_resp = false;
    139	trans->curr_cmd.resp_skb = NULL;
    140	spin_unlock(&trans->curr_cmd.resp_lock);
    141
    142	/* Init event handling related fields */
    143	skb_queue_head_init(&trans->event_queue);
    144	trans->event_queue_max_len = QTNF_MAX_EVENT_QUEUE_LEN;
    145}
    146
    147static void qtnf_trans_free_events(struct qtnf_bus *bus)
    148{
    149	struct sk_buff_head *event_queue = &bus->trans.event_queue;
    150	struct sk_buff *current_event_skb = skb_dequeue(event_queue);
    151
    152	while (current_event_skb) {
    153		dev_kfree_skb_any(current_event_skb);
    154		current_event_skb = skb_dequeue(event_queue);
    155	}
    156}
    157
    158void qtnf_trans_free(struct qtnf_bus *bus)
    159{
    160	if (!bus) {
    161		pr_err("invalid bus pointer\n");
    162		return;
    163	}
    164
    165	qtnf_trans_free_events(bus);
    166}
    167
    168int qtnf_trans_handle_rx_ctl_packet(struct qtnf_bus *bus, struct sk_buff *skb)
    169{
    170	const struct qlink_msg_header *header = (void *)skb->data;
    171	int ret = -1;
    172
    173	if (unlikely(skb->len < sizeof(*header))) {
    174		pr_warn("packet is too small: %u\n", skb->len);
    175		dev_kfree_skb(skb);
    176		return -EINVAL;
    177	}
    178
    179	if (unlikely(skb->len != le16_to_cpu(header->len))) {
    180		pr_warn("cmd reply length mismatch: %u != %u\n",
    181			skb->len, le16_to_cpu(header->len));
    182		dev_kfree_skb(skb);
    183		return -EFAULT;
    184	}
    185
    186	switch (le16_to_cpu(header->type)) {
    187	case QLINK_MSG_TYPE_CMDRSP:
    188		if (unlikely(skb->len < sizeof(struct qlink_cmd))) {
    189			pr_warn("cmd reply too short: %u\n", skb->len);
    190			dev_kfree_skb(skb);
    191			break;
    192		}
    193
    194		qtnf_trans_signal_cmdresp(bus, skb);
    195		break;
    196	case QLINK_MSG_TYPE_EVENT:
    197		if (unlikely(skb->len < sizeof(struct qlink_event))) {
    198			pr_warn("event too short: %u\n", skb->len);
    199			dev_kfree_skb(skb);
    200			break;
    201		}
    202
    203		ret = qtnf_trans_event_enqueue(bus, skb);
    204		break;
    205	default:
    206		pr_warn("unknown packet type: %x\n", le16_to_cpu(header->type));
    207		dev_kfree_skb(skb);
    208		break;
    209	}
    210
    211	return ret;
    212}
    213EXPORT_SYMBOL_GPL(qtnf_trans_handle_rx_ctl_packet);