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

cec-pin-error-inj.c (11402B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
      4 */
      5
      6#include <linux/delay.h>
      7#include <linux/slab.h>
      8#include <linux/sched/types.h>
      9
     10#include <media/cec-pin.h>
     11#include "cec-pin-priv.h"
     12
     13struct cec_error_inj_cmd {
     14	unsigned int mode_offset;
     15	int arg_idx;
     16	const char *cmd;
     17};
     18
     19static const struct cec_error_inj_cmd cec_error_inj_cmds[] = {
     20	{ CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" },
     21	{ CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
     22	  CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" },
     23	{ CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" },
     24	{ CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" },
     25	{ CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
     26	  CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" },
     27
     28	{ CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" },
     29	{ CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" },
     30	{ CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
     31	  CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" },
     32	{ CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" },
     33	{ CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
     34	  CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" },
     35	{ CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
     36	  CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" },
     37	{ CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
     38	  CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" },
     39	{ CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" },
     40	{ CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" },
     41	{ CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" },
     42	{ CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
     43	  CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" },
     44	{ CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
     45	  CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" },
     46	{ 0, -1, NULL }
     47};
     48
     49u16 cec_pin_rx_error_inj(struct cec_pin *pin)
     50{
     51	u16 cmd = CEC_ERROR_INJ_OP_ANY;
     52
     53	/* Only when 18 bits have been received do we have a valid cmd */
     54	if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) &&
     55	    pin->rx_bit >= 18)
     56		cmd = pin->rx_msg.msg[1];
     57	return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd :
     58		CEC_ERROR_INJ_OP_ANY;
     59}
     60
     61u16 cec_pin_tx_error_inj(struct cec_pin *pin)
     62{
     63	u16 cmd = CEC_ERROR_INJ_OP_ANY;
     64
     65	if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) &&
     66	    pin->tx_msg.len > 1)
     67		cmd = pin->tx_msg.msg[1];
     68	return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd :
     69		CEC_ERROR_INJ_OP_ANY;
     70}
     71
     72bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
     73{
     74	static const char *delims = " \t\r";
     75	struct cec_pin *pin = adap->pin;
     76	unsigned int i;
     77	bool has_pos = false;
     78	char *p = line;
     79	char *token;
     80	char *comma;
     81	u64 *error;
     82	u8 *args;
     83	bool has_op;
     84	u8 op;
     85	u8 mode;
     86	u8 pos;
     87
     88	p = skip_spaces(p);
     89	token = strsep(&p, delims);
     90	if (!strcmp(token, "clear")) {
     91		memset(pin->error_inj, 0, sizeof(pin->error_inj));
     92		pin->rx_toggle = pin->tx_toggle = false;
     93		pin->tx_ignore_nack_until_eom = false;
     94		pin->tx_custom_pulse = false;
     95		pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
     96		pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
     97		return true;
     98	}
     99	if (!strcmp(token, "rx-clear")) {
    100		for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
    101			pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
    102		pin->rx_toggle = false;
    103		return true;
    104	}
    105	if (!strcmp(token, "tx-clear")) {
    106		for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
    107			pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
    108		pin->tx_toggle = false;
    109		pin->tx_ignore_nack_until_eom = false;
    110		pin->tx_custom_pulse = false;
    111		pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
    112		pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
    113		return true;
    114	}
    115	if (!strcmp(token, "tx-ignore-nack-until-eom")) {
    116		pin->tx_ignore_nack_until_eom = true;
    117		return true;
    118	}
    119	if (!strcmp(token, "tx-custom-pulse")) {
    120		pin->tx_custom_pulse = true;
    121		cec_pin_start_timer(pin);
    122		return true;
    123	}
    124	if (!p)
    125		return false;
    126
    127	p = skip_spaces(p);
    128	if (!strcmp(token, "tx-custom-low-usecs")) {
    129		u32 usecs;
    130
    131		if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
    132			return false;
    133		pin->tx_custom_low_usecs = usecs;
    134		return true;
    135	}
    136	if (!strcmp(token, "tx-custom-high-usecs")) {
    137		u32 usecs;
    138
    139		if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
    140			return false;
    141		pin->tx_custom_high_usecs = usecs;
    142		return true;
    143	}
    144
    145	comma = strchr(token, ',');
    146	if (comma)
    147		*comma++ = '\0';
    148	if (!strcmp(token, "any")) {
    149		has_op = false;
    150		error = pin->error_inj + CEC_ERROR_INJ_OP_ANY;
    151		args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY];
    152	} else if (!kstrtou8(token, 0, &op)) {
    153		has_op = true;
    154		error = pin->error_inj + op;
    155		args = pin->error_inj_args[op];
    156	} else {
    157		return false;
    158	}
    159
    160	mode = CEC_ERROR_INJ_MODE_ONCE;
    161	if (comma) {
    162		if (!strcmp(comma, "off"))
    163			mode = CEC_ERROR_INJ_MODE_OFF;
    164		else if (!strcmp(comma, "once"))
    165			mode = CEC_ERROR_INJ_MODE_ONCE;
    166		else if (!strcmp(comma, "always"))
    167			mode = CEC_ERROR_INJ_MODE_ALWAYS;
    168		else if (!strcmp(comma, "toggle"))
    169			mode = CEC_ERROR_INJ_MODE_TOGGLE;
    170		else
    171			return false;
    172	}
    173
    174	token = strsep(&p, delims);
    175	if (p) {
    176		p = skip_spaces(p);
    177		has_pos = !kstrtou8(p, 0, &pos);
    178	}
    179
    180	if (!strcmp(token, "clear")) {
    181		*error = 0;
    182		return true;
    183	}
    184	if (!strcmp(token, "rx-clear")) {
    185		*error &= ~CEC_ERROR_INJ_RX_MASK;
    186		return true;
    187	}
    188	if (!strcmp(token, "tx-clear")) {
    189		*error &= ~CEC_ERROR_INJ_TX_MASK;
    190		return true;
    191	}
    192
    193	for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
    194		const char *cmd = cec_error_inj_cmds[i].cmd;
    195		unsigned int mode_offset;
    196		u64 mode_mask;
    197		int arg_idx;
    198		bool is_bit_pos = true;
    199
    200		if (strcmp(token, cmd))
    201			continue;
    202
    203		mode_offset = cec_error_inj_cmds[i].mode_offset;
    204		mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
    205		arg_idx = cec_error_inj_cmds[i].arg_idx;
    206
    207		if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
    208			if (has_op)
    209				return false;
    210			if (!has_pos)
    211				pos = 0x0f;
    212			is_bit_pos = false;
    213		} else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) {
    214			if (!has_pos || !pos)
    215				return false;
    216			is_bit_pos = false;
    217		}
    218
    219		if (arg_idx >= 0 && is_bit_pos) {
    220			if (!has_pos || pos >= 160)
    221				return false;
    222			if (has_op && pos < 10 + 8)
    223				return false;
    224			/* Invalid bit position may not be the Ack bit */
    225			if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
    226			     mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
    227			     mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
    228			    (pos % 10) == 9)
    229				return false;
    230		}
    231		*error &= ~mode_mask;
    232		*error |= (u64)mode << mode_offset;
    233		if (arg_idx >= 0)
    234			args[arg_idx] = pos;
    235		return true;
    236	}
    237	return false;
    238}
    239
    240static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
    241{
    242	if (cmd == CEC_ERROR_INJ_OP_ANY)
    243		seq_puts(sf, "any,");
    244	else
    245		seq_printf(sf, "0x%02x,", cmd);
    246	switch (mode) {
    247	case CEC_ERROR_INJ_MODE_ONCE:
    248		seq_puts(sf, "once ");
    249		break;
    250	case CEC_ERROR_INJ_MODE_ALWAYS:
    251		seq_puts(sf, "always ");
    252		break;
    253	case CEC_ERROR_INJ_MODE_TOGGLE:
    254		seq_puts(sf, "toggle ");
    255		break;
    256	default:
    257		seq_puts(sf, "off ");
    258		break;
    259	}
    260}
    261
    262int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
    263{
    264	struct cec_pin *pin = adap->pin;
    265	unsigned int i, j;
    266
    267	seq_puts(sf, "# Clear error injections:\n");
    268	seq_puts(sf, "#   clear          clear all rx and tx error injections\n");
    269	seq_puts(sf, "#   rx-clear       clear all rx error injections\n");
    270	seq_puts(sf, "#   tx-clear       clear all tx error injections\n");
    271	seq_puts(sf, "#   <op> clear     clear all rx and tx error injections for <op>\n");
    272	seq_puts(sf, "#   <op> rx-clear  clear all rx error injections for <op>\n");
    273	seq_puts(sf, "#   <op> tx-clear  clear all tx error injections for <op>\n");
    274	seq_puts(sf, "#\n");
    275	seq_puts(sf, "# RX error injection:\n");
    276	seq_puts(sf, "#   <op>[,<mode>] rx-nack              NACK the message instead of sending an ACK\n");
    277	seq_puts(sf, "#   <op>[,<mode>] rx-low-drive <bit>   force a low-drive condition at this bit position\n");
    278	seq_puts(sf, "#   <op>[,<mode>] rx-add-byte          add a spurious byte to the received CEC message\n");
    279	seq_puts(sf, "#   <op>[,<mode>] rx-remove-byte       remove the last byte from the received CEC message\n");
    280	seq_puts(sf, "#    any[,<mode>] rx-arb-lost [<poll>] generate a POLL message to trigger an arbitration lost\n");
    281	seq_puts(sf, "#\n");
    282	seq_puts(sf, "# TX error injection settings:\n");
    283	seq_puts(sf, "#   tx-ignore-nack-until-eom           ignore early NACKs until EOM\n");
    284	seq_puts(sf, "#   tx-custom-low-usecs <usecs>        define the 'low' time for the custom pulse\n");
    285	seq_puts(sf, "#   tx-custom-high-usecs <usecs>       define the 'high' time for the custom pulse\n");
    286	seq_puts(sf, "#   tx-custom-pulse                    transmit the custom pulse once the bus is idle\n");
    287	seq_puts(sf, "#\n");
    288	seq_puts(sf, "# TX error injection:\n");
    289	seq_puts(sf, "#   <op>[,<mode>] tx-no-eom            don't set the EOM bit\n");
    290	seq_puts(sf, "#   <op>[,<mode>] tx-early-eom         set the EOM bit one byte too soon\n");
    291	seq_puts(sf, "#   <op>[,<mode>] tx-add-bytes <num>   append <num> (1-255) spurious bytes to the message\n");
    292	seq_puts(sf, "#   <op>[,<mode>] tx-remove-byte       drop the last byte from the message\n");
    293	seq_puts(sf, "#   <op>[,<mode>] tx-short-bit <bit>   make this bit shorter than allowed\n");
    294	seq_puts(sf, "#   <op>[,<mode>] tx-long-bit <bit>    make this bit longer than allowed\n");
    295	seq_puts(sf, "#   <op>[,<mode>] tx-custom-bit <bit>  send the custom pulse instead of this bit\n");
    296	seq_puts(sf, "#   <op>[,<mode>] tx-short-start       send a start pulse that's too short\n");
    297	seq_puts(sf, "#   <op>[,<mode>] tx-long-start        send a start pulse that's too long\n");
    298	seq_puts(sf, "#   <op>[,<mode>] tx-custom-start      send the custom pulse instead of the start pulse\n");
    299	seq_puts(sf, "#   <op>[,<mode>] tx-last-bit <bit>    stop sending after this bit\n");
    300	seq_puts(sf, "#   <op>[,<mode>] tx-low-drive <bit>   force a low-drive condition at this bit position\n");
    301	seq_puts(sf, "#\n");
    302	seq_puts(sf, "# <op>       CEC message opcode (0-255) or 'any'\n");
    303	seq_puts(sf, "# <mode>     'once' (default), 'always', 'toggle' or 'off'\n");
    304	seq_puts(sf, "# <bit>      CEC message bit (0-159)\n");
    305	seq_puts(sf, "#            10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
    306	seq_puts(sf, "# <poll>     CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
    307	seq_puts(sf, "# <usecs>    microseconds (0-10000000, default 1000)\n");
    308
    309	seq_puts(sf, "\nclear\n");
    310
    311	for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
    312		u64 e = pin->error_inj[i];
    313
    314		for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
    315			const char *cmd = cec_error_inj_cmds[j].cmd;
    316			unsigned int mode;
    317			unsigned int mode_offset;
    318			int arg_idx;
    319
    320			mode_offset = cec_error_inj_cmds[j].mode_offset;
    321			arg_idx = cec_error_inj_cmds[j].arg_idx;
    322			mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
    323			if (!mode)
    324				continue;
    325			cec_pin_show_cmd(sf, i, mode);
    326			seq_puts(sf, cmd);
    327			if (arg_idx >= 0)
    328				seq_printf(sf, " %u",
    329					   pin->error_inj_args[i][arg_idx]);
    330			seq_puts(sf, "\n");
    331		}
    332	}
    333
    334	if (pin->tx_ignore_nack_until_eom)
    335		seq_puts(sf, "tx-ignore-nack-until-eom\n");
    336	if (pin->tx_custom_pulse)
    337		seq_puts(sf, "tx-custom-pulse\n");
    338	if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
    339		seq_printf(sf, "tx-custom-low-usecs %u\n",
    340			   pin->tx_custom_low_usecs);
    341	if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
    342		seq_printf(sf, "tx-custom-high-usecs %u\n",
    343			   pin->tx_custom_high_usecs);
    344	return 0;
    345}