root/drivers/net/ethernet/intel/ice/ice_parser_rt.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2024 Intel Corporation */

#include "ice_common.h"

static void ice_rt_tsr_set(struct ice_parser_rt *rt, u16 tsr)
{
        rt->gpr[ICE_GPR_TSR_IDX] = tsr;
}

static void ice_rt_ho_set(struct ice_parser_rt *rt, u16 ho)
{
        rt->gpr[ICE_GPR_HO_IDX] = ho;
        memcpy(&rt->gpr[ICE_GPR_HV_IDX], &rt->pkt_buf[ho], ICE_GPR_HV_SIZE);
}

static void ice_rt_np_set(struct ice_parser_rt *rt, u16 pc)
{
        rt->gpr[ICE_GPR_NP_IDX] = pc;
}

static void ice_rt_nn_set(struct ice_parser_rt *rt, u16 node)
{
        rt->gpr[ICE_GPR_NN_IDX] = node;
}

static void
ice_rt_flag_set(struct ice_parser_rt *rt, unsigned int idx, bool set)
{
        struct ice_hw *hw = rt->psr->hw;
        unsigned int word, id;

        word = idx / ICE_GPR_FLG_SIZE;
        id = idx % ICE_GPR_FLG_SIZE;

        if (set) {
                rt->gpr[ICE_GPR_FLG_IDX + word] |= (u16)BIT(id);
                ice_debug(hw, ICE_DBG_PARSER, "Set parser flag %u\n", idx);
        } else {
                rt->gpr[ICE_GPR_FLG_IDX + word] &= ~(u16)BIT(id);
                ice_debug(hw, ICE_DBG_PARSER, "Clear parser flag %u\n", idx);
        }
}

static void ice_rt_gpr_set(struct ice_parser_rt *rt, int idx, u16 val)
{
        struct ice_hw *hw = rt->psr->hw;

        if (idx == ICE_GPR_HO_IDX)
                ice_rt_ho_set(rt, val);
        else
                rt->gpr[idx] = val;

        ice_debug(hw, ICE_DBG_PARSER, "Set GPR %d value %d\n", idx, val);
}

static void ice_rt_err_set(struct ice_parser_rt *rt, unsigned int idx, bool set)
{
        struct ice_hw *hw = rt->psr->hw;

        if (set) {
                rt->gpr[ICE_GPR_ERR_IDX] |= (u16)BIT(idx);
                ice_debug(hw, ICE_DBG_PARSER, "Set parser error %u\n", idx);
        } else {
                rt->gpr[ICE_GPR_ERR_IDX] &= ~(u16)BIT(idx);
                ice_debug(hw, ICE_DBG_PARSER, "Reset parser error %u\n", idx);
        }
}

/**
 * ice_parser_rt_reset - reset the parser runtime
 * @rt: pointer to the parser runtime
 */
void ice_parser_rt_reset(struct ice_parser_rt *rt)
{
        struct ice_parser *psr = rt->psr;
        struct ice_metainit_item *mi;
        unsigned int i;

        mi = &psr->mi_table[0];

        memset(rt, 0, sizeof(*rt));
        rt->psr = psr;

        ice_rt_tsr_set(rt, mi->tsr);
        ice_rt_ho_set(rt, mi->ho);
        ice_rt_np_set(rt, mi->pc);
        ice_rt_nn_set(rt, mi->pg_rn);

        for (i = 0; i < ICE_PARSER_FLG_NUM; i++) {
                if (mi->flags & BIT(i))
                        ice_rt_flag_set(rt, i, true);
        }
}

/**
 * ice_parser_rt_pktbuf_set - set a packet into parser runtime
 * @rt: pointer to the parser runtime
 * @pkt_buf: buffer with packet data
 * @pkt_len: packet buffer length
 */
void ice_parser_rt_pktbuf_set(struct ice_parser_rt *rt, const u8 *pkt_buf,
                              int pkt_len)
{
        int len = min(ICE_PARSER_MAX_PKT_LEN, pkt_len);
        u16 ho = rt->gpr[ICE_GPR_HO_IDX];

        memcpy(rt->pkt_buf, pkt_buf, len);
        rt->pkt_len = pkt_len;

        memcpy(&rt->gpr[ICE_GPR_HV_IDX], &rt->pkt_buf[ho], ICE_GPR_HV_SIZE);
}

static void ice_bst_key_init(struct ice_parser_rt *rt,
                             struct ice_imem_item *imem)
{
        u8 tsr = (u8)rt->gpr[ICE_GPR_TSR_IDX];
        u16 ho = rt->gpr[ICE_GPR_HO_IDX];
        u8 *key = rt->bst_key;
        int idd, i;

        idd = ICE_BST_TCAM_KEY_SIZE - 1;
        if (imem->b_kb.tsr_ctrl)
                key[idd] = tsr;
        else
                key[idd] = imem->b_kb.prio;

        idd = ICE_BST_TCAM_KEY_SIZE - 2;
        for (i = idd; i >= 0; i--) {
                int j;

                j = ho + idd - i;
                if (j < ICE_PARSER_MAX_PKT_LEN)
                        key[i] = rt->pkt_buf[j];
                else
                        key[i] = 0;
        }

        ice_debug_array_w_prefix(rt->psr->hw, ICE_DBG_PARSER,
                                 KBUILD_MODNAME ": Generated Boost TCAM Key",
                                 key, ICE_BST_TCAM_KEY_SIZE);
}

static u16 ice_bit_rev_u16(u16 v, int len)
{
        return bitrev16(v) >> (BITS_PER_TYPE(v) - len);
}

static u32 ice_bit_rev_u32(u32 v, int len)
{
        return bitrev32(v) >> (BITS_PER_TYPE(v) - len);
}

static u32 ice_hv_bit_sel(struct ice_parser_rt *rt, int start, int len)
{
        int offset;
        u32 buf[2];
        u64 val;

        offset = ICE_GPR_HV_IDX + (start / BITS_PER_TYPE(u16));

        memcpy(buf, &rt->gpr[offset], sizeof(buf));

        buf[0] = bitrev8x4(buf[0]);
        buf[1] = bitrev8x4(buf[1]);

        val = *(u64 *)buf;
        val >>= start % BITS_PER_TYPE(u16);

        return ice_bit_rev_u32(val, len);
}

static u32 ice_pk_build(struct ice_parser_rt *rt,
                        struct ice_np_keybuilder *kb)
{
        if (kb->opc == ICE_NPKB_OPC_EXTRACT)
                return ice_hv_bit_sel(rt, kb->start_reg0, kb->len_reg1);
        else if (kb->opc == ICE_NPKB_OPC_BUILD)
                return rt->gpr[kb->start_reg0] |
                       ((u32)rt->gpr[kb->len_reg1] << BITS_PER_TYPE(u16));
        else if (kb->opc == ICE_NPKB_OPC_BYPASS)
                return 0;

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Unsupported OP Code %u\n",
                  kb->opc);
        return U32_MAX;
}

static bool ice_flag_get(struct ice_parser_rt *rt, unsigned int index)
{
        int word = index / ICE_GPR_FLG_SIZE;
        int id = index % ICE_GPR_FLG_SIZE;

        return !!(rt->gpr[ICE_GPR_FLG_IDX + word] & (u16)BIT(id));
}

static int ice_imem_pgk_init(struct ice_parser_rt *rt,
                             struct ice_imem_item *imem)
{
        memset(&rt->pg_key, 0, sizeof(rt->pg_key));
        rt->pg_key.next_proto = ice_pk_build(rt, &imem->np_kb);
        if (rt->pg_key.next_proto == U32_MAX)
                return -EINVAL;

        if (imem->pg_kb.flag0_ena)
                rt->pg_key.flag0 = ice_flag_get(rt, imem->pg_kb.flag0_idx);
        if (imem->pg_kb.flag1_ena)
                rt->pg_key.flag1 = ice_flag_get(rt, imem->pg_kb.flag1_idx);
        if (imem->pg_kb.flag2_ena)
                rt->pg_key.flag2 = ice_flag_get(rt, imem->pg_kb.flag2_idx);
        if (imem->pg_kb.flag3_ena)
                rt->pg_key.flag3 = ice_flag_get(rt, imem->pg_kb.flag3_idx);

        rt->pg_key.alu_reg = rt->gpr[imem->pg_kb.alu_reg_idx];
        rt->pg_key.node_id = rt->gpr[ICE_GPR_NN_IDX];

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Generate Parse Graph Key: node_id(%d), flag0-3(%d,%d,%d,%d), boost_idx(%d), alu_reg(0x%04x), next_proto(0x%08x)\n",
                  rt->pg_key.node_id,
                  rt->pg_key.flag0,
                  rt->pg_key.flag1,
                  rt->pg_key.flag2,
                  rt->pg_key.flag3,
                  rt->pg_key.boost_idx,
                  rt->pg_key.alu_reg,
                  rt->pg_key.next_proto);

        return 0;
}

static void ice_imem_alu0_set(struct ice_parser_rt *rt,
                              struct ice_imem_item *imem)
{
        rt->alu0 = &imem->alu0;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU0 from imem pc %d\n",
                  imem->idx);
}

static void ice_imem_alu1_set(struct ice_parser_rt *rt,
                              struct ice_imem_item *imem)
{
        rt->alu1 = &imem->alu1;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU1 from imem pc %d\n",
                  imem->idx);
}

static void ice_imem_alu2_set(struct ice_parser_rt *rt,
                              struct ice_imem_item *imem)
{
        rt->alu2 = &imem->alu2;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU2 from imem pc %d\n",
                  imem->idx);
}

static void ice_imem_pgp_set(struct ice_parser_rt *rt,
                             struct ice_imem_item *imem)
{
        rt->pg_prio = imem->pg_prio;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load PG priority %d from imem pc %d\n",
                  rt->pg_prio, imem->idx);
}

static int ice_bst_pgk_init(struct ice_parser_rt *rt,
                            struct ice_bst_tcam_item *bst)
{
        memset(&rt->pg_key, 0, sizeof(rt->pg_key));
        rt->pg_key.boost_idx = bst->hit_idx_grp;
        rt->pg_key.next_proto = ice_pk_build(rt, &bst->np_kb);
        if (rt->pg_key.next_proto == U32_MAX)
                return -EINVAL;

        if (bst->pg_kb.flag0_ena)
                rt->pg_key.flag0 = ice_flag_get(rt, bst->pg_kb.flag0_idx);
        if (bst->pg_kb.flag1_ena)
                rt->pg_key.flag1 = ice_flag_get(rt, bst->pg_kb.flag1_idx);
        if (bst->pg_kb.flag2_ena)
                rt->pg_key.flag2 = ice_flag_get(rt, bst->pg_kb.flag2_idx);
        if (bst->pg_kb.flag3_ena)
                rt->pg_key.flag3 = ice_flag_get(rt, bst->pg_kb.flag3_idx);

        rt->pg_key.alu_reg = rt->gpr[bst->pg_kb.alu_reg_idx];
        rt->pg_key.node_id = rt->gpr[ICE_GPR_NN_IDX];

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Generate Parse Graph Key: node_id(%d), flag0-3(%d,%d,%d,%d), boost_idx(%d), alu_reg(0x%04x), next_proto(0x%08x)\n",
                  rt->pg_key.node_id,
                  rt->pg_key.flag0,
                  rt->pg_key.flag1,
                  rt->pg_key.flag2,
                  rt->pg_key.flag3,
                  rt->pg_key.boost_idx,
                  rt->pg_key.alu_reg,
                  rt->pg_key.next_proto);

        return 0;
}

static void ice_bst_alu0_set(struct ice_parser_rt *rt,
                             struct ice_bst_tcam_item *bst)
{
        rt->alu0 = &bst->alu0;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU0 from boost address %d\n",
                  bst->addr);
}

static void ice_bst_alu1_set(struct ice_parser_rt *rt,
                             struct ice_bst_tcam_item *bst)
{
        rt->alu1 = &bst->alu1;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU1 from boost address %d\n",
                  bst->addr);
}

static void ice_bst_alu2_set(struct ice_parser_rt *rt,
                             struct ice_bst_tcam_item *bst)
{
        rt->alu2 = &bst->alu2;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load ALU2 from boost address %d\n",
                  bst->addr);
}

static void ice_bst_pgp_set(struct ice_parser_rt *rt,
                            struct ice_bst_tcam_item *bst)
{
        rt->pg_prio = bst->pg_prio;
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load PG priority %d from boost address %d\n",
                  rt->pg_prio, bst->addr);
}

static struct ice_pg_cam_item *ice_rt_pg_cam_match(struct ice_parser_rt *rt)
{
        struct ice_parser *psr = rt->psr;
        struct ice_pg_cam_item *item;

        item = ice_pg_cam_match(psr->pg_cam_table, ICE_PG_CAM_TABLE_SIZE,
                                &rt->pg_key);
        if (!item)
                item = ice_pg_cam_match(psr->pg_sp_cam_table,
                                        ICE_PG_SP_CAM_TABLE_SIZE, &rt->pg_key);
        return item;
}

static
struct ice_pg_nm_cam_item *ice_rt_pg_nm_cam_match(struct ice_parser_rt *rt)
{
        struct ice_parser *psr = rt->psr;
        struct ice_pg_nm_cam_item *item;

        item = ice_pg_nm_cam_match(psr->pg_nm_cam_table,
                                   ICE_PG_NM_CAM_TABLE_SIZE, &rt->pg_key);

        if (!item)
                item = ice_pg_nm_cam_match(psr->pg_nm_sp_cam_table,
                                           ICE_PG_NM_SP_CAM_TABLE_SIZE,
                                           &rt->pg_key);
        return item;
}

static void ice_gpr_add(struct ice_parser_rt *rt, int idx, u16 val)
{
        rt->pu.gpr_val_upd[idx] = true;
        rt->pu.gpr_val[idx] = val;

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Pending update for register %d value %d\n",
                  idx, val);
}

static void ice_pg_exe(struct ice_parser_rt *rt)
{
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ParseGraph action ...\n");

        ice_gpr_add(rt, ICE_GPR_NP_IDX, rt->action->next_pc);
        ice_gpr_add(rt, ICE_GPR_NN_IDX, rt->action->next_node);

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ParseGraph action done.\n");
}

static void ice_flg_add(struct ice_parser_rt *rt, int idx, bool val)
{
        rt->pu.flg_msk |= BIT_ULL(idx);
        if (val)
                rt->pu.flg_val |= BIT_ULL(idx);
        else
                rt->pu.flg_val &= ~BIT_ULL(idx);

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Pending update for flag %d value %d\n",
                  idx, val);
}

static void ice_flg_update(struct ice_parser_rt *rt, struct ice_alu *alu)
{
        u32 hv_bit_sel;
        int i;

        if (!alu->dedicate_flags_ena)
                return;

        if (alu->flags_extr_imm) {
                for (i = 0; i < alu->dst_len; i++)
                        ice_flg_add(rt, alu->dst_start + i,
                                    !!(alu->flags_start_imm & BIT(i)));
        } else {
                for (i = 0; i < alu->dst_len; i++) {
                        hv_bit_sel = ice_hv_bit_sel(rt,
                                                    alu->flags_start_imm + i,
                                                    1);
                        ice_flg_add(rt, alu->dst_start + i, !!hv_bit_sel);
                }
        }
}

static void ice_po_update(struct ice_parser_rt *rt, struct ice_alu *alu)
{
        if (alu->proto_offset_opc == ICE_PO_OFF_HDR_ADD)
                rt->po = (u16)(rt->gpr[ICE_GPR_HO_IDX] + alu->proto_offset);
        else if (alu->proto_offset_opc == ICE_PO_OFF_HDR_SUB)
                rt->po = (u16)(rt->gpr[ICE_GPR_HO_IDX] - alu->proto_offset);
        else if (alu->proto_offset_opc == ICE_PO_OFF_REMAIN)
                rt->po = rt->gpr[ICE_GPR_HO_IDX];

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Update Protocol Offset = %d\n",
                  rt->po);
}

static u16 ice_reg_bit_sel(struct ice_parser_rt *rt, int reg_idx,
                           int start, int len)
{
        int offset;
        u32 val;

        offset = ICE_GPR_HV_IDX + (start / BITS_PER_TYPE(u16));

        memcpy(&val, &rt->gpr[offset], sizeof(val));

        val = bitrev8x4(val);
        val >>= start % BITS_PER_TYPE(u16);

        return ice_bit_rev_u16(val, len);
}

static void ice_err_add(struct ice_parser_rt *rt, int idx, bool val)
{
        rt->pu.err_msk |= (u16)BIT(idx);
        if (val)
                rt->pu.flg_val |= (u64)BIT_ULL(idx);
        else
                rt->pu.flg_val &= ~(u64)BIT_ULL(idx);

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Pending update for error %d value %d\n",
                  idx, val);
}

static void ice_dst_reg_bit_set(struct ice_parser_rt *rt, struct ice_alu *alu,
                                bool val)
{
        u16 flg_idx;

        if (alu->dedicate_flags_ena) {
                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "DedicatedFlagsEnable should not be enabled in opcode %d\n",
                          alu->opc);
                return;
        }

        if (alu->dst_reg_id == ICE_GPR_ERR_IDX) {
                if (alu->dst_start >= ICE_PARSER_ERR_NUM) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Invalid error %d\n",
                                  alu->dst_start);
                        return;
                }
                ice_err_add(rt, alu->dst_start, val);
        } else if (alu->dst_reg_id >= ICE_GPR_FLG_IDX) {
                flg_idx = (u16)(((alu->dst_reg_id - ICE_GPR_FLG_IDX) << 4) +
                                alu->dst_start);

                if (flg_idx >= ICE_PARSER_FLG_NUM) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Invalid flag %d\n",
                                  flg_idx);
                        return;
                }
                ice_flg_add(rt, flg_idx, val);
        } else {
                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Unexpected Dest Register Bit set, RegisterID %d Start %d\n",
                          alu->dst_reg_id, alu->dst_start);
        }
}

static void ice_alu_exe(struct ice_parser_rt *rt, struct ice_alu *alu)
{
        u16 dst, src, shift, imm;

        if (alu->shift_xlate_sel) {
                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "shift_xlate_sel != 0 is not expected\n");
                return;
        }

        ice_po_update(rt, alu);
        ice_flg_update(rt, alu);

        dst = rt->gpr[alu->dst_reg_id];
        src = ice_reg_bit_sel(rt, alu->src_reg_id,
                              alu->src_start, alu->src_len);
        shift = alu->shift_xlate_key;
        imm = alu->imm;

        switch (alu->opc) {
        case ICE_ALU_PARK:
                break;
        case ICE_ALU_MOV_ADD:
                dst = (src << shift) + imm;
                ice_gpr_add(rt, alu->dst_reg_id, dst);
                break;
        case ICE_ALU_ADD:
                dst += (src << shift) + imm;
                ice_gpr_add(rt, alu->dst_reg_id, dst);
                break;
        case ICE_ALU_ORLT:
                if (src < imm)
                        ice_dst_reg_bit_set(rt, alu, true);
                ice_gpr_add(rt, ICE_GPR_NP_IDX, alu->branch_addr);
                break;
        case ICE_ALU_OREQ:
                if (src == imm)
                        ice_dst_reg_bit_set(rt, alu, true);
                ice_gpr_add(rt, ICE_GPR_NP_IDX, alu->branch_addr);
                break;
        case ICE_ALU_SETEQ:
                ice_dst_reg_bit_set(rt, alu, src == imm);
                ice_gpr_add(rt, ICE_GPR_NP_IDX, alu->branch_addr);
                break;
        case ICE_ALU_MOV_XOR:
                dst = (src << shift) ^ imm;
                ice_gpr_add(rt, alu->dst_reg_id, dst);
                break;
        default:
                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Unsupported ALU instruction %d\n",
                          alu->opc);
                break;
        }
}

static void ice_alu0_exe(struct ice_parser_rt *rt)
{
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU0 ...\n");
        ice_alu_exe(rt, rt->alu0);
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU0 done.\n");
}

static void ice_alu1_exe(struct ice_parser_rt *rt)
{
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU1 ...\n");
        ice_alu_exe(rt, rt->alu1);
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU1 done.\n");
}

static void ice_alu2_exe(struct ice_parser_rt *rt)
{
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU2 ...\n");
        ice_alu_exe(rt, rt->alu2);
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Executing ALU2 done.\n");
}

static void ice_pu_exe(struct ice_parser_rt *rt)
{
        struct ice_gpr_pu *pu = &rt->pu;
        unsigned int i;

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Updating Registers ...\n");

        for (i = 0; i < ICE_PARSER_GPR_NUM; i++) {
                if (pu->gpr_val_upd[i])
                        ice_rt_gpr_set(rt, i, pu->gpr_val[i]);
        }

        for (i = 0; i < ICE_PARSER_FLG_NUM; i++) {
                if (pu->flg_msk & BIT(i))
                        ice_rt_flag_set(rt, i, pu->flg_val & BIT(i));
        }

        for (i = 0; i < ICE_PARSER_ERR_NUM; i++) {
                if (pu->err_msk & BIT(i))
                        ice_rt_err_set(rt, i, pu->err_val & BIT(i));
        }

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Updating Registers done.\n");
}

static void ice_alu_pg_exe(struct ice_parser_rt *rt)
{
        memset(&rt->pu, 0, sizeof(rt->pu));

        switch (rt->pg_prio) {
        case (ICE_PG_P0):
                ice_pg_exe(rt);
                ice_alu0_exe(rt);
                ice_alu1_exe(rt);
                ice_alu2_exe(rt);
                break;
        case (ICE_PG_P1):
                ice_alu0_exe(rt);
                ice_pg_exe(rt);
                ice_alu1_exe(rt);
                ice_alu2_exe(rt);
                break;
        case (ICE_PG_P2):
                ice_alu0_exe(rt);
                ice_alu1_exe(rt);
                ice_pg_exe(rt);
                ice_alu2_exe(rt);
                break;
        case (ICE_PG_P3):
                ice_alu0_exe(rt);
                ice_alu1_exe(rt);
                ice_alu2_exe(rt);
                ice_pg_exe(rt);
                break;
        }

        ice_pu_exe(rt);

        if (rt->action->ho_inc == 0)
                return;

        if (rt->action->ho_polarity)
                ice_rt_ho_set(rt, rt->gpr[ICE_GPR_HO_IDX] + rt->action->ho_inc);
        else
                ice_rt_ho_set(rt, rt->gpr[ICE_GPR_HO_IDX] - rt->action->ho_inc);
}

static void ice_proto_off_update(struct ice_parser_rt *rt)
{
        struct ice_parser *psr = rt->psr;

        if (rt->action->is_pg) {
                struct ice_proto_grp_item *proto_grp =
                        &psr->proto_grp_table[rt->action->proto_id];
                u16 po;
                int i;

                for (i = 0; i < ICE_PROTO_COUNT_PER_GRP; i++) {
                        struct ice_proto_off *entry = &proto_grp->po[i];

                        if (entry->proto_id == U8_MAX)
                                break;

                        if (!entry->polarity)
                                po = rt->po + entry->offset;
                        else
                                po = rt->po - entry->offset;

                        rt->protocols[entry->proto_id] = true;
                        rt->offsets[entry->proto_id] = po;

                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Set Protocol %d at offset %d\n",
                                  entry->proto_id, po);
                }
        } else {
                rt->protocols[rt->action->proto_id] = true;
                rt->offsets[rt->action->proto_id] = rt->po;

                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Set Protocol %d at offset %d\n",
                          rt->action->proto_id, rt->po);
        }
}

static void ice_marker_set(struct ice_parser_rt *rt, int idx)
{
        unsigned int byte = idx / BITS_PER_BYTE;
        unsigned int bit = idx % BITS_PER_BYTE;

        rt->markers[byte] |= (u8)BIT(bit);
}

static void ice_marker_update(struct ice_parser_rt *rt)
{
        struct ice_parser *psr = rt->psr;

        if (rt->action->is_mg) {
                struct ice_mk_grp_item *mk_grp =
                        &psr->mk_grp_table[rt->action->marker_id];
                int i;

                for (i = 0; i < ICE_MARKER_ID_NUM; i++) {
                        u8 marker = mk_grp->markers[i];

                        if (marker == ICE_MARKER_MAX_SIZE)
                                break;

                        ice_marker_set(rt, marker);
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Set Marker %d\n",
                                  marker);
                }
        } else {
                if (rt->action->marker_id != ICE_MARKER_MAX_SIZE)
                        ice_marker_set(rt, rt->action->marker_id);

                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Set Marker %d\n",
                          rt->action->marker_id);
        }
}

static u16 ice_ptype_resolve(struct ice_parser_rt *rt)
{
        struct ice_ptype_mk_tcam_item *item;
        struct ice_parser *psr = rt->psr;

        item = ice_ptype_mk_tcam_match(psr->ptype_mk_tcam_table,
                                       rt->markers, ICE_MARKER_ID_SIZE);
        if (item)
                return item->ptype;

        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Could not resolve PTYPE\n");
        return U16_MAX;
}

static void ice_proto_off_resolve(struct ice_parser_rt *rt,
                                  struct ice_parser_result *rslt)
{
        int i;

        for (i = 0; i < ICE_PO_PAIR_SIZE - 1; i++) {
                if (rt->protocols[i]) {
                        rslt->po[rslt->po_num].proto_id = (u8)i;
                        rslt->po[rslt->po_num].offset = rt->offsets[i];
                        rslt->po_num++;
                }
        }
}

static void ice_result_resolve(struct ice_parser_rt *rt,
                               struct ice_parser_result *rslt)
{
        struct ice_parser *psr = rt->psr;

        memset(rslt, 0, sizeof(*rslt));

        memcpy(&rslt->flags_psr, &rt->gpr[ICE_GPR_FLG_IDX],
               ICE_PARSER_FLAG_PSR_SIZE);
        rslt->flags_pkt = ice_flg_redirect(psr->flg_rd_table, rslt->flags_psr);
        rslt->flags_sw = ice_xlt_kb_flag_get(psr->xlt_kb_sw, rslt->flags_pkt);
        rslt->flags_fd = ice_xlt_kb_flag_get(psr->xlt_kb_fd, rslt->flags_pkt);
        rslt->flags_rss = ice_xlt_kb_flag_get(psr->xlt_kb_rss, rslt->flags_pkt);

        ice_proto_off_resolve(rt, rslt);
        rslt->ptype = ice_ptype_resolve(rt);
}

/**
 * ice_parser_rt_execute - parser execution routine
 * @rt: pointer to the parser runtime
 * @rslt: input/output parameter to save parser result
 *
 * Return: 0 on success or errno.
 */
int ice_parser_rt_execute(struct ice_parser_rt *rt,
                          struct ice_parser_result *rslt)
{
        struct ice_pg_nm_cam_item *pg_nm_cam;
        struct ice_parser *psr = rt->psr;
        struct ice_pg_cam_item *pg_cam;
        int status = 0;
        u16 node;
        u16 pc;

        node = rt->gpr[ICE_GPR_NN_IDX];
        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Start with Node: %u\n", node);

        while (true) {
                struct ice_bst_tcam_item *bst;
                struct ice_imem_item *imem;

                pc = rt->gpr[ICE_GPR_NP_IDX];
                imem = &psr->imem_table[pc];
                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Load imem at pc: %u\n",
                          pc);

                ice_bst_key_init(rt, imem);
                bst = ice_bst_tcam_match(psr->bst_tcam_table, rt->bst_key);
                if (!bst) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "No Boost TCAM Match\n");
                        status = ice_imem_pgk_init(rt, imem);
                        if (status)
                                break;
                        ice_imem_alu0_set(rt, imem);
                        ice_imem_alu1_set(rt, imem);
                        ice_imem_alu2_set(rt, imem);
                        ice_imem_pgp_set(rt, imem);
                } else {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Boost TCAM Match address: %u\n",
                                  bst->addr);
                        if (imem->b_m.pg) {
                                status = ice_bst_pgk_init(rt, bst);
                                if (status)
                                        break;
                                ice_bst_pgp_set(rt, bst);
                        } else {
                                status = ice_imem_pgk_init(rt, imem);
                                if (status)
                                        break;
                                ice_imem_pgp_set(rt, imem);
                        }

                        if (imem->b_m.alu0)
                                ice_bst_alu0_set(rt, bst);
                        else
                                ice_imem_alu0_set(rt, imem);

                        if (imem->b_m.alu1)
                                ice_bst_alu1_set(rt, bst);
                        else
                                ice_imem_alu1_set(rt, imem);

                        if (imem->b_m.alu2)
                                ice_bst_alu2_set(rt, bst);
                        else
                                ice_imem_alu2_set(rt, imem);
                }

                rt->action = NULL;
                pg_cam = ice_rt_pg_cam_match(rt);
                if (!pg_cam) {
                        pg_nm_cam = ice_rt_pg_nm_cam_match(rt);
                        if (pg_nm_cam) {
                                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Match ParseGraph Nomatch CAM Address %u\n",
                                          pg_nm_cam->idx);
                                rt->action = &pg_nm_cam->action;
                        }
                } else {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Match ParseGraph CAM Address %u\n",
                                  pg_cam->idx);
                        rt->action = &pg_cam->action;
                }

                if (!rt->action) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Failed to match ParseGraph CAM, stop parsing.\n");
                        status = -EINVAL;
                        break;
                }

                ice_alu_pg_exe(rt);
                ice_marker_update(rt);
                ice_proto_off_update(rt);

                ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Go to node %u\n",
                          rt->action->next_node);

                if (rt->action->is_last_round) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Last Round in ParseGraph Action, stop parsing.\n");
                        break;
                }

                if (rt->gpr[ICE_GPR_HO_IDX] >= rt->pkt_len) {
                        ice_debug(rt->psr->hw, ICE_DBG_PARSER, "Header Offset (%u) is larger than packet len (%u), stop parsing\n",
                                  rt->gpr[ICE_GPR_HO_IDX], rt->pkt_len);
                        break;
                }
        }

        ice_result_resolve(rt, rslt);

        return status;
}