[v2,1/2] net/cpfl: parse flow offloading hint from P4 context file

Message ID 20240105081649.530384-2-wenjing.qiao@intel.com (mailing list archive)
State Changes Requested
Delegated to: Bruce Richardson
Headers
Series net/cpfl: support flow offloading for P4 |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Wenjing Qiao Jan. 5, 2024, 8:16 a.m. UTC
  From: Wenjing Qiao <wenjing.qiao@intel.com>

To supporting P4-programmed network controller, reuse devargs
"flow_parser" to specify the path of a p4 context JSON configure
file. The cpfl PMD use the JSON configuration file to translate
rte_flow tokens into low level hardware representation.

Note, the p4 context JSON file is generated by the P4 compiler
and is intended to work exclusively with a specific P4 pipeline
configuration, which must be compiled and programmed into the hardware.

Signed-off-by: Wenjing Qiao <wenjing.qiao@intel.com>
---
 drivers/net/cpfl/cpfl_ethdev.h          |    9 +-
 drivers/net/cpfl/cpfl_flow.c            |   10 +-
 drivers/net/cpfl/cpfl_flow_engine_fxp.c |    9 +-
 drivers/net/cpfl/cpfl_flow_parser.c     |   60 +-
 drivers/net/cpfl/cpfl_flow_parser.h     |    2 +-
 drivers/net/cpfl/cpfl_tdi_parser.c      | 1721 +++++++++++++++++++++++
 drivers/net/cpfl/cpfl_tdi_parser.h      |  294 ++++
 drivers/net/cpfl/meson.build            |    1 +
 8 files changed, 2084 insertions(+), 22 deletions(-)
 create mode 100644 drivers/net/cpfl/cpfl_tdi_parser.c
 create mode 100644 drivers/net/cpfl/cpfl_tdi_parser.h
  

Comments

Vladimir Medvedkin Feb. 9, 2024, 4:20 p.m. UTC | #1
Hi Wenjing,

Please find comments inlined

On 05/01/2024 08:16, wenjing.qiao@intel.com wrote:
> From: Wenjing Qiao <wenjing.qiao@intel.com>
>
> To supporting P4-programmed network controller, reuse devargs
> "flow_parser" to specify the path of a p4 context JSON configure
> file. The cpfl PMD use the JSON configuration file to translate
> rte_flow tokens into low level hardware representation.
>
> Note, the p4 context JSON file is generated by the P4 compiler
> and is intended to work exclusively with a specific P4 pipeline
> configuration, which must be compiled and programmed into the hardware.
>
> Signed-off-by: Wenjing Qiao <wenjing.qiao@intel.com>
> ---
<snip>
> +	if (cpfl_check_is_p4_mode(root)) {
> +		PMD_DRV_LOG(NOTICE, "flow parser mode is p4 mode.");
> +		prog = rte_zmalloc("tdi_parser", sizeof(struct cpfl_tdi_program), 0);
> +		if (prog == NULL) {
> +			PMD_DRV_LOG(ERR, "Failed to create program object.");
> +			return -ENOMEM;
> +		}
> +		ret = cpfl_tdi_program_create(root, prog);
> +		if (ret != 0) {
> +			PMD_INIT_LOG(ERR, "Failed to create tdi program from file %s", filename);
> +			rte_free(prog);

This looks like doublefree to me because cpfl_tdi_program_create() on 
fail calls cpfl_tdi_program_destroy() which in turn does rte_free for 
program.

On a separate note maybe cpfl_tdi_program_create() function should not 
call cpfl_tdi_program_destroy() and maybe it should be responsibility of 
whoever calls _create().


<snip>

> +static int
> +cpfl_tdi_parse_match_key_field_obj(json_t *root, struct cpfl_tdi_match_key_field *mkf)
> +{
> +	int ret, val = 0;
> +	char last3char[4];
> +
> +	ret = cpfl_tdi_get_string_obj(root, "name", mkf->name);
> +	if (ret != 0)
> +		return ret;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "instance_name", mkf->instance_name);
> +	if (ret != 0)
> +		return ret;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "field_name", mkf->field_name);
> +	if (ret != 0)
> +		return ret;
> +	strncpy(last3char, mkf->field_name + strlen(mkf->field_name) - 3, 3);
> +	last3char[3] = '\0';
> +	if (!strcmp(last3char, "VSI") || !strcmp(last3char, "vsi"))

If this is now case sensitive then I'd suggest to make contents of the 
last3char lower case to handle all variations of "VSi".

> +		mkf->is_vsi = true;
> +	else
> +		mkf->is_vsi = false;
> +
> +	ret = cpfl_tdi_parse_match_type(root, mkf);
> +	if (ret != 0)
> +		return ret;
> +
> +	ret = cpfl_tdi_get_integer_obj(root, "bit_width", &val);
> +	if (ret != 0)
> +		return ret;
> +
> +	mkf->bit_width = (uint16_t)val;

Here and several places below, does it need to be range checked?

> +
> +	ret = cpfl_tdi_get_integer_obj(root, "index", &val);
> +	if (ret != 0)
> +		return ret;
> +
> +	mkf->index = (uint32_t)val;
> +
> +	ret = cpfl_tdi_get_integer_obj(root, "position", &val);
> +	if (ret != 0)
> +		return ret;
> +
> +	mkf->position = (uint32_t)val;
> +
> +	return 0;
> +}
> +
> +static int
> +cpfl_tdi_parse_match_key_fields(json_t *root, struct cpfl_tdi_table *table)
> +{
> +	int ret;
> +	int array_len = json_array_size(root);
> +
> +	if (array_len == 0)
> +		return 0;
> +
> +	table->match_key_field_num = (uint16_t)array_len;
> +	table->match_key_fields =
> +	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_match_key_field) * array_len, 0);
> +	if (table->match_key_fields == NULL) {
> +		PMD_DRV_LOG(ERR, "Failed to create match key field array.");
> +		return -ENOMEM;
> +	}
> +
> +	for (int i = 0; i < array_len; i++) {
> +		json_t *mkf_object = json_array_get(root, i);
> +
> +		ret = cpfl_tdi_parse_match_key_field_obj(mkf_object, &table->match_key_fields[i]);
> +		if (ret != 0)
Possible memory leak due to earlier rte_zmalloc(). There are several 
places below with the same problem.
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +cpfl_tdi_parse_byte_order(json_t *root, struct cpfl_tdi_match_key_format *mkf)
> +{
> +	char bo[CPFL_TDI_JSON_STR_SIZE_MAX];
> +	int ret;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "byte_order", bo);
> +	if (ret != 0)
> +		return -EINVAL;
> +
> +	if (!strcmp(bo, "HOST")) {
Is it expected to be upper case always?
> +		mkf->byte_order = CPFL_TDI_BYTE_ORDER_HOST;
> +	} else if (!strcmp(bo, "NETWORK")) {
> +		mkf->byte_order = CPFL_TDI_BYTE_ORDER_NETWORK;
> +	} else {
> +		PMD_DRV_LOG(ERR, "Unknown byte order type %s", bo);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
<snip>
> +
> +static int
> +cpfl_tdi_pparse_action_code(json_t *root, struct cpfl_tdi_hw_action *ha)
double "p"
<snip>
> +static int
> +cpfl_tdi_parse_table_obj(json_t *root, struct cpfl_tdi_table *table)
> +{
> +	int ret, val = 0;
> +	struct json_t *jobj = NULL;
> +
> +	ret = cpfl_tdi_parse_table_type(root, table);
> +	if (ret != 0)
> +		return ret;
> +
> +	ret = cpfl_tdi_get_integer_obj(root, "handle", &val);
> +	if (ret != 0)
> +		return ret;
> +	table->handle = (uint32_t)val;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "name", table->name);
> +	if (ret != 0)
> +		return ret;
> +
> +	if (table->table_type == CPFL_TDI_TABLE_TYPE_POLICER_METER) {
> +		/* TODO */
It looks like not implemented yet, should this return 0? Does it need to 
have some kind of logging?
> +		return 0;
> +	}
<snip>
> +static int
> +cpfl_tdi_parse_global_configs(json_t *root, struct cpfl_tdi_global_configs *gc)
> +{
> +	json_t *jobj = NULL;
> +	int ret;
> +
> +	ret = cpfl_tdi_get_array_obj(root, "hardware_blocks", &jobj);
> +	if (ret != 0)
> +		return ret;
> +
> +	return cpfl_tdi_parse_gc_hardware_blocks(jobj, gc);
> +}
> +
> +int
> +cpfl_tdi_program_create(json_t *root, struct cpfl_tdi_program *prog)
Should input parameters be checked against NULL?
> +{
> +	json_t *jobj = NULL;
> +	int ret;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "program_name", prog->program_name);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "build_date", prog->build_date);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "compile_command", prog->compile_command);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "compiler_version", prog->compiler_version);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "schema_version", prog->schema_version);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_string_obj(root, "target", prog->target);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_object_obj(root, "global_configs", &jobj);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_parse_global_configs(jobj, &prog->global_configs);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_get_array_obj(root, "tables", &jobj);
> +	if (ret != 0)
> +		goto err;
> +
> +	ret = cpfl_tdi_parse_tables(jobj, prog);
> +	if (ret != 0)
> +		goto err;
> +
> +	json_decref(root);
is this json_decref() needed? This function is called from 
cpfl_parser_create() which already does json_decref().
> +
> +	return 0;
> +
> +err:
> +	cpfl_tdi_program_destroy(prog);
> +	return ret;
> +}
> +
<snip>
  

Patch

diff --git a/drivers/net/cpfl/cpfl_ethdev.h b/drivers/net/cpfl/cpfl_ethdev.h
index 457db6d6be..e580f80f2f 100644
--- a/drivers/net/cpfl/cpfl_ethdev.h
+++ b/drivers/net/cpfl/cpfl_ethdev.h
@@ -185,6 +185,12 @@  struct cpfl_repr {
 	bool func_up; /* If the represented function is up */
 };
 
+struct cpfl_flow_parser {
+	struct cpfl_flow_js_parser *fixed_parser;
+	struct cpfl_tdi_program *p4_parser;
+	bool is_p4_parser;
+};
+
 struct cpfl_metadata_chunk {
 	int type;
 	uint8_t data[CPFL_META_CHUNK_LENGTH];
@@ -218,8 +224,7 @@  struct cpfl_adapter_ext {
 
 	rte_spinlock_t repr_lock;
 	struct rte_hash *repr_allowlist_hash;
-
-	struct cpfl_flow_js_parser *flow_parser;
+	struct cpfl_flow_parser flow_parser;
 	struct rte_bitmap *mod_bm;
 	void *mod_bm_mem;
 
diff --git a/drivers/net/cpfl/cpfl_flow.c b/drivers/net/cpfl/cpfl_flow.c
index 3ba6c0f0e7..1c4131da2c 100644
--- a/drivers/net/cpfl/cpfl_flow.c
+++ b/drivers/net/cpfl/cpfl_flow.c
@@ -6,6 +6,7 @@ 
 
 #include "cpfl_flow.h"
 #include "cpfl_flow_parser.h"
+#include "cpfl_tdi_parser.h"
 
 TAILQ_HEAD(cpfl_flow_engine_list, cpfl_flow_engine);
 
@@ -331,9 +332,14 @@  cpfl_flow_init(struct cpfl_adapter_ext *ad, struct cpfl_devargs *devargs)
 void
 cpfl_flow_uninit(struct cpfl_adapter_ext *ad)
 {
-	if (ad->flow_parser == NULL)
+	if (ad->flow_parser.fixed_parser == NULL && ad->flow_parser.p4_parser == NULL)
 		return;
 
-	cpfl_parser_destroy(ad->flow_parser);
+	if (ad->flow_parser.fixed_parser)
+		cpfl_parser_destroy(ad->flow_parser.fixed_parser);
+
+	if (ad->flow_parser.p4_parser)
+		cpfl_tdi_program_destroy(ad->flow_parser.p4_parser);
+
 	cpfl_flow_engine_uninit(ad);
 }
diff --git a/drivers/net/cpfl/cpfl_flow_engine_fxp.c b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
index 8a4e1419b4..f269ff97e1 100644
--- a/drivers/net/cpfl/cpfl_flow_engine_fxp.c
+++ b/drivers/net/cpfl/cpfl_flow_engine_fxp.c
@@ -503,20 +503,25 @@  cpfl_fxp_parse_pattern_action(struct rte_eth_dev *dev,
 	struct cpfl_rule_info_meta *rim;
 	int ret;
 
+	if (adapter->flow_parser.is_p4_parser)
+		return -EINVAL;
+
 	ret = cpfl_fxp_get_metadata_port(itf, actions);
 	if (!ret) {
 		PMD_DRV_LOG(ERR, "Fail to save metadata.");
 		return -EINVAL;
 	}
 
-	ret = cpfl_flow_parse_items(itf, adapter->flow_parser, pattern, attr, &pr_action);
+	ret = cpfl_flow_parse_items(itf, adapter->flow_parser.fixed_parser, pattern, attr,
+				    &pr_action);
 	if (ret) {
 		PMD_DRV_LOG(ERR, "No Match pattern support.");
 		return -EINVAL;
 	}
 
 	if (cpfl_is_mod_action(actions)) {
-		ret = cpfl_flow_parse_actions(adapter->flow_parser, actions, mr_action);
+		ret = cpfl_flow_parse_actions(adapter->flow_parser.fixed_parser,
+					      actions, mr_action);
 		if (ret) {
 			PMD_DRV_LOG(ERR, "action parse fails.");
 			return -EINVAL;
diff --git a/drivers/net/cpfl/cpfl_flow_parser.c b/drivers/net/cpfl/cpfl_flow_parser.c
index a8f0488f21..e7f8a8a6cc 100644
--- a/drivers/net/cpfl/cpfl_flow_parser.c
+++ b/drivers/net/cpfl/cpfl_flow_parser.c
@@ -5,6 +5,7 @@ 
 #include <arpa/inet.h>
 
 #include "cpfl_flow_parser.h"
+#include "cpfl_tdi_parser.h"
 
 static enum rte_flow_item_type
 cpfl_get_item_type_by_str(const char *type)
@@ -938,36 +939,65 @@  cpfl_parser_init(json_t *ob_root, struct cpfl_flow_js_parser *parser)
 	return 0;
 }
 
+static int
+cpfl_check_is_p4_mode(json_t *ob_root)
+{
+	return json_object_get(ob_root, "patterns") ? false : true;
+}
+
 int
-cpfl_parser_create(struct cpfl_flow_js_parser **flow_parser, const char *filename)
+cpfl_parser_create(struct cpfl_flow_parser *flow_parser, const char *filename)
 {
 	struct cpfl_flow_js_parser *parser;
+	struct cpfl_tdi_program *prog;
 	json_error_t json_error;
 	json_t *root;
 	int ret;
 
-	parser = rte_zmalloc("flow_parser", sizeof(struct cpfl_flow_js_parser), 0);
-	if (!parser) {
-		PMD_DRV_LOG(ERR, "Not enough memory to create flow parser.");
-		return -ENOMEM;
-	}
 	root = json_load_file(filename, 0, &json_error);
 	if (!root) {
 		PMD_DRV_LOG(ERR, "Bad JSON file \"%s\": %s", filename, json_error.text);
-		goto free_parser;
+		return -EINVAL;
 	}
-	ret = cpfl_parser_init(root, parser);
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR, "parser init failed.");
-		goto free_parser;
+
+	if (cpfl_check_is_p4_mode(root)) {
+		PMD_DRV_LOG(NOTICE, "flow parser mode is p4 mode.");
+		prog = rte_zmalloc("tdi_parser", sizeof(struct cpfl_tdi_program), 0);
+		if (prog == NULL) {
+			PMD_DRV_LOG(ERR, "Failed to create program object.");
+			return -ENOMEM;
+		}
+		ret = cpfl_tdi_program_create(root, prog);
+		if (ret != 0) {
+			PMD_INIT_LOG(ERR, "Failed to create tdi program from file %s", filename);
+			rte_free(prog);
+			return -EINVAL;
+		}
+		flow_parser->p4_parser = prog;
+		flow_parser->fixed_parser = NULL;
+		flow_parser->is_p4_parser = true;
+	} else {
+		PMD_DRV_LOG(NOTICE, "flow parser mode is fixed function mode.");
+		parser = rte_zmalloc("flow_parser", sizeof(struct cpfl_flow_js_parser), 0);
+		if (!parser) {
+			PMD_DRV_LOG(ERR, "Not enough memory to create flow parser.");
+			return -ENOMEM;
+		}
+
+		ret = cpfl_parser_init(root, parser);
+		if (ret < 0) {
+			PMD_DRV_LOG(ERR, "parser init failed.");
+			rte_free(parser);
+			return -EINVAL;
+		}
+		flow_parser->fixed_parser = parser;
+		flow_parser->p4_parser = NULL;
+		flow_parser->is_p4_parser = false;
 	}
-	*flow_parser = parser;
+
 	json_decref(root);
 
 	return 0;
-free_parser:
-	rte_free(parser);
-	return -EINVAL;
 }
 
 static void
diff --git a/drivers/net/cpfl/cpfl_flow_parser.h b/drivers/net/cpfl/cpfl_flow_parser.h
index 23904e39f1..d420464d5c 100644
--- a/drivers/net/cpfl/cpfl_flow_parser.h
+++ b/drivers/net/cpfl/cpfl_flow_parser.h
@@ -273,7 +273,7 @@  struct cpfl_flow_mr_action {
 	};
 };
 
-int cpfl_parser_create(struct cpfl_flow_js_parser **parser, const char *filename);
+int cpfl_parser_create(struct cpfl_flow_parser *flow_parser, const char *filename);
 int cpfl_parser_destroy(struct cpfl_flow_js_parser *parser);
 int cpfl_flow_parse_items(struct cpfl_itf *itf,
 			  struct cpfl_flow_js_parser *parser,
diff --git a/drivers/net/cpfl/cpfl_tdi_parser.c b/drivers/net/cpfl/cpfl_tdi_parser.c
new file mode 100644
index 0000000000..f771e8a60a
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi_parser.c
@@ -0,0 +1,1721 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+
+#include "cpfl_tdi_parser.h"
+#include "base/idpf_osdep.h"
+
+static int
+cpfl_tdi_get_integer_obj(json_t *jobj, const char *key, int *output)
+{
+	json_t *int_obj = json_object_get(jobj, key);
+
+	if (int_obj == NULL) {
+		PMD_DRV_LOG(ERR, "Missing %s", key);
+		return -EINVAL;
+	}
+
+	if (!json_is_integer(int_obj)) {
+		PMD_DRV_LOG(ERR, "%s is not a integer object.", key);
+		return -EINVAL;
+	}
+
+	*output = json_integer_value(int_obj);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_get_string_obj(json_t *jobj, const char *key, char *output)
+{
+	json_t *str_obj = json_object_get(jobj, key);
+
+	if (str_obj == NULL) {
+		PMD_DRV_LOG(ERR, "Missing %s", key);
+		return -EINVAL;
+	}
+
+	if (!json_is_string(str_obj)) {
+		PMD_DRV_LOG(ERR, "%s is not a string object.", key);
+		return -EINVAL;
+	}
+
+	strncpy(output, json_string_value(str_obj), CPFL_TDI_JSON_STR_SIZE_MAX - 1);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_get_boolean_obj(json_t *jobj, const char *key, bool *output)
+{
+	json_t *bool_obj = json_object_get(jobj, key);
+
+	if (bool_obj == NULL) {
+		PMD_DRV_LOG(ERR, "Missing %s", key);
+		return -EINVAL;
+	}
+
+	if (!json_is_boolean(bool_obj)) {
+		PMD_DRV_LOG(ERR, "%s is not a boolean object.", key);
+		return -EINVAL;
+	}
+
+	*output = (bool)json_integer_value(bool_obj);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_get_array_obj(json_t *jobj, const char *key, json_t **output)
+{
+	json_t *array_obj = json_object_get(jobj, key);
+
+	if (array_obj == NULL) {
+		PMD_DRV_LOG(ERR, "Missing %s", key);
+		return -EINVAL;
+	}
+
+	if (!json_is_array(array_obj)) {
+		PMD_DRV_LOG(ERR, "%s is not a array object.", key);
+		return -EINVAL;
+	}
+
+	*output = array_obj;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_get_object_obj(json_t *jobj, const char *key, json_t **output)
+{
+	json_t *obj_obj = json_object_get(jobj, key);
+
+	if (obj_obj == NULL) {
+		PMD_DRV_LOG(ERR, "Missing %s", key);
+		return -EINVAL;
+	}
+
+	if (!json_is_object(obj_obj)) {
+		PMD_DRV_LOG(ERR, "%s is not a array object.", key);
+		return -EINVAL;
+	}
+
+	*output = obj_obj;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_table_type(json_t *root, struct cpfl_tdi_table *table)
+{
+	char tt[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "table_type", tt);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(tt, "match")) {
+		table->table_type = CPFL_TDI_TABLE_TYPE_MATCH;
+	} else if (!strcmp(tt, "match_value_lookup_table")) {
+		table->table_type = CPFL_TDI_TABLE_TYPE_MATCH_VALUE_LOOKUP_TABLE;
+	} else if (!strcmp(tt, "policer_meter")) {
+		table->table_type = CPFL_TDI_TABLE_TYPE_POLICER_METER;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown table type %s", tt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_table_dir(json_t *root, struct cpfl_tdi_table *table)
+{
+	char dir[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "direction", dir);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(dir, "RX")) {
+		table->direction = CPFL_TDI_TABLE_DIR_RX;
+	} else if (!strcmp(dir, "TX")) {
+		table->direction = CPFL_TDI_TABLE_DIR_TX;
+	} else if (!strcmp(dir, "BIDIRECTIONAL")) {
+		table->direction = CPFL_TDI_TABLE_DIR_BI;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown direction type %s", dir);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_type(json_t *root, struct cpfl_tdi_match_key_field *mkf)
+{
+	char mt[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "match_type", mt);
+	if (ret != 0)
+		return ret;
+
+	if (!strcmp(mt, "exact")) {
+		mkf->match_type = CPFL_TDI_MATCH_TYPE_EXACT;
+	} else if (!strcmp(mt, "selector")) {
+		mkf->match_type = CPFL_TDI_MATCH_TYPE_SELECTOR;
+	} else if (!strcmp(mt, "ternary")) {
+		mkf->match_type = CPFL_TDI_MATCH_TYPE_TERNARY;
+	} else if (!strcmp(mt, "lpm")) {
+		mkf->match_type = CPFL_TDI_MATCH_TYPE_LPM;
+	} else {
+		PMD_DRV_LOG(ERR, "Unsupported match type %s.", mt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_key_field_obj(json_t *root, struct cpfl_tdi_match_key_field *mkf)
+{
+	int ret, val = 0;
+	char last3char[4];
+
+	ret = cpfl_tdi_get_string_obj(root, "name", mkf->name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "instance_name", mkf->instance_name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "field_name", mkf->field_name);
+	if (ret != 0)
+		return ret;
+	strncpy(last3char, mkf->field_name + strlen(mkf->field_name) - 3, 3);
+	last3char[3] = '\0';
+	if (!strcmp(last3char, "VSI") || !strcmp(last3char, "vsi"))
+		mkf->is_vsi = true;
+	else
+		mkf->is_vsi = false;
+
+	ret = cpfl_tdi_parse_match_type(root, mkf);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "bit_width", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->bit_width = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "index", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->index = (uint32_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "position", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->position = (uint32_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_key_fields(json_t *root, struct cpfl_tdi_table *table)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	table->match_key_field_num = (uint16_t)array_len;
+	table->match_key_fields =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_match_key_field) * array_len, 0);
+	if (table->match_key_fields == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create match key field array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *mkf_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_match_key_field_obj(mkf_object, &table->match_key_fields[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_byte_order(json_t *root, struct cpfl_tdi_match_key_format *mkf)
+{
+	char bo[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "byte_order", bo);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(bo, "HOST")) {
+		mkf->byte_order = CPFL_TDI_BYTE_ORDER_HOST;
+	} else if (!strcmp(bo, "NETWORK")) {
+		mkf->byte_order = CPFL_TDI_BYTE_ORDER_NETWORK;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown byte order type %s", bo);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_key_format_obj(json_t *root, struct cpfl_tdi_match_key_format *mkf)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_integer_obj(root, "match_key_handle", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->match_key_handle = (uint32_t)val;
+
+	ret = cpfl_tdi_parse_byte_order(root, mkf);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "byte_array_index", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->byte_array_index = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "start_bit_offset", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->start_bit_offset = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "bit_width", &val);
+	if (ret != 0)
+		return ret;
+
+	mkf->bit_width = (uint16_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_key_format_array(json_t *root, struct cpfl_tdi_table *table)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	table->match_key_format_num = (uint16_t)array_len;
+	table->match_key_format =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_match_key_format) * array_len, 0);
+	if (table->match_key_format == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create match key format array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *mkf_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_match_key_format_obj(mkf_object, &table->match_key_format[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_p4_parameter_obj(json_t *root, struct cpfl_tdi_p4_parameter *param)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", param->name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "bit_width", &val);
+	if (ret != 0)
+		return ret;
+
+	param->bit_width = (uint16_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_p4_parameters(json_t *root, struct cpfl_tdi_action *act)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	act->p4_parameter_num = (uint16_t)array_len;
+	act->p4_parameters = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_p4_parameter) * array_len, 0);
+	if (act->p4_parameters == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create p4 parameter array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *pp_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_p4_parameter_obj(pp_object, &act->p4_parameters[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_action_obj(json_t *root, struct cpfl_tdi_action *act)
+{
+	int ret, val = 0;
+	json_t *jobj = NULL;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", act->name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "handle", &val);
+	if (ret != 0)
+		return ret;
+
+	act->handle = (uint32_t)val;
+
+	ret = cpfl_tdi_get_boolean_obj(root, "constant_default_action",
+				       &act->constant_default_action);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_boolean_obj(root, "is_compiler_added_action",
+				       &act->is_compiler_added_action);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_boolean_obj(root, "allowed_as_hit_action", &act->allowed_as_hit_action);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_boolean_obj(root, "allowed_as_default_action",
+				       &act->allowed_as_default_action);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_array_obj(root, "p4_parameters", &jobj);
+	if (ret != 0)
+		return ret;
+
+	return cpfl_tdi_parse_p4_parameters(jobj, act);
+}
+
+static int
+cpfl_tdi_parse_actions(json_t *root, struct cpfl_tdi_table *table)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	table->action_num = (uint16_t)array_len;
+	table->actions = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_action) * array_len, 0);
+	if (table->actions == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create action array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *act_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_action_obj(act_object, &table->actions[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_ma_hw_block(json_t *root, struct cpfl_tdi_ma_hardware_block *hb)
+{
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", name);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(name, "SEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_SEM;
+	} else if (!strcmp(name, "LEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_LEM;
+	} else if (!strcmp(name, "WCM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_WCM;
+	} else if (!strcmp(name, "LPM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_LPM;
+	} else if (!strcmp(name, "MOD")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_MOD;
+	} else if (!strcmp(name, "HASH")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_HASH;
+	} else if (!strcmp(name, "RC")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_RC;
+	} else if (!strcmp(name, "CXP_LEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_CXP_LEM;
+	} else if (!strcmp(name, "METER")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_METER;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown hardware block type %s", name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_profiles(json_t *root, struct cpfl_tdi_ma_hardware_block *hb)
+{
+	int array_len = json_array_size(root);
+
+	if (array_len > 16) {
+		PMD_DRV_LOG(ERR, "Profile array out of bound: %d.", array_len);
+		return -EINVAL;
+	}
+
+	if (array_len == 0)
+		return 0;
+
+	hb->profile_num = (uint16_t)array_len;
+	for (int i = 0; i < array_len; i++) {
+		int val;
+		json_t *int_obj = json_array_get(root, i);
+
+		if (!json_is_integer(int_obj)) {
+			PMD_DRV_LOG(ERR, "Invalid profile id, not an integer.");
+			return -EINVAL;
+		}
+		val = json_integer_value(int_obj);
+		hb->profile[i] = (uint8_t)val;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_immediate_field_obj(json_t *root, struct cpfl_tdi_immediate_field *imf)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_string_obj(root, "param_name", imf->param_name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "param_handle", &val);
+	if (ret != 0)
+		return ret;
+
+	imf->param_handle = (uint32_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "dest_start", &val);
+	if (ret != 0)
+		return ret;
+
+	imf->dest_start = (uint16_t)val;
+	if (json_object_get(root, "start_bit_offset")) {
+		ret = cpfl_tdi_get_integer_obj(root, "start_bit_offset", &val);
+		if (ret != 0)
+			return ret;
+
+		imf->start_bit_offset = (uint16_t)val;
+	}
+
+	ret = cpfl_tdi_get_integer_obj(root, "dest_width", &val);
+	if (ret != 0)
+		return ret;
+
+	imf->dest_width = (uint16_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_af_immediate_fields(json_t *root, struct cpfl_tdi_action_format *af)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	af->immediate_field_num = (uint16_t)array_len;
+	af->immediate_fields =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_immediate_field) * array_len, 0);
+	if (af->immediate_fields == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to immediate field array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *if_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_immediate_field_obj(if_object, &af->immediate_fields[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_field_type(json_t *root, struct cpfl_tdi_mod_field *mf)
+{
+	char t[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+	int val = 0;
+
+	ret = cpfl_tdi_get_string_obj(root, "type", t);
+	if (ret != 0)
+		return ret;
+
+	if (!strcmp("parameter", t)) {
+		mf->type = CPFL_TDI_MOD_FIELD_TYPE_PARAMETER;
+		ret = cpfl_tdi_get_integer_obj(root, "param_handle", &val);
+		if (ret != 0)
+			return ret;
+		mf->param_handle = (uint32_t)val;
+	} else if (!strcmp("constant", t)) {
+		mf->type = CPFL_TDI_MOD_FIELD_TYPE_CONSTANT;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown mod field type %s.", t);
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_field_byte_order(json_t *root, struct cpfl_tdi_mod_field *mf)
+{
+	char bo[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "byte_order", bo);
+	if (ret != 0)
+		return ret;
+
+	if (!strcmp("HOST", bo))
+		mf->byte_order = CPFL_TDI_BYTE_ORDER_HOST;
+	else if (!strcmp("NETWORK", bo))
+		mf->byte_order = CPFL_TDI_BYTE_ORDER_NETWORK;
+	else
+		PMD_DRV_LOG(ERR, "Unknown byte order type %s.", bo);
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_field_value(json_t *root, struct cpfl_tdi_mod_field *mf)
+{
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	if (array_len > CPFL_TDI_VALUE_SIZE_MAX) {
+		PMD_DRV_LOG(ERR, "Value array out of bound.");
+		return -EINVAL;
+	}
+
+	mf->value_size = (uint16_t)array_len;
+	for (int i = 0; i < array_len; i++) {
+		int val;
+		json_t *val_obj = json_array_get(root, i);
+
+		if (!json_is_integer(val_obj)) {
+			PMD_DRV_LOG(ERR, "Invalid value item, not an integer.");
+			return -EINVAL;
+		}
+		val = json_integer_value(val_obj);
+		mf->value[i] = (uint8_t)val;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_field_obj(json_t *root, struct cpfl_tdi_mod_field *mf)
+{
+	json_t *jobj = NULL;
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", mf->name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "handle", &val);
+	if (ret != 0)
+		return ret;
+
+	mf->handle = (uint32_t)val;
+
+	ret = cpfl_tdi_parse_mod_field_type(root, mf);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_mod_field_byte_order(root, mf);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "byte_array_index", &val);
+	if (ret != 0)
+		return ret;
+
+	mf->byte_array_index = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "start_bit_offset", &val);
+	if (ret != 0)
+		return ret;
+
+	mf->start_bit_offset = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "bit_width", &val);
+	if (ret != 0)
+		return ret;
+
+	mf->bit_width = (uint16_t)val;
+
+	ret = cpfl_tdi_get_array_obj(root, "value", &jobj);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_mod_field_value(jobj, mf);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_fields(json_t *root, struct cpfl_tdi_mod_content_format *mcf)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	mcf->mod_field_num = (uint16_t)array_len;
+	mcf->mod_fields = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_mod_field) * array_len, 0);
+	if (mcf->mod_fields == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create mod field array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *mf_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_mod_field_obj(mf_object, &mcf->mod_fields[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_mod_content_format(json_t *root, struct cpfl_tdi_mod_content_format *mcf)
+{
+	json_t *jobj = NULL;
+	int ret, val = 0;
+
+	if (json_object_get(root, "mod_profile")) {
+		ret = cpfl_tdi_get_integer_obj(root, "mod_profile", &val);
+		if (ret != 0)
+			return ret;
+		mcf->mod_profile = (uint16_t)val;
+	} else if (json_object_get(root, "mod_lut_num")) {
+		ret = cpfl_tdi_get_integer_obj(root, "mod_lut_num", &val);
+		if (ret != 0)
+			return ret;
+		mcf->mod_lut_num = (uint16_t)val;
+	} else {
+		PMD_DRV_LOG(ERR, "Failed to parse mod_content_format.");
+		return -EINVAL;
+	}
+
+	ret = cpfl_tdi_get_integer_obj(root, "mod_obj_size", &val);
+	if (ret != 0)
+		return ret;
+
+	mcf->mod_obj_size = (uint16_t)val;
+
+	if (json_object_get(root, "mod_fields") != NULL) {
+		ret = cpfl_tdi_get_array_obj(root, "mod_fields", &jobj);
+		if (ret != 0)
+			return ret;
+
+		return cpfl_tdi_parse_mod_fields(jobj, mcf);
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_pparse_action_code(json_t *root, struct cpfl_tdi_hw_action *ha)
+{
+	char ac[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "action_code", ac);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(ac, "SET10_1b")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_SET10_1b;
+	} else if (!strcmp(ac, "SET1_16b")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_SET1_16b;
+	} else if (!strcmp(ac, "SET1A_24b")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_SET1A_24b;
+	} else if (!strcmp(ac, "SET1B_24b")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_SET1B_24b;
+	} else if (!strcmp(ac, "SET2_8b")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_SET2_8b;
+	} else if (!strcmp(ac, "NOP")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_NOP;
+	} else if (!strcmp(ac, "AUX_DATA")) {
+		ha->action_code = CPFL_TDI_ACTION_CODE_AUX_DATA;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown action code type %s", ac);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_setmd_action_code(json_t *root, struct cpfl_tdi_hw_action *ha)
+{
+	char ac[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	/* allow no value */
+	if (json_object_get(root, "setmd_action_code") == NULL) {
+		ha->setmd_action_code = CPFL_TDI_SETMD_ACTION_CODE_NONE;
+		return 0;
+	}
+
+	ret = cpfl_tdi_get_string_obj(root, "setmd_action_code", ac);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(ac, "SET_8b")) {
+		ha->setmd_action_code = CPFL_TDI_SETMD_ACTION_CODE_SET_8b;
+	} else if (!strcmp(ac, "SET_16b")) {
+		ha->setmd_action_code = CPFL_TDI_SETMD_ACTION_CODE_SET_16b;
+	} else if (!strcmp(ac, "SET_32b_AUX")) {
+		ha->setmd_action_code = CPFL_TDI_SETMD_ACTION_CODE_SET_32b_AUX;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown setmd action code type %s", ac);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_hw_action_parameter_obj(json_t *root, struct cpfl_tdi_hw_action_parameter *param)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_string_obj(root, "param_name", param->param_name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "param_handle", &val);
+	if (ret != 0)
+		return ret;
+
+	param->param_handle = (uint32_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_hw_action_parameters(json_t *root, struct cpfl_tdi_hw_action *ha)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	ha->parameter_num = (uint16_t)array_len;
+	ha->parameters =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_hw_action_parameter) * array_len, 0);
+	if (ha->parameters == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create hw action parameter array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *p_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_hw_action_parameter_obj(p_object, &ha->parameters[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_hw_action_obj(json_t *root, struct cpfl_tdi_hw_action *ha)
+{
+	int ret, val = 0;
+	json_t *jobj = NULL;
+
+	ret = cpfl_tdi_get_integer_obj(root, "prec", &val);
+	if (ret != 0)
+		return ret;
+
+	ha->prec = (uint16_t)val;
+
+	ret = cpfl_tdi_pparse_action_code(root, ha);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_setmd_action_code(root, ha);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "index", &val);
+	if (ret != 0)
+		return ret;
+
+	ha->index = (uint32_t)val;
+
+	if (json_object_get(root, "mod_profile") != NULL) {
+		ret = cpfl_tdi_get_integer_obj(root, "mod_profile", &val);
+		if (ret != 0)
+			return ret;
+		ha->mod_profile = (uint16_t)val;
+	}
+
+	if (json_object_get(root, "prefetch") != NULL) {
+		ret = cpfl_tdi_get_integer_obj(root, "prefetch", &val);
+		if (ret != 0)
+			return ret;
+		ha->prefetch = (uint16_t)val;
+	}
+
+	if (json_object_get(root, "parameters") != NULL) {
+		ret = cpfl_tdi_get_array_obj(root, "parameters", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_hw_action_parameters(jobj, ha);
+		if (ret != 0)
+			return ret;
+	}
+
+	if (json_object_get(root, "p4_ref_action_handle")) {
+		ret = cpfl_tdi_get_integer_obj(root, "p4_ref_action_handle", &val);
+		if (ret != 0)
+			return ret;
+		ha->p4_ref_action_handle = (uint32_t)val;
+	}
+
+	if (json_object_get(root, "p4_ref_table_handle")) {
+		ret = cpfl_tdi_get_integer_obj(root, "p4_ref_table_handle", &val);
+		if (ret != 0)
+			return ret;
+		ha->p4_ref_table_handle = (uint32_t)val;
+	}
+
+	if (json_object_get(root, "value")) {
+		ret = cpfl_tdi_get_integer_obj(root, "value", &val);
+		if (ret != 0)
+			return ret;
+		ha->value = (uint16_t)val;
+	}
+
+	if (json_object_get(root, "mask")) {
+		ret = cpfl_tdi_get_integer_obj(root, "mask", &val);
+		if (ret != 0)
+			return ret;
+		ha->mask = (uint16_t)val;
+	}
+
+	if (json_object_get(root, "type_id")) {
+		ret = cpfl_tdi_get_integer_obj(root, "type_id", &val);
+		if (ret != 0)
+			return ret;
+		ha->type_id = (uint16_t)val;
+	}
+
+	if (json_object_get(root, "offset")) {
+		ret = cpfl_tdi_get_integer_obj(root, "offset", &val);
+		if (ret != 0)
+			return ret;
+		ha->offset = (uint16_t)val;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_hw_actions_list(json_t *root, struct cpfl_tdi_action_format *af)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	af->hw_action_num = (uint16_t)array_len;
+	af->hw_actions_list = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_hw_action) * array_len, 0);
+	if (af->hw_actions_list == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create hw action array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *ha_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_hw_action_obj(ha_object, &af->hw_actions_list[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_action_format_obj(json_t *root, struct cpfl_tdi_action_format *af)
+{
+	int ret, val = 0;
+	json_t *jobj = NULL;
+
+	ret = cpfl_tdi_get_string_obj(root, "action_name", af->action_name);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "action_handle", &val);
+	if (ret != 0)
+		return ret;
+
+	af->action_handle = (uint32_t)val;
+
+	ret = cpfl_tdi_get_array_obj(root, "immediate_fields", &jobj);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_af_immediate_fields(jobj, af);
+	if (ret != 0)
+		return ret;
+
+	jobj = json_object_get(root, "mod_content_format");
+	if (jobj != NULL) {
+		ret = cpfl_tdi_parse_mod_content_format(jobj, &af->mod_content_format);
+		if (ret != 0)
+			return ret;
+	}
+
+	ret = cpfl_tdi_get_array_obj(root, "hw_actions_list", &jobj);
+	if (ret != 0)
+		return ret;
+
+	return cpfl_tdi_parse_hw_actions_list(jobj, af);
+}
+
+static int
+cpfl_tdi_parse_action_format_array(json_t *root, struct cpfl_tdi_ma_hardware_block *hb)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	hb->action_format_num = (uint16_t)array_len;
+	hb->action_format = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_action_format) * array_len, 0);
+	if (hb->action_format == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create action format array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *af_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_action_format_obj(af_object, &hb->action_format[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_act_rams(json_t *root, struct cpfl_tdi_wcm_params *wm)
+{
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	if (array_len > 16) {
+		PMD_DRV_LOG(ERR, "Action ram array out of bound.");
+		return -EINVAL;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		int val;
+		json_t *am_obj = json_array_get(root, i);
+
+		if (!json_is_integer(am_obj)) {
+			PMD_DRV_LOG(ERR, "Invalid action ram index, not an integer.");
+			return -EINVAL;
+		}
+		val = json_integer_value(am_obj);
+		wm->act_rams[i] = (uint8_t)val;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_wcm_params(json_t *root, struct cpfl_tdi_wcm_params *wm)
+{
+	int ret, val = 0;
+	json_t *jobj = NULL;
+
+	ret = cpfl_tdi_get_integer_obj(root, "wcm_group", &val);
+	if (ret != 0)
+		return ret;
+
+	wm->wcm_group = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "slice_start_idx", &val);
+	if (ret != 0)
+		return ret;
+
+	wm->slice_start_idx = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "table_width", &val);
+	if (ret != 0)
+		return ret;
+
+	wm->table_width = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "entry_cnt", &val);
+	if (ret != 0)
+		return ret;
+
+	wm->entry_cnt = (uint16_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "entry_idx", &val);
+	if (ret != 0)
+		return ret;
+
+	wm->entry_idx = (uint16_t)val;
+
+	ret = cpfl_tdi_get_array_obj(root, "act_rams", &jobj);
+	if (ret != 0)
+		return ret;
+
+	return cpfl_tdi_parse_act_rams(jobj, wm);
+}
+
+static int
+cpfl_tdi_parse_hb_immediate_fields(json_t *root, struct cpfl_tdi_ma_hardware_block *hb)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	hb->meter.immediate_field_num = (uint16_t)array_len;
+	hb->meter.immediate_fields =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_immediate_field) * array_len, 0);
+	if (hb->meter.immediate_fields == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to immediate field array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *if_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_immediate_field_obj(if_object, &hb->meter.immediate_fields[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_ma_hardware_block_obj(json_t *root,
+				     enum cpfl_tdi_table_type table_type,
+				     struct cpfl_tdi_ma_hardware_block *hb)
+{
+	int ret, val = 0;
+	json_t *jobj = NULL;
+
+	ret = cpfl_tdi_parse_ma_hw_block(root, hb);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "id", &val);
+	if (ret != 0)
+		return ret;
+
+	hb->id = (uint32_t)val;
+
+	ret = cpfl_tdi_get_string_obj(root, "hw_interface", hb->hw_interface);
+	if (ret != 0)
+		return ret;
+	if (table_type == CPFL_TDI_TABLE_TYPE_MATCH) {
+		ret = cpfl_tdi_get_array_obj(root, "profile", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_profiles(jobj, hb);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_get_array_obj(root, "action_format", &jobj);
+		if (ret != 0)
+			return ret;
+		ret = cpfl_tdi_parse_action_format_array(jobj, hb);
+		if (ret != 0)
+			return ret;
+	}
+
+	switch (hb->hw_block) {
+	case CPFL_TDI_HW_BLOCK_SEM:
+		ret = cpfl_tdi_get_integer_obj(root, "sub_profile", &val);
+		if (ret != 0)
+			return ret;
+
+		hb->sem.sub_profile = (uint16_t)val;
+
+		ret = cpfl_tdi_get_integer_obj(root, "obj_id", &val);
+		if (ret != 0)
+			return ret;
+
+		hb->sem.obj_id = (uint32_t)val;
+		break;
+	case CPFL_TDI_HW_BLOCK_WCM:
+		ret = cpfl_tdi_get_object_obj(root, "wcm_params", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_wcm_params(jobj, &hb->wcm.wcm_params);
+		if (ret != 0)
+			return ret;
+		break;
+	case CPFL_TDI_HW_BLOCK_MOD:
+		ret = cpfl_tdi_get_string_obj(root, "hw_resource", hb->mod.hw_resource);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_get_integer_obj(root, "hw_resource_id", &val);
+		if (ret != 0)
+			return ret;
+		hb->mod.hw_resource_id = (uint32_t)val;
+		break;
+	case CPFL_TDI_HW_BLOCK_METER:
+		ret = cpfl_tdi_get_string_obj(root, "hw_resource", hb->mod.hw_resource);
+		if (ret != 0)
+			return ret;
+		ret = cpfl_tdi_get_integer_obj(root, "hw_resource_id", &val);
+		if (ret != 0)
+			return ret;
+		hb->mod.hw_resource_id = (uint32_t)val;
+		ret = cpfl_tdi_get_array_obj(root, "immediate_fields", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_hb_immediate_fields(jobj, hb);
+		if (ret != 0)
+			return ret;
+		break;
+	case CPFL_TDI_HW_BLOCK_LEM:
+	case CPFL_TDI_HW_BLOCK_CXP_LEM:
+		ret = cpfl_tdi_get_integer_obj(root, "hash_size", &val);
+		if (ret != 0)
+			return ret;
+		hb->lem.hash_size = (uint16_t)val;
+		break;
+	case CPFL_TDI_HW_BLOCK_LPM:
+		ret = cpfl_tdi_get_integer_obj(root, "max_prefix_len", &val);
+		if (ret != 0)
+			return ret;
+		hb->lpm.max_prefix_len = (uint16_t)val;
+		break;
+	case CPFL_TDI_HW_BLOCK_HASH:
+		break;
+	default:
+		printf("not support this hardware_block type: %d\n", hb->hw_block);
+		break;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_ma_hardware_blocks(json_t *root,
+				  enum cpfl_tdi_table_type table_type,
+				  struct cpfl_tdi_match_attributes *ma)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	ma->hardware_block_num = (uint16_t)array_len;
+	ma->hardware_blocks =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_ma_hardware_block) * array_len, 0);
+	if (ma->hardware_blocks == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create match attribute's hardware block array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *hb_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_ma_hardware_block_obj(hb_object, table_type,
+							   &ma->hardware_blocks[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_match_attributes(json_t *root,
+				enum cpfl_tdi_table_type table_type,
+				struct cpfl_tdi_match_attributes *ma)
+{
+	json_t *jobj = NULL;
+	int ret;
+
+	ret = cpfl_tdi_get_array_obj(root, "hardware_blocks", &jobj);
+	if (ret != 0)
+		return ret;
+
+	return cpfl_tdi_parse_ma_hardware_blocks(jobj, table_type, ma);
+}
+
+static int
+cpfl_tdi_parse_table_obj(json_t *root, struct cpfl_tdi_table *table)
+{
+	int ret, val = 0;
+	struct json_t *jobj = NULL;
+
+	ret = cpfl_tdi_parse_table_type(root, table);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_get_integer_obj(root, "handle", &val);
+	if (ret != 0)
+		return ret;
+	table->handle = (uint32_t)val;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", table->name);
+	if (ret != 0)
+		return ret;
+
+	if (table->table_type == CPFL_TDI_TABLE_TYPE_POLICER_METER) {
+		/* TODO */
+		return 0;
+	}
+
+	if (table->table_type == CPFL_TDI_TABLE_TYPE_MATCH) {
+		ret = cpfl_tdi_parse_table_dir(root, table);
+		if (ret != 0)
+			return ret;
+		ret = cpfl_tdi_get_boolean_obj(root, "add_on_miss", &table->add_on_miss);
+		if (ret != 0)
+			return ret;
+		ret = cpfl_tdi_get_boolean_obj(root, "idle_timeout_with_auto_delete",
+					       &table->idle_timeout_with_auto_delete);
+		if (ret != 0)
+			return ret;
+		ret = cpfl_tdi_get_integer_obj(root, "default_action_handle", &val);
+		if (ret != 0)
+			return ret;
+		table->default_action_handle = (uint32_t)val;
+		ret = cpfl_tdi_get_array_obj(root, "actions", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_actions(jobj, table);
+		if (ret != 0)
+			return ret;
+	} else if (table->table_type == CPFL_TDI_TABLE_TYPE_MATCH_VALUE_LOOKUP_TABLE) {
+		ret = cpfl_tdi_get_integer_obj(root, "size", &val);
+		if (ret != 0)
+			return ret;
+		table->size = (uint16_t)val;
+		ret = cpfl_tdi_get_boolean_obj(root, "p4_hidden", &table->p4_hidden);
+		if (ret != 0)
+			return ret;
+	}
+
+	ret = cpfl_tdi_get_array_obj(root, "match_key_fields", &jobj);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_match_key_fields(jobj, table);
+	if (ret != 0)
+		return ret;
+
+	if (json_object_get(root, "match_key_format") != NULL) {
+		ret = cpfl_tdi_get_array_obj(root, "match_key_format", &jobj);
+		if (ret != 0)
+			return ret;
+
+		ret = cpfl_tdi_parse_match_key_format_array(jobj, table);
+		if (ret != 0)
+			return ret;
+	}
+
+	ret = cpfl_tdi_get_object_obj(root, "match_attributes", &jobj);
+	if (ret != 0)
+		return ret;
+
+	ret = cpfl_tdi_parse_match_attributes(jobj, table->table_type, &table->match_attributes);
+	if (ret != 0)
+		return ret;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_tables(json_t *root, struct cpfl_tdi_program *prog)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	prog->table_num = (uint16_t)array_len;
+	prog->tables = rte_zmalloc(NULL, sizeof(struct cpfl_tdi_table) * array_len, 0);
+	if (prog->tables == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create table array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *table_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_table_obj(table_object, &prog->tables[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_hash_space_cfg(json_t *root, struct cpfl_tdi_hash_space_cfg *cfg)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_integer_obj(root, "base_128_entries", &val);
+	if (ret != 0)
+		return ret;
+
+	cfg->base_128_entries = (uint32_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "base_256_entries", &val);
+	if (ret != 0)
+		return ret;
+
+	cfg->base_256_entries = (uint32_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_rc_entry_space_cfg(json_t *root, struct cpfl_tdi_rc_entry_space_cfg *cfg)
+{
+	int ret, val = 0;
+
+	ret = cpfl_tdi_get_integer_obj(root, "rc_num_banks", &val);
+	if (ret != 0)
+		return ret;
+
+	cfg->rc_num_banks = (uint32_t)val;
+
+	ret = cpfl_tdi_get_integer_obj(root, "rc_num_entries", &val);
+	if (ret != 0)
+		return ret;
+
+	cfg->rc_num_entries = (uint32_t)val;
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_gc_hw_block(json_t *root, struct cpfl_tdi_gc_hardware_block *hb)
+{
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "name", name);
+	if (ret != 0)
+		return -EINVAL;
+
+	if (!strcmp(name, "SEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_SEM;
+	} else if (!strcmp(name, "LEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_LEM;
+	} else if (!strcmp(name, "WCM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_WCM;
+	} else if (!strcmp(name, "MOD")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_MOD;
+	} else if (!strcmp(name, "HASH")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_HASH;
+	} else if (!strcmp(name, "RC")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_RC;
+	} else if (!strcmp(name, "CXP_LEM")) {
+		hb->hw_block = CPFL_TDI_HW_BLOCK_CXP_LEM;
+	} else {
+		PMD_DRV_LOG(ERR, "Unknown hardware block type %s", name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_gc_hardware_block(json_t *root, struct cpfl_tdi_gc_hardware_block *hb)
+{
+	json_t *jobj = NULL;
+	int ret;
+
+	ret = cpfl_tdi_parse_gc_hw_block(root, hb);
+	if (ret != 0)
+		return ret;
+
+	switch (hb->hw_block) {
+	case CPFL_TDI_HW_BLOCK_MOD:
+		ret = cpfl_tdi_get_object_obj(root, "hash_space_cfg", &jobj);
+		if (ret != 0)
+			return ret;
+
+		return cpfl_tdi_parse_hash_space_cfg(jobj, &hb->hash_space_cfg);
+	case CPFL_TDI_HW_BLOCK_RC:
+		ret = cpfl_tdi_get_object_obj(root, "rc_entry_space_cfg", &jobj);
+		if (ret != 0)
+			return ret;
+		return cpfl_tdi_parse_rc_entry_space_cfg(jobj, &hb->rc_entry_space_cfg);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_gc_hardware_blocks(json_t *root, struct cpfl_tdi_global_configs *gc)
+{
+	int ret;
+	int array_len = json_array_size(root);
+
+	if (array_len == 0)
+		return 0;
+
+	gc->hardware_block_num = (uint16_t)array_len;
+	gc->hardware_blocks =
+	    rte_zmalloc(NULL, sizeof(struct cpfl_tdi_gc_hardware_block) * array_len, 0);
+	if (gc->hardware_blocks == NULL) {
+		PMD_DRV_LOG(ERR, "Failed to create hardware block array.");
+		return -ENOMEM;
+	}
+
+	for (int i = 0; i < array_len; i++) {
+		json_t *hb_object = json_array_get(root, i);
+
+		ret = cpfl_tdi_parse_gc_hardware_block(hb_object, &gc->hardware_blocks[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int
+cpfl_tdi_parse_global_configs(json_t *root, struct cpfl_tdi_global_configs *gc)
+{
+	json_t *jobj = NULL;
+	int ret;
+
+	ret = cpfl_tdi_get_array_obj(root, "hardware_blocks", &jobj);
+	if (ret != 0)
+		return ret;
+
+	return cpfl_tdi_parse_gc_hardware_blocks(jobj, gc);
+}
+
+int
+cpfl_tdi_program_create(json_t *root, struct cpfl_tdi_program *prog)
+{
+	json_t *jobj = NULL;
+	int ret;
+
+	ret = cpfl_tdi_get_string_obj(root, "program_name", prog->program_name);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_string_obj(root, "build_date", prog->build_date);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_string_obj(root, "compile_command", prog->compile_command);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_string_obj(root, "compiler_version", prog->compiler_version);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_string_obj(root, "schema_version", prog->schema_version);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_string_obj(root, "target", prog->target);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_object_obj(root, "global_configs", &jobj);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_parse_global_configs(jobj, &prog->global_configs);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_get_array_obj(root, "tables", &jobj);
+	if (ret != 0)
+		goto err;
+
+	ret = cpfl_tdi_parse_tables(jobj, prog);
+	if (ret != 0)
+		goto err;
+
+	json_decref(root);
+
+	return 0;
+
+err:
+	cpfl_tdi_program_destroy(prog);
+	return ret;
+}
+
+static void
+cpfl_tdi_destroy_hw_action(struct cpfl_tdi_hw_action *action)
+{
+	if (action->parameter_num > 0)
+		rte_free(action->parameters);
+}
+
+static void
+cpfl_tdi_cpfl_tdi_destroy_action_format(struct cpfl_tdi_action_format *format)
+{
+	uint16_t i;
+
+	if (format->immediate_field_num > 0)
+		rte_free(format->immediate_fields);
+
+	if (format->mod_content_format.mod_field_num > 0)
+		rte_free(format->mod_content_format.mod_fields);
+
+	for (i = 0; i < format->hw_action_num; i++)
+		cpfl_tdi_destroy_hw_action(&format->hw_actions_list[i]);
+
+	if (format->hw_action_num > 0)
+		rte_free(format->hw_actions_list);
+}
+
+static void
+cpfl_tdi_destroy_hardware_block(struct cpfl_tdi_ma_hardware_block *hb)
+{
+	uint16_t i;
+
+	for (i = 0; i < hb->action_format_num; i++)
+		cpfl_tdi_cpfl_tdi_destroy_action_format(&hb->action_format[i]);
+
+	if (hb->action_format_num > 0)
+		rte_free(hb->action_format);
+}
+
+static void
+cpfl_tdi_destroy_action(struct cpfl_tdi_action *action)
+{
+	if (action->p4_parameter_num > 0)
+		rte_free(action->p4_parameters);
+}
+
+static void
+cpfl_tdi_destroy_table(struct cpfl_tdi_table *table)
+{
+	uint16_t i;
+
+	if (table->match_key_field_num > 0)
+		rte_free(table->match_key_fields);
+
+	if (table->match_key_format_num > 0)
+		rte_free(table->match_key_format);
+
+	for (i = 0; i < table->action_num; i++)
+		cpfl_tdi_destroy_action(&table->actions[i]);
+
+	if (table->action_num > 0)
+		rte_free(table->actions);
+
+	for (i = 0; i < table->match_attributes.hardware_block_num; i++)
+		cpfl_tdi_destroy_hardware_block(&table->match_attributes.hardware_blocks[i]);
+
+	if (table->match_attributes.hardware_block_num > 0)
+		rte_free(table->match_attributes.hardware_blocks);
+}
+
+void
+cpfl_tdi_program_destroy(struct cpfl_tdi_program *program)
+{
+	uint16_t i;
+
+	for (i = 0; i < program->table_num; i++)
+		cpfl_tdi_destroy_table(&program->tables[i]);
+
+	if (program->table_num > 0)
+		rte_free(program->tables);
+
+	rte_free(program);
+}
diff --git a/drivers/net/cpfl/cpfl_tdi_parser.h b/drivers/net/cpfl/cpfl_tdi_parser.h
new file mode 100644
index 0000000000..9e036e762c
--- /dev/null
+++ b/drivers/net/cpfl/cpfl_tdi_parser.h
@@ -0,0 +1,294 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#ifndef _CPFL_TDI_PARSER_H_
+#define _CPFL_TDI_PARSER_H_
+
+#include <jansson.h>
+#include <rte_flow.h>
+
+#include "cpfl_ethdev.h"
+
+#define CPFL_TDI_JSON_STR_SIZE_MAX 100
+
+enum cpfl_tdi_table_type {
+	CPFL_TDI_TABLE_TYPE_MATCH,
+	CPFL_TDI_TABLE_TYPE_MATCH_VALUE_LOOKUP_TABLE,
+	CPFL_TDI_TABLE_TYPE_POLICER_METER,
+};
+
+enum cpfl_tdi_table_dir {
+	CPFL_TDI_TABLE_DIR_RX,
+	CPFL_TDI_TABLE_DIR_TX,
+	CPFL_TDI_TABLE_DIR_BI,
+};
+
+enum cpfl_tdi_match_type {
+	CPFL_TDI_MATCH_TYPE_EXACT,
+	CPFL_TDI_MATCH_TYPE_SELECTOR,
+	CPFL_TDI_MATCH_TYPE_TERNARY,
+	CPFL_TDI_MATCH_TYPE_LPM,
+};
+
+/**
+ * Byte order.
+ *
+ * To specify the byte order of table key / action field value in bytes.
+ */
+enum cpfl_tdi_byte_order {
+	CPFL_TDI_BYTE_ORDER_HOST,    /**< follow host byte order. */
+	CPFL_TDI_BYTE_ORDER_NETWORK, /**< follow network byte order. */
+};
+
+#define CPFL_TDI_NAME_SIZE_MAX 80
+
+struct cpfl_tdi_match_key_format {
+	uint32_t match_key_handle;
+	enum cpfl_tdi_byte_order byte_order;
+	uint16_t byte_array_index;
+	uint16_t start_bit_offset;
+	uint16_t bit_width;
+};
+
+struct cpfl_tdi_match_key_field {
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char instance_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char field_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	bool is_vsi;
+	enum cpfl_tdi_match_type match_type;
+	uint16_t bit_width;
+	uint32_t index;
+	uint32_t position;
+};
+
+struct cpfl_tdi_p4_parameter {
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint16_t bit_width;
+};
+
+struct cpfl_tdi_action {
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint32_t handle;
+	bool constant_default_action;
+	bool is_compiler_added_action;
+	bool allowed_as_hit_action;
+	bool allowed_as_default_action;
+	uint16_t p4_parameter_num;
+	struct cpfl_tdi_p4_parameter *p4_parameters;
+};
+
+struct cpfl_tdi_immediate_field {
+	char param_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint32_t param_handle;
+	uint16_t dest_start;
+	uint16_t start_bit_offset;
+	uint16_t dest_width;
+};
+
+enum cpfl_tdi_mod_field_type {
+	CPFL_TDI_MOD_FIELD_TYPE_PARAMETER,
+	CPFL_TDI_MOD_FIELD_TYPE_CONSTANT,
+};
+
+#define CPFL_TDI_VALUE_SIZE_MAX 16
+
+struct cpfl_tdi_mod_field {
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint32_t handle;
+	uint32_t param_handle;
+	enum cpfl_tdi_mod_field_type type;
+	enum cpfl_tdi_byte_order byte_order;
+	uint16_t byte_array_index;
+	uint16_t start_bit_offset;
+	uint16_t bit_width;
+	uint16_t value_size;
+	uint8_t value[CPFL_TDI_VALUE_SIZE_MAX];
+};
+
+struct cpfl_tdi_mod_content_format {
+	union {
+		uint16_t mod_profile;
+		uint16_t mod_lut_num;
+	};
+	uint16_t mod_obj_size;
+	uint16_t mod_field_num;
+	struct cpfl_tdi_mod_field *mod_fields;
+};
+
+struct cpfl_tdi_hw_action_parameter {
+	char param_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint32_t param_handle;
+};
+
+enum cpfl_tdi_action_code {
+	CPFL_TDI_ACTION_CODE_NONE,
+	CPFL_TDI_ACTION_CODE_SET10_1b,
+	CPFL_TDI_ACTION_CODE_SET1_16b,
+	CPFL_TDI_ACTION_CODE_SET1A_24b,
+	CPFL_TDI_ACTION_CODE_SET1B_24b,
+	CPFL_TDI_ACTION_CODE_SET2_8b,
+	CPFL_TDI_ACTION_CODE_NOP,
+	CPFL_TDI_ACTION_CODE_AUX_DATA,
+};
+
+enum cpfl_tdi_setmd_action_code {
+	CPFL_TDI_SETMD_ACTION_CODE_NONE,
+	CPFL_TDI_SETMD_ACTION_CODE_SET_8b,
+	CPFL_TDI_SETMD_ACTION_CODE_SET_16b,
+	CPFL_TDI_SETMD_ACTION_CODE_SET_32b_AUX,
+};
+
+struct cpfl_tdi_hw_action {
+	uint16_t prec;
+	enum cpfl_tdi_action_code action_code;
+	enum cpfl_tdi_setmd_action_code setmd_action_code;
+	uint16_t index;
+	uint16_t mod_profile;
+	uint16_t prefetch;
+	uint16_t parameter_num;
+	struct cpfl_tdi_hw_action_parameter *parameters;
+	uint32_t p4_ref_action_handle;
+	uint32_t p4_ref_table_handle;
+	uint16_t value;
+	uint16_t mask;
+	uint16_t type_id;
+	uint16_t offset;
+};
+
+struct cpfl_tdi_action_format {
+	char action_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint32_t action_handle;
+	uint16_t immediate_field_num;
+	struct cpfl_tdi_immediate_field *immediate_fields;
+	struct cpfl_tdi_mod_content_format mod_content_format;
+	uint16_t hw_action_num;
+	struct cpfl_tdi_hw_action *hw_actions_list;
+};
+
+enum cpfl_tdi_hw_block {
+	CPFL_TDI_HW_BLOCK_SEM,
+	CPFL_TDI_HW_BLOCK_LEM,
+	CPFL_TDI_HW_BLOCK_WCM,
+	CPFL_TDI_HW_BLOCK_LPM,
+	CPFL_TDI_HW_BLOCK_MOD,
+	CPFL_TDI_HW_BLOCK_METER,
+	CPFL_TDI_HW_BLOCK_HASH,
+	CPFL_TDI_HW_BLOCK_RC,
+	CPFL_TDI_HW_BLOCK_CXP_LEM,
+};
+
+struct cpfl_tdi_wcm_params {
+	uint16_t wcm_group;
+	uint16_t slice_start_idx;
+	uint16_t table_width;
+	uint16_t entry_cnt;
+	uint16_t entry_idx;
+	uint8_t act_rams[16];
+};
+
+struct cpfl_tdi_ma_hardware_block {
+	enum cpfl_tdi_hw_block hw_block;
+	uint32_t id;
+	char hw_interface[CPFL_TDI_JSON_STR_SIZE_MAX];
+	uint16_t profile_num;
+	uint16_t profile[16];
+	uint16_t action_format_num;
+	struct cpfl_tdi_action_format *action_format;
+	union {
+		struct {
+			uint16_t sub_profile;
+			uint32_t obj_id;
+		} sem;
+		struct {
+			struct cpfl_tdi_wcm_params wcm_params;
+		} wcm;
+		struct {
+			char hw_resource[CPFL_TDI_JSON_STR_SIZE_MAX];
+			uint32_t hw_resource_id;
+		} mod;
+		struct {
+			char hw_resource[CPFL_TDI_JSON_STR_SIZE_MAX];
+			uint32_t hw_resource_id;
+			uint16_t immediate_field_num;
+			struct cpfl_tdi_immediate_field *immediate_fields;
+		} meter;
+		struct {
+			uint16_t hash_size;
+		} lem;
+		struct {
+			uint16_t max_prefix_len;
+		} lpm;
+	};
+};
+
+struct cpfl_tdi_match_attributes {
+	uint16_t hardware_block_num;
+	struct cpfl_tdi_ma_hardware_block *hardware_blocks;
+};
+
+struct cpfl_tdi_table {
+	enum cpfl_tdi_table_type table_type;
+	uint32_t handle;
+	char name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	union {
+		/* "table_type": "match" */
+		struct {
+			enum cpfl_tdi_table_dir direction;
+			bool add_on_miss;
+			bool idle_timeout_with_auto_delete;
+		};
+		/* "table_type": "match_value_lookup_table" */
+		struct {
+			uint16_t size;
+			bool p4_hidden;
+		};
+	};
+	uint16_t match_key_field_num;
+	struct cpfl_tdi_match_key_field *match_key_fields;
+	uint16_t match_key_format_num;
+	struct cpfl_tdi_match_key_format *match_key_format;
+	uint32_t default_action_handle;
+	uint16_t action_num;
+	struct cpfl_tdi_action *actions;
+	struct cpfl_tdi_match_attributes match_attributes;
+};
+
+struct cpfl_tdi_hash_space_cfg {
+	uint32_t base_128_entries;
+	uint32_t base_256_entries;
+};
+
+struct cpfl_tdi_rc_entry_space_cfg {
+	uint32_t rc_num_banks;
+	uint32_t rc_num_entries;
+};
+
+struct cpfl_tdi_gc_hardware_block {
+	enum cpfl_tdi_hw_block hw_block;
+	union {
+		struct cpfl_tdi_hash_space_cfg hash_space_cfg;
+		struct cpfl_tdi_rc_entry_space_cfg rc_entry_space_cfg;
+	};
+};
+
+struct cpfl_tdi_global_configs {
+	uint16_t hardware_block_num;
+	struct cpfl_tdi_gc_hardware_block *hardware_blocks;
+};
+
+struct cpfl_tdi_program {
+	char program_name[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char build_date[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char compile_command[2 * CPFL_TDI_JSON_STR_SIZE_MAX];
+	char compiler_version[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char schema_version[CPFL_TDI_JSON_STR_SIZE_MAX];
+	char target[CPFL_TDI_JSON_STR_SIZE_MAX];
+	struct cpfl_tdi_global_configs global_configs;
+	uint16_t table_num;
+	struct cpfl_tdi_table *tables;
+};
+
+int cpfl_tdi_program_create(json_t *ob_root, struct cpfl_tdi_program *program);
+void cpfl_tdi_program_destroy(struct cpfl_tdi_program *program);
+
+#endif
diff --git a/drivers/net/cpfl/meson.build b/drivers/net/cpfl/meson.build
index e4e0e269bd..f948033f1f 100644
--- a/drivers/net/cpfl/meson.build
+++ b/drivers/net/cpfl/meson.build
@@ -47,6 +47,7 @@  if dpdk_conf.has('RTE_HAS_JANSSON')
             'cpfl_flow_engine_fxp.c',
             'cpfl_flow_parser.c',
             'cpfl_fxp_rule.c',
+            'cpfl_tdi_parser.c',
     )
     ext_deps += jansson_dep
 endif