11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2017 Oracle.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon  * Take a look at request_threaded_irq().  It takes thread_fn and dev_id.  Then
201f5207b7SJohn Levon  * it does:
211f5207b7SJohn Levon  *
221f5207b7SJohn Levon  *	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
231f5207b7SJohn Levon  *	action->thread_fn = thread_fn;
241f5207b7SJohn Levon  *	action->dev_id = dev_id;
251f5207b7SJohn Levon  *
261f5207b7SJohn Levon  * It doesn't ever pass action back to the higher levels, but instead registers
271f5207b7SJohn Levon  * it with the lower levels.
281f5207b7SJohn Levon  *
291f5207b7SJohn Levon  * The kzalloc() allocation creates a new mtag.  We don't know at this point
301f5207b7SJohn Levon  * what "thread_fn" and "dev_id" are because they come from many different
311f5207b7SJohn Levon  * sources.
321f5207b7SJohn Levon  *
331f5207b7SJohn Levon  * So what we do is we pass the information back to the callers that thread_fn
341f5207b7SJohn Levon  * and dev_id are stored as a specific mtag data.  Then when the callers *do*
351f5207b7SJohn Levon  * know what values are passed they create an mtag_alias.  An mtag_alias is a
361f5207b7SJohn Levon  * many to one relationship.  Then they store that in mtag_data using the
371f5207b7SJohn Levon  * mtag_alias.
381f5207b7SJohn Levon  *
391f5207b7SJohn Levon  */
401f5207b7SJohn Levon 
411f5207b7SJohn Levon #include "smatch.h"
421f5207b7SJohn Levon #include "smatch_extra.h"
431f5207b7SJohn Levon #include "smatch_slist.h"
441f5207b7SJohn Levon 
451f5207b7SJohn Levon static int my_id;
461f5207b7SJohn Levon 
471f5207b7SJohn Levon struct tag_assign_info {
481f5207b7SJohn Levon 	mtag_t tag;
491f5207b7SJohn Levon 	int offset;
501f5207b7SJohn Levon };
511f5207b7SJohn Levon ALLOCATOR(tag_assign_info, "tag name offset");
521f5207b7SJohn Levon 
alloc_tag_data_state(mtag_t tag,char * name,int offset)531f5207b7SJohn Levon static struct smatch_state *alloc_tag_data_state(mtag_t tag, char *name, int offset)
541f5207b7SJohn Levon {
551f5207b7SJohn Levon 	struct smatch_state *state;
561f5207b7SJohn Levon 	struct tag_assign_info *data;
571f5207b7SJohn Levon 
581f5207b7SJohn Levon 	data = __alloc_tag_assign_info(0);
591f5207b7SJohn Levon 	data->tag = tag;
601f5207b7SJohn Levon 	data->offset = offset;
611f5207b7SJohn Levon 
621f5207b7SJohn Levon 	state = __alloc_smatch_state(0);
631f5207b7SJohn Levon 	state->name = alloc_sname(name);
641f5207b7SJohn Levon 	state->data = data;
651f5207b7SJohn Levon 	return state;
661f5207b7SJohn Levon }
671f5207b7SJohn Levon 
merge_tag_info(struct smatch_state * s1,struct smatch_state * s2)681f5207b7SJohn Levon struct smatch_state *merge_tag_info(struct smatch_state *s1, struct smatch_state *s2)
691f5207b7SJohn Levon {
701f5207b7SJohn Levon 	/* Basically ignore undefined states */
711f5207b7SJohn Levon 	if (s1 == &undefined)
721f5207b7SJohn Levon 		return s2;
731f5207b7SJohn Levon 	if (s2 == &undefined)
741f5207b7SJohn Levon 		return s1;
751f5207b7SJohn Levon 
761f5207b7SJohn Levon 	return &merged;
771f5207b7SJohn Levon }
781f5207b7SJohn Levon 
match_assign(struct expression * expr)791f5207b7SJohn Levon static void match_assign(struct expression *expr)
801f5207b7SJohn Levon {
811f5207b7SJohn Levon 	struct expression *left;
821f5207b7SJohn Levon 	struct symbol *right_sym;
831f5207b7SJohn Levon 	char *name;
841f5207b7SJohn Levon 	mtag_t tag;
851f5207b7SJohn Levon 	int offset;
861f5207b7SJohn Levon 	int param;
871f5207b7SJohn Levon 
881f5207b7SJohn Levon 	if (expr->op != '=')
891f5207b7SJohn Levon 		return;
901f5207b7SJohn Levon 	left = strip_expr(expr->left);
91c85f09ccSJohn Levon 	if (is_local_variable(left))
92efe51d0cSJohn Levon 		return;
931f5207b7SJohn Levon 	right_sym = expr_to_sym(expr->right);
941f5207b7SJohn Levon 	if (!right_sym)
951f5207b7SJohn Levon 		return;
961f5207b7SJohn Levon 
971f5207b7SJohn Levon 	param = get_param_num_from_sym(right_sym);
981f5207b7SJohn Levon 	if (param < 0)
991f5207b7SJohn Levon 		return;
1001f5207b7SJohn Levon 	// FIXME:  modify param_has_filter_data() to take a name/sym
1011f5207b7SJohn Levon 	if (!expr_to_mtag_offset(left, &tag, &offset))
1021f5207b7SJohn Levon 		return;
1031f5207b7SJohn Levon 	name = expr_to_str(left);
1041f5207b7SJohn Levon 	if (!name)
1051f5207b7SJohn Levon 		return;
1061f5207b7SJohn Levon 	set_state_expr(my_id, expr->right, alloc_tag_data_state(tag, name, offset));
1071f5207b7SJohn Levon 	free_string(name);
1081f5207b7SJohn Levon }
1091f5207b7SJohn Levon 
propogate_assignment(struct expression * expr,mtag_t tag,int offset,int param,char * key)1101f5207b7SJohn Levon static void propogate_assignment(struct expression *expr, mtag_t tag, int offset, int param, char *key)
1111f5207b7SJohn Levon {
1121f5207b7SJohn Levon 	struct expression *arg;
1131f5207b7SJohn Levon 	int orig_param;
1141f5207b7SJohn Levon 	char buf[32];
1151f5207b7SJohn Levon 	char *name;
1161f5207b7SJohn Levon 	struct symbol *sym;
1171f5207b7SJohn Levon 
1181f5207b7SJohn Levon 	arg = get_argument_from_call_expr(expr->args, param);
1191f5207b7SJohn Levon 	if (!arg)
1201f5207b7SJohn Levon 		return;
1211f5207b7SJohn Levon 	name = get_variable_from_key(arg, key, &sym);
1221f5207b7SJohn Levon 	if (!name || !sym)
1231f5207b7SJohn Levon 		goto free;
1241f5207b7SJohn Levon 
1251f5207b7SJohn Levon 	orig_param = get_param_num_from_sym(sym);
1261f5207b7SJohn Levon 	if (orig_param < 0)
1271f5207b7SJohn Levon 		goto free;
1281f5207b7SJohn Levon 
1291f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "$->[%d]", offset);
1301f5207b7SJohn Levon 	set_state(my_id, name, sym, alloc_tag_data_state(tag, buf, offset));
1311f5207b7SJohn Levon free:
1321f5207b7SJohn Levon 	free_string(name);
1331f5207b7SJohn Levon }
1341f5207b7SJohn Levon 
assign_to_alias(struct expression * expr,int param,mtag_t tag,int offset,char * key)1351f5207b7SJohn Levon static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int offset, char *key)
1361f5207b7SJohn Levon {
1371f5207b7SJohn Levon 	struct expression *arg, *gen_expr;
1381f5207b7SJohn Levon 	struct range_list *rl;
1391f5207b7SJohn Levon 	mtag_t arg_tag;
1401f5207b7SJohn Levon 	mtag_t alias;
141efe51d0cSJohn Levon 	int arg_offset;
1421f5207b7SJohn Levon 
1431f5207b7SJohn Levon 	arg = get_argument_from_call_expr(expr->args, param);
1441f5207b7SJohn Levon 	if (!arg)
1451f5207b7SJohn Levon 		return;
1461f5207b7SJohn Levon 
1471f5207b7SJohn Levon 	gen_expr = gen_expression_from_key(arg, key);
1481f5207b7SJohn Levon 	if (!gen_expr)
1491f5207b7SJohn Levon 		return;
1501f5207b7SJohn Levon 
1511f5207b7SJohn Levon 	get_absolute_rl(gen_expr, &rl);
1521f5207b7SJohn Levon 
1531f5207b7SJohn Levon 	if (!create_mtag_alias(tag, expr, &alias))
1541f5207b7SJohn Levon 		return;
1551f5207b7SJohn Levon 
1561f5207b7SJohn Levon //	insert_mtag_data(alias, offset, rl);
1571f5207b7SJohn Levon 
158efe51d0cSJohn Levon 	// FIXME:  is arg_offset handled correctly?
159*6523a3aaSJohn Levon 	if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) &&
160*6523a3aaSJohn Levon 	    arg_offset < MTAG_OFFSET_MASK)
161*6523a3aaSJohn Levon 		sql_insert_mtag_map(alias, offset, arg_tag, arg_offset);
1621f5207b7SJohn Levon }
1631f5207b7SJohn Levon 
call_does_mtag_assign(struct expression * expr,int param,char * key,char * value)1641f5207b7SJohn Levon static void call_does_mtag_assign(struct expression *expr, int param, char *key, char *value)
1651f5207b7SJohn Levon {
1661f5207b7SJohn Levon 	char *p;
1671f5207b7SJohn Levon 	mtag_t tag;
1681f5207b7SJohn Levon 	int offset;
1691f5207b7SJohn Levon 
1701f5207b7SJohn Levon 	while (expr->type == EXPR_ASSIGNMENT)
1711f5207b7SJohn Levon 		expr = strip_expr(expr->right);
1721f5207b7SJohn Levon 	if (expr->type != EXPR_CALL)
1731f5207b7SJohn Levon 		return;
1741f5207b7SJohn Levon 
1751f5207b7SJohn Levon 	tag = strtoul(value, NULL, 10);
1761f5207b7SJohn Levon 	p = strchr(value, '+');
1771f5207b7SJohn Levon 	if (!p)
1781f5207b7SJohn Levon 		return;
1791f5207b7SJohn Levon 	offset = atoi(p + 1);
1801f5207b7SJohn Levon 
1811f5207b7SJohn Levon //	save_mtag_to_map(expr, tag, offset, param, key, value);
1821f5207b7SJohn Levon 	propogate_assignment(expr, tag, offset, param, key);
1831f5207b7SJohn Levon 	assign_to_alias(expr, param, tag, offset, key);
1841f5207b7SJohn Levon }
1851f5207b7SJohn Levon 
print_stored_to_mtag(int return_id,char * return_ranges,struct expression * expr)1861f5207b7SJohn Levon static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
1871f5207b7SJohn Levon {
1881f5207b7SJohn Levon 	struct sm_state *sm;
1891f5207b7SJohn Levon 	struct tag_assign_info *data;
1901f5207b7SJohn Levon 	char buf[256];
1911f5207b7SJohn Levon 	const char *param_name;
1921f5207b7SJohn Levon 	int param;
1931f5207b7SJohn Levon 
1941f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
1951f5207b7SJohn Levon 		if (!sm->state->data)
1961f5207b7SJohn Levon 			continue;
1971f5207b7SJohn Levon 
1981f5207b7SJohn Levon 		param = get_param_num_from_sym(sm->sym);
1991f5207b7SJohn Levon 		if (param < 0)
2001f5207b7SJohn Levon 			continue;
2011f5207b7SJohn Levon 		param_name = get_param_name(sm);
2021f5207b7SJohn Levon 		if (!param_name)
2031f5207b7SJohn Levon 			continue;
2041f5207b7SJohn Levon 
2051f5207b7SJohn Levon 		data = sm->state->data;
2061f5207b7SJohn Levon 		snprintf(buf, sizeof(buf), "%lld+%d", data->tag, data->offset);
2071f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges, MTAG_ASSIGN, param, param_name, buf);
2081f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
2091f5207b7SJohn Levon }
2101f5207b7SJohn Levon 
register_param_to_mtag_data(int id)2111f5207b7SJohn Levon void register_param_to_mtag_data(int id)
2121f5207b7SJohn Levon {
2131f5207b7SJohn Levon 	my_id = id;
2141f5207b7SJohn Levon 
215efe51d0cSJohn Levon 	set_dynamic_states(my_id);
2161f5207b7SJohn Levon 	add_hook(&match_assign, ASSIGNMENT_HOOK);
2171f5207b7SJohn Levon 	select_return_states_hook(MTAG_ASSIGN, &call_does_mtag_assign);
2181f5207b7SJohn Levon 	add_merge_hook(my_id, &merge_tag_info);
2191f5207b7SJohn Levon 	add_split_return_callback(&print_stored_to_mtag);
2201f5207b7SJohn Levon }
2211f5207b7SJohn Levon 
222