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

vivid-cec.c (9626B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * vivid-cec.c - A Virtual Video Test Driver, cec emulation
      4 *
      5 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
      6 */
      7
      8#include <linux/delay.h>
      9#include <media/cec.h>
     10
     11#include "vivid-core.h"
     12#include "vivid-cec.h"
     13
     14#define CEC_START_BIT_US		4500
     15#define CEC_DATA_BIT_US			2400
     16#define CEC_MARGIN_US			350
     17
     18struct xfer_on_bus {
     19	struct cec_adapter	*adap;
     20	u8			status;
     21};
     22
     23static bool find_dest_adap(struct vivid_dev *dev,
     24			   struct cec_adapter *adap, u8 dest)
     25{
     26	unsigned int i;
     27
     28	if (dest >= 0xf)
     29		return false;
     30
     31	if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
     32	    dev->cec_rx_adap->is_configured &&
     33	    cec_has_log_addr(dev->cec_rx_adap, dest))
     34		return true;
     35
     36	for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
     37		if (adap == dev->cec_tx_adap[i])
     38			continue;
     39		if (!dev->cec_tx_adap[i]->is_configured)
     40			continue;
     41		if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
     42			return true;
     43	}
     44	return false;
     45}
     46
     47static bool xfer_ready(struct vivid_dev *dev)
     48{
     49	unsigned int i;
     50	bool ready = false;
     51
     52	spin_lock(&dev->cec_xfers_slock);
     53	for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
     54		if (dev->xfers[i].sft &&
     55		    dev->xfers[i].sft <= dev->cec_sft) {
     56			ready = true;
     57			break;
     58		}
     59	}
     60	spin_unlock(&dev->cec_xfers_slock);
     61
     62	return ready;
     63}
     64
     65/*
     66 * If an adapter tries to send successive messages, it must wait for the
     67 * longest signal-free time between its transmissions. But, if another
     68 * adapter sends a message in the interim, then the wait can be reduced
     69 * because the messages are no longer successive. Make these adjustments
     70 * if necessary. Should be called holding cec_xfers_slock.
     71 */
     72static void adjust_sfts(struct vivid_dev *dev)
     73{
     74	unsigned int i;
     75	u8 initiator;
     76
     77	for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
     78		if (dev->xfers[i].sft <= CEC_SIGNAL_FREE_TIME_RETRY)
     79			continue;
     80		initiator = dev->xfers[i].msg[0] >> 4;
     81		if (initiator == dev->last_initiator)
     82			dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
     83		else
     84			dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
     85	}
     86}
     87
     88/*
     89 * The main emulation of the bus on which CEC adapters attempt to send
     90 * messages to each other. The bus keeps track of how long it has been
     91 * signal-free and accepts a pending transmission only if the state of
     92 * the bus matches the transmission's signal-free requirements. It calls
     93 * cec_transmit_attempt_done() for all transmits that enter the bus and
     94 * cec_received_msg() for successful transmits.
     95 */
     96int vivid_cec_bus_thread(void *_dev)
     97{
     98	u32 last_sft;
     99	unsigned int i;
    100	unsigned int dest;
    101	ktime_t start, end;
    102	s64 delta_us, retry_us;
    103	struct vivid_dev *dev = _dev;
    104
    105	dev->cec_sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
    106	for (;;) {
    107		bool first = true;
    108		int wait_xfer_us = 0;
    109		bool valid_dest = false;
    110		int wait_arb_lost_us = 0;
    111		unsigned int first_idx = 0;
    112		unsigned int first_status = 0;
    113		struct cec_msg first_msg = {};
    114		struct xfer_on_bus xfers_on_bus[MAX_OUTPUTS] = {};
    115
    116		wait_event_interruptible(dev->kthread_waitq_cec, xfer_ready(dev) ||
    117					 kthread_should_stop());
    118		if (kthread_should_stop())
    119			break;
    120		last_sft = dev->cec_sft;
    121		dev->cec_sft = 0;
    122		/*
    123		 * Move the messages that are ready onto the bus. The adapter with
    124		 * the most leading zeros will win control of the bus and any other
    125		 * adapters will lose arbitration.
    126		 */
    127		spin_lock(&dev->cec_xfers_slock);
    128		for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
    129			if (!dev->xfers[i].sft || dev->xfers[i].sft > last_sft)
    130				continue;
    131			if (first) {
    132				first = false;
    133				first_idx = i;
    134				xfers_on_bus[first_idx].adap = dev->xfers[i].adap;
    135				memcpy(first_msg.msg, dev->xfers[i].msg, dev->xfers[i].len);
    136				first_msg.len = dev->xfers[i].len;
    137			} else {
    138				xfers_on_bus[i].adap = dev->xfers[i].adap;
    139				xfers_on_bus[i].status = CEC_TX_STATUS_ARB_LOST;
    140				/*
    141				 * For simplicity wait for all 4 bits of the initiator's
    142				 * address even though HDMI specification uses bit-level
    143				 * precision.
    144				 */
    145				wait_arb_lost_us = 4 * CEC_DATA_BIT_US + CEC_START_BIT_US;
    146			}
    147			dev->xfers[i].sft = 0;
    148		}
    149		dev->last_initiator = cec_msg_initiator(&first_msg);
    150		adjust_sfts(dev);
    151		spin_unlock(&dev->cec_xfers_slock);
    152
    153		dest = cec_msg_destination(&first_msg);
    154		valid_dest = cec_msg_is_broadcast(&first_msg);
    155		if (!valid_dest)
    156			valid_dest = find_dest_adap(dev, xfers_on_bus[first_idx].adap, dest);
    157		if (valid_dest) {
    158			first_status = CEC_TX_STATUS_OK;
    159			/*
    160			 * Message length is in bytes, but each byte is transmitted in
    161			 * a block of 10 bits.
    162			 */
    163			wait_xfer_us = first_msg.len * 10 * CEC_DATA_BIT_US;
    164		} else {
    165			first_status = CEC_TX_STATUS_NACK;
    166			/*
    167			 * A message that is not acknowledged stops transmitting after
    168			 * the header block of 10 bits.
    169			 */
    170			wait_xfer_us = 10 * CEC_DATA_BIT_US;
    171		}
    172		wait_xfer_us += CEC_START_BIT_US;
    173		xfers_on_bus[first_idx].status = first_status;
    174
    175		/* Sleep as if sending messages on a real hardware bus. */
    176		start = ktime_get();
    177		if (wait_arb_lost_us) {
    178			usleep_range(wait_arb_lost_us - CEC_MARGIN_US, wait_arb_lost_us);
    179			for (i = 0; i < ARRAY_SIZE(xfers_on_bus); i++) {
    180				if (xfers_on_bus[i].status != CEC_TX_STATUS_ARB_LOST)
    181					continue;
    182				cec_transmit_attempt_done(xfers_on_bus[i].adap,
    183							  CEC_TX_STATUS_ARB_LOST);
    184			}
    185			if (kthread_should_stop())
    186				break;
    187		}
    188		wait_xfer_us -= wait_arb_lost_us;
    189		usleep_range(wait_xfer_us - CEC_MARGIN_US, wait_xfer_us);
    190		cec_transmit_attempt_done(xfers_on_bus[first_idx].adap, first_status);
    191		if (kthread_should_stop())
    192			break;
    193		if (first_status == CEC_TX_STATUS_OK) {
    194			if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
    195				cec_received_msg(dev->cec_rx_adap, &first_msg);
    196			for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
    197				if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i])
    198					cec_received_msg(dev->cec_tx_adap[i], &first_msg);
    199		}
    200		end = ktime_get();
    201		/*
    202		 * If the emulated transfer took more or less time than it should
    203		 * have, then compensate by adjusting the wait time needed for the
    204		 * bus to be signal-free for 3 bit periods (the retry time).
    205		 */
    206		delta_us = div_s64(end - start, 1000);
    207		delta_us -= wait_xfer_us + wait_arb_lost_us;
    208		retry_us = CEC_SIGNAL_FREE_TIME_RETRY * CEC_DATA_BIT_US - delta_us;
    209		if (retry_us > CEC_MARGIN_US)
    210			usleep_range(retry_us - CEC_MARGIN_US, retry_us);
    211		dev->cec_sft = CEC_SIGNAL_FREE_TIME_RETRY;
    212		/*
    213		 * If there are no messages that need to be retried, check if any
    214		 * adapters that did not just transmit a message are ready to
    215		 * transmit. If none of these adapters are ready, then increase
    216		 * the signal-free time so that the bus is available to all
    217		 * adapters and go back to waiting for a transmission.
    218		 */
    219		while (dev->cec_sft >= CEC_SIGNAL_FREE_TIME_RETRY &&
    220		       dev->cec_sft < CEC_SIGNAL_FREE_TIME_NEXT_XFER &&
    221		       !xfer_ready(dev) && !kthread_should_stop()) {
    222			usleep_range(2 * CEC_DATA_BIT_US - CEC_MARGIN_US,
    223				     2 * CEC_DATA_BIT_US);
    224			dev->cec_sft += 2;
    225		}
    226	}
    227	return 0;
    228}
    229
    230static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
    231{
    232	adap->cec_pin_is_high = true;
    233	return 0;
    234}
    235
    236static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
    237{
    238	return 0;
    239}
    240
    241static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
    242				   u32 signal_free_time, struct cec_msg *msg)
    243{
    244	struct vivid_dev *dev = cec_get_drvdata(adap);
    245	u8 idx = cec_msg_initiator(msg);
    246
    247	spin_lock(&dev->cec_xfers_slock);
    248	dev->xfers[idx].adap = adap;
    249	memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
    250	dev->xfers[idx].len = msg->len;
    251	dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
    252	if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) {
    253		if (idx == dev->last_initiator)
    254			dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
    255		else
    256			dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
    257	}
    258	spin_unlock(&dev->cec_xfers_slock);
    259	wake_up_interruptible(&dev->kthread_waitq_cec);
    260
    261	return 0;
    262}
    263
    264static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
    265{
    266	struct vivid_dev *dev = cec_get_drvdata(adap);
    267	struct cec_msg reply;
    268	u8 dest = cec_msg_destination(msg);
    269	u8 disp_ctl;
    270	char osd[14];
    271
    272	if (cec_msg_is_broadcast(msg))
    273		dest = adap->log_addrs.log_addr[0];
    274	cec_msg_init(&reply, dest, cec_msg_initiator(msg));
    275
    276	switch (cec_msg_opcode(msg)) {
    277	case CEC_MSG_SET_OSD_STRING:
    278		if (!cec_is_sink(adap))
    279			return -ENOMSG;
    280		cec_ops_set_osd_string(msg, &disp_ctl, osd);
    281		switch (disp_ctl) {
    282		case CEC_OP_DISP_CTL_DEFAULT:
    283			strscpy(dev->osd, osd, sizeof(dev->osd));
    284			dev->osd_jiffies = jiffies;
    285			break;
    286		case CEC_OP_DISP_CTL_UNTIL_CLEARED:
    287			strscpy(dev->osd, osd, sizeof(dev->osd));
    288			dev->osd_jiffies = 0;
    289			break;
    290		case CEC_OP_DISP_CTL_CLEAR:
    291			dev->osd[0] = 0;
    292			dev->osd_jiffies = 0;
    293			break;
    294		default:
    295			cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
    296					      CEC_OP_ABORT_INVALID_OP);
    297			cec_transmit_msg(adap, &reply, false);
    298			break;
    299		}
    300		break;
    301	default:
    302		return -ENOMSG;
    303	}
    304	return 0;
    305}
    306
    307static const struct cec_adap_ops vivid_cec_adap_ops = {
    308	.adap_enable = vivid_cec_adap_enable,
    309	.adap_log_addr = vivid_cec_adap_log_addr,
    310	.adap_transmit = vivid_cec_adap_transmit,
    311	.received = vivid_received,
    312};
    313
    314struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
    315					 unsigned int idx,
    316					 bool is_source)
    317{
    318	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN;
    319	char name[32];
    320
    321	snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d",
    322		 dev->inst, is_source ? "out" : "cap", idx);
    323	return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
    324				    name, caps, CEC_MAX_LOG_ADDRS);
    325}