aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2018-01-11 23:29:14 -0500
committerDaniel Borkmann <daniel@iogearbox.net>2018-01-14 17:36:30 -0500
commitce4ebfd859c33ea098bfa2e1b4623128046f59c8 (patch)
treee5fce8510ee95c5978b2bc80aeaefe20e7ebfcc3
parent9d080d5da959ac4b64954f47b5ffd35a752d268e (diff)
nfp: bpf: add helpers for updating immediate instructions
Immediate loads are used to load the return address of a helper. We need to be able to update those loads for relocations. Immediate loads can be slightly more complex and spread over two instructions in general, but here we only care about simple loads of small (< 65k) constants, so complex cases are not handled. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.c58
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.h4
2 files changed, 62 insertions, 0 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.c b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
index 9ee3a3f60cc7..3f6952b66a49 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
@@ -50,6 +50,11 @@ const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
50 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, 50 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },
51}; 51};
52 52
53static bool unreg_is_imm(u16 reg)
54{
55 return (reg & UR_REG_IMM) == UR_REG_IMM;
56}
57
53u16 br_get_offset(u64 instr) 58u16 br_get_offset(u64 instr)
54{ 59{
55 u16 addr_lo, addr_hi; 60 u16 addr_lo, addr_hi;
@@ -80,6 +85,59 @@ void br_add_offset(u64 *instr, u16 offset)
80 br_set_offset(instr, addr + offset); 85 br_set_offset(instr, addr + offset);
81} 86}
82 87
88static bool immed_can_modify(u64 instr)
89{
90 if (FIELD_GET(OP_IMMED_INV, instr) ||
91 FIELD_GET(OP_IMMED_SHIFT, instr) ||
92 FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) {
93 pr_err("Can't decode/encode immed!\n");
94 return false;
95 }
96 return true;
97}
98
99u16 immed_get_value(u64 instr)
100{
101 u16 reg;
102
103 if (!immed_can_modify(instr))
104 return 0;
105
106 reg = FIELD_GET(OP_IMMED_A_SRC, instr);
107 if (!unreg_is_imm(reg))
108 reg = FIELD_GET(OP_IMMED_B_SRC, instr);
109
110 return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr);
111}
112
113void immed_set_value(u64 *instr, u16 immed)
114{
115 if (!immed_can_modify(*instr))
116 return;
117
118 if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) {
119 *instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff);
120 *instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff);
121 } else {
122 *instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff);
123 *instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff);
124 }
125
126 *instr &= ~OP_IMMED_IMM;
127 *instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8);
128}
129
130void immed_add_value(u64 *instr, u16 offset)
131{
132 u16 val;
133
134 if (!immed_can_modify(*instr))
135 return;
136
137 val = immed_get_value(*instr);
138 immed_set_value(instr, val + offset);
139}
140
83static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst) 141static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)
84{ 142{
85 bool lm_id, lm_dec = false; 143 bool lm_id, lm_dec = false;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
index 20e51cb60e69..5f9291db98e0 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
@@ -138,6 +138,10 @@ enum immed_shift {
138 IMMED_SHIFT_2B = 2, 138 IMMED_SHIFT_2B = 2,
139}; 139};
140 140
141u16 immed_get_value(u64 instr);
142void immed_set_value(u64 *instr, u16 immed);
143void immed_add_value(u64 *instr, u16 offset);
144
141#define OP_SHF_BASE 0x08000000000ULL 145#define OP_SHF_BASE 0x08000000000ULL
142#define OP_SHF_A_SRC 0x000000000ffULL 146#define OP_SHF_A_SRC 0x000000000ffULL
143#define OP_SHF_SC 0x00000000300ULL 147#define OP_SHF_SC 0x00000000300ULL