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

nfp_asm.c (7495B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
      2/* Copyright (C) 2016-2018 Netronome Systems, Inc. */
      3
      4#include <linux/bitops.h>
      5#include <linux/errno.h>
      6#include <linux/kernel.h>
      7#include <linux/string.h>
      8#include <linux/types.h>
      9
     10#include "nfp_asm.h"
     11
     12const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
     13	[CMD_TGT_WRITE8_SWAP] =		{ 0x02, 0x42 },
     14	[CMD_TGT_WRITE32_SWAP] =	{ 0x02, 0x5f },
     15	[CMD_TGT_READ8] =		{ 0x01, 0x43 },
     16	[CMD_TGT_READ32] =		{ 0x00, 0x5c },
     17	[CMD_TGT_READ32_LE] =		{ 0x01, 0x5c },
     18	[CMD_TGT_READ32_SWAP] =		{ 0x02, 0x5c },
     19	[CMD_TGT_READ_LE] =		{ 0x01, 0x40 },
     20	[CMD_TGT_READ_SWAP_LE] =	{ 0x03, 0x40 },
     21	[CMD_TGT_ADD] =			{ 0x00, 0x47 },
     22	[CMD_TGT_ADD_IMM] =		{ 0x02, 0x47 },
     23};
     24
     25static bool unreg_is_imm(u16 reg)
     26{
     27	return (reg & UR_REG_IMM) == UR_REG_IMM;
     28}
     29
     30u16 br_get_offset(u64 instr)
     31{
     32	u16 addr_lo, addr_hi;
     33
     34	addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr);
     35	addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr);
     36
     37	return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) |
     38		addr_lo;
     39}
     40
     41void br_set_offset(u64 *instr, u16 offset)
     42{
     43	u16 addr_lo, addr_hi;
     44
     45	addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO));
     46	addr_hi = offset != addr_lo;
     47	*instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO);
     48	*instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi);
     49	*instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo);
     50}
     51
     52void br_add_offset(u64 *instr, u16 offset)
     53{
     54	u16 addr;
     55
     56	addr = br_get_offset(*instr);
     57	br_set_offset(instr, addr + offset);
     58}
     59
     60static bool immed_can_modify(u64 instr)
     61{
     62	if (FIELD_GET(OP_IMMED_INV, instr) ||
     63	    FIELD_GET(OP_IMMED_SHIFT, instr) ||
     64	    FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) {
     65		pr_err("Can't decode/encode immed!\n");
     66		return false;
     67	}
     68	return true;
     69}
     70
     71u16 immed_get_value(u64 instr)
     72{
     73	u16 reg;
     74
     75	if (!immed_can_modify(instr))
     76		return 0;
     77
     78	reg = FIELD_GET(OP_IMMED_A_SRC, instr);
     79	if (!unreg_is_imm(reg))
     80		reg = FIELD_GET(OP_IMMED_B_SRC, instr);
     81
     82	return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8;
     83}
     84
     85void immed_set_value(u64 *instr, u16 immed)
     86{
     87	if (!immed_can_modify(*instr))
     88		return;
     89
     90	if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) {
     91		*instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff);
     92		*instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff);
     93	} else {
     94		*instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff);
     95		*instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff);
     96	}
     97
     98	*instr &= ~OP_IMMED_IMM;
     99	*instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8);
    100}
    101
    102void immed_add_value(u64 *instr, u16 offset)
    103{
    104	u16 val;
    105
    106	if (!immed_can_modify(*instr))
    107		return;
    108
    109	val = immed_get_value(*instr);
    110	immed_set_value(instr, val + offset);
    111}
    112
    113static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)
    114{
    115	bool lm_id, lm_dec = false;
    116	u16 val = swreg_value(reg);
    117
    118	switch (swreg_type(reg)) {
    119	case NN_REG_GPR_A:
    120	case NN_REG_GPR_B:
    121	case NN_REG_GPR_BOTH:
    122		return val;
    123	case NN_REG_NNR:
    124		return UR_REG_NN | val;
    125	case NN_REG_XFER:
    126		return UR_REG_XFR | val;
    127	case NN_REG_LMEM:
    128		lm_id = swreg_lm_idx(reg);
    129
    130		switch (swreg_lm_mode(reg)) {
    131		case NN_LM_MOD_NONE:
    132			if (val & ~UR_REG_LM_IDX_MAX) {
    133				pr_err("LM offset too large\n");
    134				return 0;
    135			}
    136			return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) |
    137				val;
    138		case NN_LM_MOD_DEC:
    139			lm_dec = true;
    140			fallthrough;
    141		case NN_LM_MOD_INC:
    142			if (val) {
    143				pr_err("LM offset in inc/dev mode\n");
    144				return 0;
    145			}
    146			return UR_REG_LM | UR_REG_LM_POST_MOD |
    147				FIELD_PREP(UR_REG_LM_IDX, lm_id) |
    148				FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec);
    149		default:
    150			pr_err("bad LM mode for unrestricted operands %d\n",
    151			       swreg_lm_mode(reg));
    152			return 0;
    153		}
    154	case NN_REG_IMM:
    155		if (val & ~0xff) {
    156			pr_err("immediate too large\n");
    157			return 0;
    158		}
    159		return UR_REG_IMM_encode(val);
    160	case NN_REG_NONE:
    161		return is_dst ? UR_REG_NO_DST : REG_NONE;
    162	}
    163
    164	pr_err("unrecognized reg encoding %08x\n", reg);
    165	return 0;
    166}
    167
    168int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,
    169			  struct nfp_insn_ur_regs *reg)
    170{
    171	memset(reg, 0, sizeof(*reg));
    172
    173	/* Decode destination */
    174	if (swreg_type(dst) == NN_REG_IMM)
    175		return -EFAULT;
    176
    177	if (swreg_type(dst) == NN_REG_GPR_B)
    178		reg->dst_ab = ALU_DST_B;
    179	if (swreg_type(dst) == NN_REG_GPR_BOTH)
    180		reg->wr_both = true;
    181	reg->dst = nfp_swreg_to_unreg(dst, true);
    182
    183	/* Decode source operands */
    184	if (swreg_type(lreg) == swreg_type(rreg) &&
    185	    swreg_type(lreg) != NN_REG_NONE)
    186		return -EFAULT;
    187
    188	if (swreg_type(lreg) == NN_REG_GPR_B ||
    189	    swreg_type(rreg) == NN_REG_GPR_A) {
    190		reg->areg = nfp_swreg_to_unreg(rreg, false);
    191		reg->breg = nfp_swreg_to_unreg(lreg, false);
    192		reg->swap = true;
    193	} else {
    194		reg->areg = nfp_swreg_to_unreg(lreg, false);
    195		reg->breg = nfp_swreg_to_unreg(rreg, false);
    196	}
    197
    198	reg->dst_lmextn = swreg_lmextn(dst);
    199	reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);
    200
    201	return 0;
    202}
    203
    204static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8)
    205{
    206	u16 val = swreg_value(reg);
    207	bool lm_id;
    208
    209	switch (swreg_type(reg)) {
    210	case NN_REG_GPR_A:
    211	case NN_REG_GPR_B:
    212	case NN_REG_GPR_BOTH:
    213		return val;
    214	case NN_REG_XFER:
    215		return RE_REG_XFR | val;
    216	case NN_REG_LMEM:
    217		lm_id = swreg_lm_idx(reg);
    218
    219		if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) {
    220			pr_err("bad LM mode for restricted operands %d\n",
    221			       swreg_lm_mode(reg));
    222			return 0;
    223		}
    224
    225		if (val & ~RE_REG_LM_IDX_MAX) {
    226			pr_err("LM offset too large\n");
    227			return 0;
    228		}
    229
    230		return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val;
    231	case NN_REG_IMM:
    232		if (val & ~(0x7f | has_imm8 << 7)) {
    233			pr_err("immediate too large\n");
    234			return 0;
    235		}
    236		*i8 = val & 0x80;
    237		return RE_REG_IMM_encode(val & 0x7f);
    238	case NN_REG_NONE:
    239		return is_dst ? RE_REG_NO_DST : REG_NONE;
    240	case NN_REG_NNR:
    241		pr_err("NNRs used with restricted encoding\n");
    242		return 0;
    243	}
    244
    245	pr_err("unrecognized reg encoding\n");
    246	return 0;
    247}
    248
    249int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,
    250			struct nfp_insn_re_regs *reg, bool has_imm8)
    251{
    252	memset(reg, 0, sizeof(*reg));
    253
    254	/* Decode destination */
    255	if (swreg_type(dst) == NN_REG_IMM)
    256		return -EFAULT;
    257
    258	if (swreg_type(dst) == NN_REG_GPR_B)
    259		reg->dst_ab = ALU_DST_B;
    260	if (swreg_type(dst) == NN_REG_GPR_BOTH)
    261		reg->wr_both = true;
    262	reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL);
    263
    264	/* Decode source operands */
    265	if (swreg_type(lreg) == swreg_type(rreg) &&
    266	    swreg_type(lreg) != NN_REG_NONE)
    267		return -EFAULT;
    268
    269	if (swreg_type(lreg) == NN_REG_GPR_B ||
    270	    swreg_type(rreg) == NN_REG_GPR_A) {
    271		reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
    272		reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
    273		reg->swap = true;
    274	} else {
    275		reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
    276		reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
    277	}
    278
    279	reg->dst_lmextn = swreg_lmextn(dst);
    280	reg->src_lmextn = swreg_lmextn(lreg) || swreg_lmextn(rreg);
    281
    282	return 0;
    283}
    284
    285#define NFP_USTORE_ECC_POLY_WORDS		7
    286#define NFP_USTORE_OP_BITS			45
    287
    288static const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = {
    289	0x0ff800007fffULL,
    290	0x11f801ff801fULL,
    291	0x1e387e0781e1ULL,
    292	0x17cb8e388e22ULL,
    293	0x1af5b2c93244ULL,
    294	0x1f56d5525488ULL,
    295	0x0daf69a46910ULL,
    296};
    297
    298static bool parity(u64 value)
    299{
    300	return hweight64(value) & 1;
    301}
    302
    303int nfp_ustore_check_valid_no_ecc(u64 insn)
    304{
    305	if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0))
    306		return -EINVAL;
    307
    308	return 0;
    309}
    310
    311u64 nfp_ustore_calc_ecc_insn(u64 insn)
    312{
    313	u8 ecc = 0;
    314	int i;
    315
    316	for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++)
    317		ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i;
    318
    319	return insn | (u64)ecc << NFP_USTORE_OP_BITS;
    320}