17100b0e5SJeffrey Huang /* SPDX-License-Identifier: BSD-3-Clause
27100b0e5SJeffrey Huang * Copyright(c) 2019-2021 Broadcom
37100b0e5SJeffrey Huang * All rights reserved.
47100b0e5SJeffrey Huang */
508e1af1aSFarah Smith
67100b0e5SJeffrey Huang #include <stdint.h>
77100b0e5SJeffrey Huang #include <stdlib.h>
87100b0e5SJeffrey Huang #include <stdbool.h>
97100b0e5SJeffrey Huang #include <string.h>
107100b0e5SJeffrey Huang #include "lookup3.h"
117100b0e5SJeffrey Huang #include "rand.h"
127100b0e5SJeffrey Huang
1308e1af1aSFarah Smith #include "hcapi_cfa.h"
147100b0e5SJeffrey Huang #include "hcapi_cfa_defs.h"
157100b0e5SJeffrey Huang
16b56a8975SPeter Spreadborough static uint32_t hcapi_cfa_lkup_lkup3_init_cfg;
17b56a8975SPeter Spreadborough static uint32_t hcapi_cfa_lkup_em_seed_mem[HCAPI_CFA_LKUP_SEED_MEM_SIZE];
18b56a8975SPeter Spreadborough static bool hcapi_cfa_lkup_init;
197100b0e5SJeffrey Huang
SWAP_WORDS32(uint32_t val32)207100b0e5SJeffrey Huang static inline uint32_t SWAP_WORDS32(uint32_t val32)
217100b0e5SJeffrey Huang {
2208e1af1aSFarah Smith return (((val32 & 0x0000ffff) << 16) | ((val32 & 0xffff0000) >> 16));
237100b0e5SJeffrey Huang }
247100b0e5SJeffrey Huang
hcapi_cfa_seeds_init(void)257100b0e5SJeffrey Huang static void hcapi_cfa_seeds_init(void)
267100b0e5SJeffrey Huang {
277100b0e5SJeffrey Huang int i;
287100b0e5SJeffrey Huang uint32_t r;
297100b0e5SJeffrey Huang
307100b0e5SJeffrey Huang if (hcapi_cfa_lkup_init)
317100b0e5SJeffrey Huang return;
327100b0e5SJeffrey Huang
337100b0e5SJeffrey Huang hcapi_cfa_lkup_init = true;
347100b0e5SJeffrey Huang
357100b0e5SJeffrey Huang /* Initialize the lfsr */
367100b0e5SJeffrey Huang rand_init();
377100b0e5SJeffrey Huang
387100b0e5SJeffrey Huang /* RX and TX use the same seed values */
397100b0e5SJeffrey Huang hcapi_cfa_lkup_lkup3_init_cfg = SWAP_WORDS32(rand32());
407100b0e5SJeffrey Huang
417100b0e5SJeffrey Huang for (i = 0; i < HCAPI_CFA_LKUP_SEED_MEM_SIZE / 2; i++) {
427100b0e5SJeffrey Huang r = SWAP_WORDS32(rand32());
437100b0e5SJeffrey Huang hcapi_cfa_lkup_em_seed_mem[i * 2] = r;
447100b0e5SJeffrey Huang r = SWAP_WORDS32(rand32());
457100b0e5SJeffrey Huang hcapi_cfa_lkup_em_seed_mem[i * 2 + 1] = (r & 0x1);
467100b0e5SJeffrey Huang }
477100b0e5SJeffrey Huang }
487100b0e5SJeffrey Huang
hcapi_cfa_crc32_hash(uint8_t * key)497100b0e5SJeffrey Huang static uint32_t hcapi_cfa_crc32_hash(uint8_t *key)
507100b0e5SJeffrey Huang {
517100b0e5SJeffrey Huang int i;
527100b0e5SJeffrey Huang uint32_t index;
537100b0e5SJeffrey Huang uint32_t val1, val2;
547100b0e5SJeffrey Huang uint8_t temp[4];
557100b0e5SJeffrey Huang uint8_t *kptr = key;
567100b0e5SJeffrey Huang
577100b0e5SJeffrey Huang /* Do byte-wise XOR of the 52-byte HASH key first. */
587100b0e5SJeffrey Huang index = *key;
597100b0e5SJeffrey Huang kptr--;
607100b0e5SJeffrey Huang
617100b0e5SJeffrey Huang for (i = CFA_P4_EEM_KEY_MAX_SIZE - 2; i >= 0; i--) {
627100b0e5SJeffrey Huang index = index ^ *kptr;
637100b0e5SJeffrey Huang kptr--;
647100b0e5SJeffrey Huang }
657100b0e5SJeffrey Huang
667100b0e5SJeffrey Huang /* Get seeds */
677100b0e5SJeffrey Huang val1 = hcapi_cfa_lkup_em_seed_mem[index * 2];
687100b0e5SJeffrey Huang val2 = hcapi_cfa_lkup_em_seed_mem[index * 2 + 1];
697100b0e5SJeffrey Huang
707100b0e5SJeffrey Huang temp[3] = (uint8_t)(val1 >> 24);
717100b0e5SJeffrey Huang temp[2] = (uint8_t)(val1 >> 16);
727100b0e5SJeffrey Huang temp[1] = (uint8_t)(val1 >> 8);
737100b0e5SJeffrey Huang temp[0] = (uint8_t)(val1 & 0xff);
747100b0e5SJeffrey Huang val1 = 0;
757100b0e5SJeffrey Huang
767100b0e5SJeffrey Huang /* Start with seed */
777100b0e5SJeffrey Huang if (!(val2 & 0x1))
787100b0e5SJeffrey Huang val1 = hcapi_cfa_crc32i(~val1, temp, 4);
797100b0e5SJeffrey Huang
8008e1af1aSFarah Smith val1 = hcapi_cfa_crc32i(~val1, (key - (CFA_P4_EEM_KEY_MAX_SIZE - 1)),
817100b0e5SJeffrey Huang CFA_P4_EEM_KEY_MAX_SIZE);
827100b0e5SJeffrey Huang
837100b0e5SJeffrey Huang /* End with seed */
847100b0e5SJeffrey Huang if (val2 & 0x1)
857100b0e5SJeffrey Huang val1 = hcapi_cfa_crc32i(~val1, temp, 4);
867100b0e5SJeffrey Huang
877100b0e5SJeffrey Huang return val1;
887100b0e5SJeffrey Huang }
897100b0e5SJeffrey Huang
hcapi_cfa_lookup3_hash(uint8_t * in_key)907100b0e5SJeffrey Huang static uint32_t hcapi_cfa_lookup3_hash(uint8_t *in_key)
917100b0e5SJeffrey Huang {
927100b0e5SJeffrey Huang uint32_t val1;
937100b0e5SJeffrey Huang
9408e1af1aSFarah Smith val1 = hashword(((uint32_t *)in_key) + 1,
957100b0e5SJeffrey Huang CFA_P4_EEM_KEY_MAX_SIZE / (sizeof(uint32_t)),
967100b0e5SJeffrey Huang hcapi_cfa_lkup_lkup3_init_cfg);
977100b0e5SJeffrey Huang
987100b0e5SJeffrey Huang return val1;
997100b0e5SJeffrey Huang }
1007100b0e5SJeffrey Huang
hcapi_get_table_page(struct hcapi_cfa_em_table * mem,uint32_t page)10108e1af1aSFarah Smith uint64_t hcapi_get_table_page(struct hcapi_cfa_em_table *mem, uint32_t page)
1027100b0e5SJeffrey Huang {
1037100b0e5SJeffrey Huang int level = 0;
1047100b0e5SJeffrey Huang uint64_t addr;
1057100b0e5SJeffrey Huang
1067100b0e5SJeffrey Huang if (mem == NULL)
1077100b0e5SJeffrey Huang return 0;
1087100b0e5SJeffrey Huang
1097100b0e5SJeffrey Huang /*
1107100b0e5SJeffrey Huang * Use the level according to the num_level of page table
1117100b0e5SJeffrey Huang */
1127100b0e5SJeffrey Huang level = mem->num_lvl - 1;
1137100b0e5SJeffrey Huang
11408e1af1aSFarah Smith addr = (uint64_t)mem->pg_tbl[level].pg_va_tbl[page];
1157100b0e5SJeffrey Huang
1167100b0e5SJeffrey Huang return addr;
1177100b0e5SJeffrey Huang }
1187100b0e5SJeffrey Huang
1197100b0e5SJeffrey Huang /** Approximation of HCAPI hcapi_cfa_key_hash()
1207100b0e5SJeffrey Huang *
1217100b0e5SJeffrey Huang * Return:
1227100b0e5SJeffrey Huang *
1237100b0e5SJeffrey Huang */
hcapi_cfa_p4_key_hash(uint64_t * key_data,uint16_t bitlen)124b56a8975SPeter Spreadborough uint64_t hcapi_cfa_p4_key_hash(uint64_t *key_data,
1257100b0e5SJeffrey Huang uint16_t bitlen)
1267100b0e5SJeffrey Huang {
1277100b0e5SJeffrey Huang uint32_t key0_hash;
1287100b0e5SJeffrey Huang uint32_t key1_hash;
1297100b0e5SJeffrey Huang
1307100b0e5SJeffrey Huang /*
1317100b0e5SJeffrey Huang * Init the seeds if needed
1327100b0e5SJeffrey Huang */
1337100b0e5SJeffrey Huang if (!hcapi_cfa_lkup_init)
1347100b0e5SJeffrey Huang hcapi_cfa_seeds_init();
1357100b0e5SJeffrey Huang
13608e1af1aSFarah Smith key0_hash =
13708e1af1aSFarah Smith hcapi_cfa_crc32_hash(((uint8_t *)key_data) + (bitlen / 8) - 1);
1387100b0e5SJeffrey Huang
1397100b0e5SJeffrey Huang key1_hash = hcapi_cfa_lookup3_hash((uint8_t *)key_data);
1407100b0e5SJeffrey Huang
1417100b0e5SJeffrey Huang return ((uint64_t)key0_hash) << 32 | (uint64_t)key1_hash;
1427100b0e5SJeffrey Huang }
1437100b0e5SJeffrey Huang
hcapi_cfa_p4_key_hw_op_put(struct hcapi_cfa_hwop * op,struct hcapi_cfa_key_data * key_obj)14408e1af1aSFarah Smith static int hcapi_cfa_p4_key_hw_op_put(struct hcapi_cfa_hwop *op,
1457100b0e5SJeffrey Huang struct hcapi_cfa_key_data *key_obj)
1467100b0e5SJeffrey Huang {
1477100b0e5SJeffrey Huang int rc = 0;
1487100b0e5SJeffrey Huang
14908e1af1aSFarah Smith memcpy((uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset,
15008e1af1aSFarah Smith key_obj->data, key_obj->size);
1517100b0e5SJeffrey Huang
1527100b0e5SJeffrey Huang return rc;
1537100b0e5SJeffrey Huang }
1547100b0e5SJeffrey Huang
hcapi_cfa_p4_key_hw_op_get(struct hcapi_cfa_hwop * op,struct hcapi_cfa_key_data * key_obj)15508e1af1aSFarah Smith static int hcapi_cfa_p4_key_hw_op_get(struct hcapi_cfa_hwop *op,
1567100b0e5SJeffrey Huang struct hcapi_cfa_key_data *key_obj)
1577100b0e5SJeffrey Huang {
1587100b0e5SJeffrey Huang int rc = 0;
1597100b0e5SJeffrey Huang
1607100b0e5SJeffrey Huang memcpy(key_obj->data,
16108e1af1aSFarah Smith (uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset,
1627100b0e5SJeffrey Huang key_obj->size);
1637100b0e5SJeffrey Huang
1647100b0e5SJeffrey Huang return rc;
1657100b0e5SJeffrey Huang }
1667100b0e5SJeffrey Huang
hcapi_cfa_p4_key_hw_op_add(struct hcapi_cfa_hwop * op,struct hcapi_cfa_key_data * key_obj)16708e1af1aSFarah Smith static int hcapi_cfa_p4_key_hw_op_add(struct hcapi_cfa_hwop *op,
1687100b0e5SJeffrey Huang struct hcapi_cfa_key_data *key_obj)
1697100b0e5SJeffrey Huang {
1707100b0e5SJeffrey Huang int rc = 0;
1717100b0e5SJeffrey Huang struct cfa_p4_eem_64b_entry table_entry;
1727100b0e5SJeffrey Huang
1737100b0e5SJeffrey Huang /*
1747100b0e5SJeffrey Huang * Is entry free?
1757100b0e5SJeffrey Huang */
1767100b0e5SJeffrey Huang memcpy(&table_entry,
17708e1af1aSFarah Smith (uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset,
1787100b0e5SJeffrey Huang key_obj->size);
1797100b0e5SJeffrey Huang
1807100b0e5SJeffrey Huang /*
1817100b0e5SJeffrey Huang * If this is entry is valid then report failure
1827100b0e5SJeffrey Huang */
1837100b0e5SJeffrey Huang if (table_entry.hdr.word1 & (1 << CFA_P4_EEM_ENTRY_VALID_SHIFT))
1847100b0e5SJeffrey Huang return -1;
1857100b0e5SJeffrey Huang
18608e1af1aSFarah Smith memcpy((uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset,
18708e1af1aSFarah Smith key_obj->data, key_obj->size);
1887100b0e5SJeffrey Huang
1897100b0e5SJeffrey Huang return rc;
1907100b0e5SJeffrey Huang }
1917100b0e5SJeffrey Huang
hcapi_cfa_p4_key_hw_op_del(struct hcapi_cfa_hwop * op,struct hcapi_cfa_key_data * key_obj)19208e1af1aSFarah Smith static int hcapi_cfa_p4_key_hw_op_del(struct hcapi_cfa_hwop *op,
1937100b0e5SJeffrey Huang struct hcapi_cfa_key_data *key_obj)
1947100b0e5SJeffrey Huang {
1957100b0e5SJeffrey Huang int rc = 0;
1967100b0e5SJeffrey Huang struct cfa_p4_eem_64b_entry table_entry;
1977100b0e5SJeffrey Huang
1987100b0e5SJeffrey Huang /*
1997100b0e5SJeffrey Huang * Read entry
2007100b0e5SJeffrey Huang */
2017100b0e5SJeffrey Huang memcpy(&table_entry,
20208e1af1aSFarah Smith (uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset,
2037100b0e5SJeffrey Huang key_obj->size);
2047100b0e5SJeffrey Huang
2057100b0e5SJeffrey Huang /*
2067100b0e5SJeffrey Huang * If this is not a valid entry then report failure.
2077100b0e5SJeffrey Huang */
2087100b0e5SJeffrey Huang if (table_entry.hdr.word1 & (1 << CFA_P4_EEM_ENTRY_VALID_SHIFT)) {
2097100b0e5SJeffrey Huang /*
2107100b0e5SJeffrey Huang * If a key has been provided then verify the key matches
2117100b0e5SJeffrey Huang * before deleting the entry.
2127100b0e5SJeffrey Huang */
2137100b0e5SJeffrey Huang if (key_obj->data != NULL) {
21408e1af1aSFarah Smith if (memcmp(&table_entry, key_obj->data,
2157100b0e5SJeffrey Huang key_obj->size) != 0)
2167100b0e5SJeffrey Huang return -1;
2177100b0e5SJeffrey Huang }
2187100b0e5SJeffrey Huang } else {
2197100b0e5SJeffrey Huang return -1;
2207100b0e5SJeffrey Huang }
2217100b0e5SJeffrey Huang
2227100b0e5SJeffrey Huang /*
2237100b0e5SJeffrey Huang * Delete entry
2247100b0e5SJeffrey Huang */
22508e1af1aSFarah Smith memset((uint8_t *)(uintptr_t)op->hw.base_addr + key_obj->offset, 0, key_obj->size);
2267100b0e5SJeffrey Huang
2277100b0e5SJeffrey Huang return rc;
2287100b0e5SJeffrey Huang }
2297100b0e5SJeffrey Huang
230*1993b267SShahaji Bhosle /** Approximation of hcapi_cfa_key_hw_op()
2317100b0e5SJeffrey Huang *
2327100b0e5SJeffrey Huang *
2337100b0e5SJeffrey Huang */
hcapi_cfa_p4_key_hw_op(struct hcapi_cfa_hwop * op,struct hcapi_cfa_key_tbl * key_tbl,struct hcapi_cfa_key_data * key_obj,struct hcapi_cfa_key_loc * key_loc)23408e1af1aSFarah Smith static int hcapi_cfa_p4_key_hw_op(struct hcapi_cfa_hwop *op,
2357100b0e5SJeffrey Huang struct hcapi_cfa_key_tbl *key_tbl,
2367100b0e5SJeffrey Huang struct hcapi_cfa_key_data *key_obj,
2377100b0e5SJeffrey Huang struct hcapi_cfa_key_loc *key_loc)
2387100b0e5SJeffrey Huang {
2397100b0e5SJeffrey Huang int rc = 0;
24008e1af1aSFarah Smith struct hcapi_cfa_em_table *em_tbl;
24108e1af1aSFarah Smith uint32_t page;
2427100b0e5SJeffrey Huang
24308e1af1aSFarah Smith if (op == NULL || key_tbl == NULL || key_obj == NULL || key_loc == NULL)
2447100b0e5SJeffrey Huang return -1;
2457100b0e5SJeffrey Huang
24608e1af1aSFarah Smith page = key_obj->offset / key_tbl->page_size;
24708e1af1aSFarah Smith em_tbl = (struct hcapi_cfa_em_table *)key_tbl->base0;
24808e1af1aSFarah Smith op->hw.base_addr = hcapi_get_table_page(em_tbl, page);
2497100b0e5SJeffrey Huang /* Offset is adjusted to be the offset into the page */
2507100b0e5SJeffrey Huang key_obj->offset = key_obj->offset % key_tbl->page_size;
2517100b0e5SJeffrey Huang
2527100b0e5SJeffrey Huang if (op->hw.base_addr == 0)
2537100b0e5SJeffrey Huang return -1;
2547100b0e5SJeffrey Huang
2557100b0e5SJeffrey Huang switch (op->opcode) {
2567100b0e5SJeffrey Huang case HCAPI_CFA_HWOPS_PUT: /**< Write to HW operation */
25708e1af1aSFarah Smith rc = hcapi_cfa_p4_key_hw_op_put(op, key_obj);
2587100b0e5SJeffrey Huang break;
2597100b0e5SJeffrey Huang case HCAPI_CFA_HWOPS_GET: /**< Read from HW operation */
26008e1af1aSFarah Smith rc = hcapi_cfa_p4_key_hw_op_get(op, key_obj);
2617100b0e5SJeffrey Huang break;
2627100b0e5SJeffrey Huang case HCAPI_CFA_HWOPS_ADD:
26308e1af1aSFarah Smith /**< For operations which require more then simple
26408e1af1aSFarah Smith * writes to HW, this operation is used. The
2657100b0e5SJeffrey Huang * distinction with this operation when compared
2667100b0e5SJeffrey Huang * to the PUT ops is that this operation is used
2677100b0e5SJeffrey Huang * in conjunction with the HCAPI_CFA_HWOPS_DEL
2687100b0e5SJeffrey Huang * op to remove the operations issued by the
2697100b0e5SJeffrey Huang * ADD OP.
2707100b0e5SJeffrey Huang */
2717100b0e5SJeffrey Huang
27208e1af1aSFarah Smith rc = hcapi_cfa_p4_key_hw_op_add(op, key_obj);
2737100b0e5SJeffrey Huang
2747100b0e5SJeffrey Huang break;
2757100b0e5SJeffrey Huang case HCAPI_CFA_HWOPS_DEL:
27608e1af1aSFarah Smith rc = hcapi_cfa_p4_key_hw_op_del(op, key_obj);
2777100b0e5SJeffrey Huang break;
2787100b0e5SJeffrey Huang default:
2797100b0e5SJeffrey Huang rc = -1;
2807100b0e5SJeffrey Huang break;
2817100b0e5SJeffrey Huang }
2827100b0e5SJeffrey Huang
2837100b0e5SJeffrey Huang return rc;
2847100b0e5SJeffrey Huang }
28508e1af1aSFarah Smith
28608e1af1aSFarah Smith const struct hcapi_cfa_devops cfa_p4_devops = {
28708e1af1aSFarah Smith .hcapi_cfa_key_hash = hcapi_cfa_p4_key_hash,
28808e1af1aSFarah Smith .hcapi_cfa_key_hw_op = hcapi_cfa_p4_key_hw_op,
28908e1af1aSFarah Smith };
290