Blob Blame History Raw
/*
 * erase_info.c
 *
 * Created by: Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com>
 *
 * Copyright (C) 2011  IBM Corporation
 * Copyright (C) 2011  NEC Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include "makedumpfile.h"
#include "print_info.h"
#include "dwarf_info.h"
#include "erase_info.h"

#include <dlfcn.h>

struct erase_info	*erase_info = NULL;
unsigned long		num_erase_info = 1; /* Node 0 is unused. */

struct call_back eppic_cb = {
	&get_domain_all,
	&readmem,
	&get_die_attr_type,
	&get_die_name,
	&get_die_offset,
	&get_die_length,
	&get_die_member_all,
	&get_die_nfields_all,
	&get_symbol_addr_all,
	&update_filter_info_raw
};


/*
 * flags for config_entry.flag
 */ 
#define FILTER_ENTRY		0x0001
#define SIZE_ENTRY		0x0002
#define ITERATION_ENTRY		0x0004
#define LIST_ENTRY		0x0008
#define SYMBOL_ENTRY		0x0010
#define VAR_ENTRY		0x0020
#define TRAVERSAL_ENTRY		0x0040
#define ENTRY_RESOLVED		0x8000

/*
 * flags for get_config()
 */ 
#define CONFIG_SKIP_SECTION	0x01
#define CONFIG_NEW_CMD		0x02

#define IS_KEYWORD(tkn)	\
	(!strcmp(tkn, "erase") || !strcmp(tkn, "size") || \
	!strcmp(tkn, "nullify") || !strcmp(tkn, "for") || \
	!strcmp(tkn, "in") || !strcmp(tkn, "within") || \
	!strcmp(tkn, "endfor"))

struct module_sym_table {
	unsigned int		num_modules;
	unsigned int		current_mod;
	struct module_info	*modules;
};

/*
 * Filtering physical address range.
 */
struct filter_info {
	unsigned long long      vaddr; /* symbol address for debugging */
	unsigned long long      paddr;
	long			size;

	/* direct access to update erase information node */
	int			erase_info_idx;	/* 0= invalid index */
	int			size_idx;

	int			erase_ch;

	struct filter_info      *next;
	unsigned short          nullify;
};

/*
 * Filter config information
 */
struct filter_config {
	char		*name_filterconfig;
	FILE		*file_filterconfig;
	char		*cur_module;
	char		*saved_token;
	char		*token;
	int		new_section;
	int		line_count;
};

struct config_entry {
	char			*name;
	char			*type_name;
	char			*symbol_expr;	/* original symbol expression */
	unsigned short		flag;
	unsigned short		nullify;
	unsigned long long	sym_addr;	/* Symbol address */
	unsigned long		vaddr;		/* Symbol address or
						   value pointed by sym_addr */
	unsigned long long	cmp_addr;	/* for LIST_ENTRY */
	unsigned long		offset;
	unsigned long		type_flag;
	long			array_length;
	long			index;
	long			size;
	int			line;	/* Line number in config file. */
	int			erase_info_idx;	/* 0= invalid index */
	struct config_entry	*refer_to;
	struct config_entry	*next;
};

struct config {
	char			*module_name;
	struct config_entry	*iter_entry;
	struct config_entry	*list_entry;
	int			num_filter_symbols;
	struct config_entry	**filter_symbol;
	struct config_entry	**size_symbol;
};

static struct module_sym_table	mod_st = { 0 };
static struct filter_info	*filter_info = NULL;
static struct filter_config	filter_config;
static char			config_buf[BUFSIZE_FGETS];


/*
 * Internal functions.
 */
static struct module_info *
get_loaded_module(char *mod_name)
{
	unsigned int i;
	struct module_info *modules;

	modules = mod_st.modules;
	if (strcmp(mod_name, modules[mod_st.current_mod].name)) {
		for (i = 0; i < mod_st.num_modules; i++) {
			if (!strcmp(mod_name, modules[i].name))
				break;
		}
		if (i == mod_st.num_modules)
			return NULL;
		/* set the current_mod for fast lookup next time */
		mod_st.current_mod = i;
	}

	return &modules[mod_st.current_mod];
}

static unsigned long long
find_module_symbol(struct module_info *module_ptr, char *symname)
{
	int i;
	struct symbol_info *sym_info;

	sym_info = module_ptr->sym_info;
	if (!sym_info)
		return FALSE;
	for (i = 1; i < module_ptr->num_syms; i++) {
		if (sym_info[i].name && !strcmp(sym_info[i].name, symname))
			return sym_info[i].value;
	}
	return NOT_FOUND_SYMBOL;
}

static int
sym_in_module(char *symname, unsigned long long *symbol_addr)
{
	char *module_name;
	struct module_info *module_ptr;

	module_name = get_dwarf_module_name();
	if (!mod_st.num_modules
		|| !strcmp(module_name, "vmlinux")
		|| !strcmp(module_name, "xen-syms"))
		return FALSE;

	module_ptr = get_loaded_module(module_name);
	if (!module_ptr)
		return FALSE;
	*symbol_addr = find_module_symbol(module_ptr, symname);
	if (*symbol_addr == NOT_FOUND_SYMBOL)
		return FALSE;
	else
		return TRUE;
}

static unsigned int
get_num_modules(unsigned long head, unsigned int *num)
{
	unsigned long cur;
	unsigned int num_modules = 0;

	if (!num)
		return FALSE;

	if (!readmem(VADDR, head + OFFSET(list_head.next), &cur, sizeof cur)) {
		ERRMSG("Can't get next list_head.\n");
		return FALSE;
	}
	while (cur != head) {
		num_modules++;
		if (!readmem(VADDR, cur + OFFSET(list_head.next),
					&cur, sizeof cur)) {
			ERRMSG("Can't get next list_head.\n");
			return FALSE;
		}
	}
	*num = num_modules;
	return TRUE;
}

static void
free_symbol_info(struct module_info *module)
{
	int i;

	if (module->num_syms == 0)
		return;

	for (i = 1; i < module->num_syms; i++)
		if (module->sym_info[i].name)
			free(module->sym_info[i].name);
	free(module->sym_info);
}

static void
clean_module_symbols(void)
{
	int i;

	for (i = 0; i < mod_st.num_modules; i++)
		free_symbol_info(&mod_st.modules[i]);

	if (mod_st.num_modules) {
		free(mod_st.modules);
		mod_st.modules     = NULL;
		mod_st.num_modules = 0;
	}
}

static int
__load_module_symbol(struct module_info *modules, unsigned long addr_module)
{
	int ret = FALSE;
	unsigned int nsym;
	unsigned long symtab, strtab;
	unsigned long mod_base, mod_init;
	unsigned int mod_size, mod_init_size;
	unsigned char *module_struct_mem = NULL;
	unsigned char *module_core_mem = NULL;
	unsigned char *module_init_mem = NULL;
	unsigned char *symtab_mem;
	char *module_name, *strtab_mem, *nameptr;
	unsigned int num_symtab;

	/* Allocate buffer to read struct module data from vmcore. */
	if ((module_struct_mem = calloc(1, SIZE(module))) == NULL) {
		ERRMSG("Failed to allocate buffer for module\n");
		return FALSE;
	}
	if (!readmem(VADDR, addr_module, module_struct_mem,
						SIZE(module))) {
		ERRMSG("Can't get module info.\n");
		goto out;
	}

	module_name = (char *)(module_struct_mem + OFFSET(module.name));
	if (strlen(module_name) < MOD_NAME_LEN)
		strcpy(modules->name, module_name);
	else
		strncpy(modules->name, module_name, MOD_NAME_LEN-1);

	mod_init = ULONG(module_struct_mem +
					OFFSET(module.module_init));
	mod_init_size = UINT(module_struct_mem +
					OFFSET(module.init_size));
	mod_base = ULONG(module_struct_mem +
					OFFSET(module.module_core));
	mod_size = UINT(module_struct_mem +
					OFFSET(module.core_size));

	DEBUG_MSG("Module: %s, Base: 0x%lx, Size: %u\n",
			module_name, mod_base, mod_size);
	if (mod_init_size > 0) {
		module_init_mem = calloc(1, mod_init_size);
		if (module_init_mem == NULL) {
			ERRMSG("Can't allocate memory for module "
							"init\n");
			goto out;
		}
		if (!readmem(VADDR, mod_init, module_init_mem,
						mod_init_size)) {
			ERRMSG("Can't access module init in memory.\n");
			goto out;
		}
	}

	if ((module_core_mem = calloc(1, mod_size)) == NULL) {
		ERRMSG("Can't allocate memory for module\n");
		goto out;
	}
	if (!readmem(VADDR, mod_base, module_core_mem, mod_size)) {
		ERRMSG("Can't access module in memory.\n");
		goto out;
	}

	num_symtab = UINT(module_struct_mem +
					OFFSET(module.num_symtab));
	if (!num_symtab) {
		ERRMSG("%s: Symbol info not available\n", module_name);
		goto out;
	}
	modules->num_syms = num_symtab;
	DEBUG_MSG("num_sym: %d\n", num_symtab);

	symtab = ULONG(module_struct_mem + OFFSET(module.symtab));
	strtab = ULONG(module_struct_mem + OFFSET(module.strtab));

	/* check if symtab and strtab are inside the module space. */
	if (!IN_RANGE(symtab, mod_base, mod_size) &&
		!IN_RANGE(symtab, mod_init, mod_init_size)) {
		ERRMSG("%s: module symtab is outside of module "
			"address space\n", module_name);
		goto out;
	}
	if (IN_RANGE(symtab, mod_base, mod_size))
		symtab_mem = module_core_mem + (symtab - mod_base);
	else
		symtab_mem = module_init_mem + (symtab - mod_init);

	if (!IN_RANGE(strtab, mod_base, mod_size) &&
		!IN_RANGE(strtab, mod_init, mod_init_size)) {
		ERRMSG("%s: module strtab is outside of module "
			"address space\n", module_name);
		goto out;
	}
	if (IN_RANGE(strtab, mod_base, mod_size))
		strtab_mem = (char *)(module_core_mem
					+ (strtab - mod_base));
	else
		strtab_mem = (char *)(module_init_mem
					+ (strtab - mod_init));

	modules->sym_info = calloc(num_symtab, sizeof(struct symbol_info));
	if (modules->sym_info == NULL) {
		ERRMSG("Can't allocate memory to store sym info\n");
		goto out;
	}

	/* symbols starts from 1 */
	for (nsym = 1; nsym < num_symtab; nsym++) {
		Elf32_Sym *sym32;
		Elf64_Sym *sym64;
		/* 
		 * TODO:
		 * If case of ELF vmcore then the word size can be
		 * determined using flag_elf64_memory flag.
		 * But in case of kdump-compressed dump, kdump header
		 * does not carry word size info. May be in future
		 * this info will be available in kdump header.
		 * Until then, in order to make this logic work on both
		 * situation we depend on pointer_size that is
		 * extracted from vmlinux dwarf information.
		 */
		if ((get_pointer_size() * 8) == 64) {
			sym64 = (Elf64_Sym *) (symtab_mem
					+ (nsym * sizeof(Elf64_Sym)));
			modules->sym_info[nsym].value =
				(unsigned long long) sym64->st_value;
			nameptr = strtab_mem + sym64->st_name;
		} else {
			sym32 = (Elf32_Sym *) (symtab_mem
					+ (nsym * sizeof(Elf32_Sym)));
			modules->sym_info[nsym].value =
				(unsigned long long) sym32->st_value;
			nameptr = strtab_mem + sym32->st_name;
		}
		if (strlen(nameptr))
			modules->sym_info[nsym].name = strdup(nameptr);
		DEBUG_MSG("\t[%d] %llx %s\n", nsym,
					modules->sym_info[nsym].value, nameptr);
	}
	ret = TRUE;
out:
	free(module_struct_mem);
	free(module_core_mem);
	free(module_init_mem);

	return ret;
}

static int
load_module_symbols(void)
{
	unsigned long head, cur, cur_module;
	struct module_info *modules = NULL;
	unsigned int i = 0;

	head = SYMBOL(modules);
	if (!get_num_modules(head, &mod_st.num_modules) ||
	    !mod_st.num_modules) {
		ERRMSG("Can't get module count\n");
		return FALSE;
	}
	mod_st.modules = calloc(mod_st.num_modules,
					sizeof(struct module_info));
	if (!mod_st.modules) {
		ERRMSG("Can't allocate memory for module info\n");
		return FALSE;
	}
	modules = mod_st.modules;

	if (!readmem(VADDR, head + OFFSET(list_head.next), &cur, sizeof cur)) {
		ERRMSG("Can't get next list_head.\n");
		return FALSE;
	}

	/* Travese the list and read module symbols */
	while (cur != head) {
		cur_module = cur - OFFSET(module.list);

		if (!__load_module_symbol(&modules[i], cur_module))
			return FALSE;

		if (!readmem(VADDR, cur + OFFSET(list_head.next),
					&cur, sizeof cur)) {
			ERRMSG("Can't get next list_head.\n");
			return FALSE;
		}
		i++;
	}
	return TRUE;
}

static void
free_config_entry(struct config_entry *ce)
{
	struct config_entry *p;

	while(ce) {
		p  = ce;
		ce = p->next;
		if (p->name)
			free(p->name);
		if (p->type_name)
			free(p->type_name);
		if (p->symbol_expr)
			free(p->symbol_expr);
		free(p);
	}
}

static void
free_config(struct config *config)
{
	int i;

	if (config == NULL)
		return;

	if (config->module_name)
		free(config->module_name);
	for (i = 0; i < config->num_filter_symbols; i++) {
		if (config->filter_symbol[i])
			free_config_entry(config->filter_symbol[i]);
		if (config->size_symbol[i])
			free_config_entry(config->size_symbol[i]);
	}
	if (config->filter_symbol)
		free(config->filter_symbol);
	if (config->size_symbol)
		free(config->size_symbol);
	free(config);
}

static void
print_config_entry(struct config_entry *ce)
{
	while (ce) {
		DEBUG_MSG("Name: %s\n", ce->name);
		DEBUG_MSG("Type Name: %s, ", ce->type_name);
		DEBUG_MSG("flag: %x, ", ce->flag);
		DEBUG_MSG("Type flag: %lx, ", ce->type_flag);
		DEBUG_MSG("sym_addr: %llx, ", ce->sym_addr);
		DEBUG_MSG("vaddr: %lx, ", ce->vaddr);
		DEBUG_MSG("offset: %llx, ", (unsigned long long)ce->offset);
		DEBUG_MSG("size: %ld\n", ce->size);

		ce = ce->next;
	}
}

/*
 * Read the non-terminal's which are in the form of <Symbol>[.member[...]]
 */
static struct config_entry *
create_config_entry(const char *token, unsigned short flag, int line)
{
	struct config_entry *ce = NULL, *ptr, *prev_ce;
	char *str, *cur, *next;
	long len;
	int depth = 0;

	if (!token)
		return NULL;

	cur = str = strdup(token);
	prev_ce = ptr = NULL;
	while (cur != NULL) {
		if ((next = strchr(cur, '.')) != NULL) {
			*next++ = '\0';
		}
		if (!strlen(cur)) {
			cur = next;
			continue;
		}

		if ((ptr = calloc(1, sizeof(struct config_entry))) == NULL) {
			ERRMSG("Can't allocate memory for config_entry\n");
			goto err_out;
		}
		ptr->line = line;
		ptr->flag |= flag;
		if (depth == 0) {
			/* First node is always a symbol name */
			ptr->flag |= SYMBOL_ENTRY;
		}
		if (flag & ITERATION_ENTRY) {
			/* Max depth for iteration entry is 1 */
			if (depth > 0) {
				ERRMSG("Config error at %d: Invalid iteration "
					"variable entry.\n", line);
				goto err_out;
			}
			ptr->name = strdup(cur);
		}
		if (flag & (FILTER_ENTRY | LIST_ENTRY)) {
			ptr->name = strdup(cur);
		}
		if (flag & SIZE_ENTRY) {
			char ch = '\0';
			int n = 0;
			/* See if absolute length is provided */
			if ((depth == 0) &&
				((n = sscanf(cur, "%ld%c", &len, &ch)) > 0)) {
				if (len < 0) {
					ERRMSG("Config error at %d: size "
						"value must be positive.\n",
						line);
					goto err_out;
				}
				ptr->size = len;
				ptr->flag |= ENTRY_RESOLVED;
				if (n == 2) {
					/* Handle suffix.
					 * K = Kilobytes
					 * M = Megabytes
					 */
					switch (ch) {
					case 'M':
					case 'm':
						ptr->size *= 1024;
					case 'K':
					case 'k':
						ptr->size *= 1024;
						break;
					}
				}
			}
			else
				ptr->name = strdup(cur);
		}
		if (prev_ce) {
			prev_ce->next = ptr;
			prev_ce       = ptr;
		} else
			ce = prev_ce = ptr;

		cur = next;
		ptr = NULL;
		depth++;
	}
	free(str);
	return ce;

err_out:
	if (ce)
		free_config_entry(ce);
	if (ptr)
		free_config_entry(ptr);
	free(str);
	return NULL;
}

static int
is_module_loaded(char *mod_name)
{
	if (!strcmp(mod_name, "vmlinux") || get_loaded_module(mod_name))
		return TRUE;
	return FALSE;
}

/*
 * read filter config file and return each string token. If the parameter
 * expected_token is non-NULL, then return the current token if it matches
 * with expected_token otherwise save the current token and return NULL.
 * At start of every module section filter_config.new_section is set to 1 and
 * subsequent function invocations return NULL untill filter_config.new_section
 * is reset to 0 by passing @flag = CONFIG_NEW_CMD (0x02).
 *
 * Parameters:
 * @expected_token	INPUT
 *	Token string to match with currnet token.
 *	=NULL - return the current available token.
 *
 * @flag		INPUT
 *	=0x01 - Skip to next module section.
 *	=0x02 - Treat the next token as next filter command and reset.
 *
 * @line		OUTPUT
 *	Line number of current token in filter config file.
 *
 * @cur_mod		OUTPUT
 *	Points to current module section name on non-NULL return value.
 *
 * @eof			OUTPUT
 *	set to -1 when end of file is reached.
 *	set to -2 when end of section is reached.
 */
#define NOT_REACH_END		(0)
#define REACH_END_OF_FILE	(-1)
#define REACH_END_OF_SECTION	(-2)

static char *
get_config_token(char *expected_token, unsigned char flag, int *line,
			char **cur_mod, int *eof)
{
	char *p;
	struct filter_config *fc = &filter_config;
	int skip = flag & CONFIG_SKIP_SECTION;

	if (!fc->file_filterconfig)
		return NULL;

	if (eof)
		*eof = NOT_REACH_END;

	/*
	 * set token and saved_token to NULL if skip module section is set
	 * to 1.
	 */
	if (skip) {
		fc->token       = NULL;
		fc->saved_token = NULL;

	} else if (fc->saved_token) {
		fc->token       = fc->saved_token;
		fc->saved_token = NULL;

	} else if (fc->token)
		fc->token       = strtok(NULL, " ");

	/* Read next line if we are done all tokens from previous line */
	while (!fc->token && fgets(config_buf, sizeof(config_buf),
					fc->file_filterconfig)) {
		if ((p = strchr(config_buf, '\n'))) {
			*p = '\0';
			fc->line_count++;
		}
		if ((p = strchr(config_buf, '#'))) {
			*p = '\0';
		}
		/* replace all tabs with spaces */
		for (p = config_buf; *p != '\0'; p++)
			if (*p == '\t')
				*p = ' ';
		if (config_buf[0] == '[') {
			/* module section entry */
			p = strchr(config_buf, ']');
			if (!p) {
				ERRMSG("Config error at %d: Invalid module "
					"section entry.\n", fc->line_count);
				/* skip to next valid module section */
				skip = 1;
			} else {
				/*
				 * Found the valid module section. Reset the
				 * skip flag.
				 */
				*p = '\0';
				if (fc->cur_module)
					free(fc->cur_module);
				fc->cur_module  = strdup(&config_buf[1]);
				fc->new_section = 1;
				skip = 0;
			}
			continue;
		}
		/*
		 * If symbol info for current module is not loaded then
		 * skip to next module section.
		 */
		if (skip ||
			(fc->cur_module && !is_module_loaded(fc->cur_module)))
			continue;

		fc->token = strtok(config_buf, " ");
	}
	if (!fc->token) {
		if (eof)
			*eof = REACH_END_OF_FILE;
		return NULL;
	}
	if (fc->new_section && !(flag & CONFIG_NEW_CMD)) {
		fc->saved_token = fc->token;
		if (eof)
			*eof = REACH_END_OF_SECTION;
		return NULL;
	}

	fc->new_section = 0;

	if (cur_mod)
		*cur_mod = fc->cur_module;

	if (line)
		*line = fc->line_count;

	if (expected_token && strcmp(fc->token, expected_token)) {
		fc->saved_token = fc->token;
		return NULL;
	}
	return fc->token;
}

static int
read_size_entry(struct config *config, int line, int idx)
{
	char *token = get_config_token(NULL, 0, &line, NULL, NULL);

	if (!token || IS_KEYWORD(token)) {
		ERRMSG("Config error at %d: expected size symbol after"
		" 'size' keyword.\n", line);
		return FALSE;
	}
	config->size_symbol[idx] = create_config_entry(token, SIZE_ENTRY, line);
	if (!config->size_symbol[idx]) {
		ERRMSG("Error at line %d: Failed to read size symbol\n",
									line);
		return FALSE;
	}
	if (config->iter_entry && config->size_symbol[idx]->name &&
					(!strcmp(config->size_symbol[idx]->name,
					config->iter_entry->name))) {
		config->size_symbol[idx]->flag &= ~SYMBOL_ENTRY;
		config->size_symbol[idx]->flag |= VAR_ENTRY;
		config->size_symbol[idx]->refer_to = config->iter_entry;
	}
	return TRUE;
}

/*
 * Read erase command entry. The erase command syntax is:
 *
 *	erase <Symbol>[.member[...]] [size <SizeValue>[K|M]]
 *	erase <Symbol>[.member[...]] [size <SizeSymbol>]
 *	erase <Symbol>[.member[...]] [nullify]
 */
static int
read_erase_cmd_entry(struct config *config, int line)
{
	int size, idx;
	char *token = get_config_token(NULL, 0, &line, NULL, NULL);

	if (!token || IS_KEYWORD(token)) {
		ERRMSG("Config error at %d: expected kernel symbol after"
		" 'erase' command.\n", line);
		return FALSE;
	}

	idx = config->num_filter_symbols;
	config->num_filter_symbols++;
	size = config->num_filter_symbols * sizeof(struct config_entry *);
	config->filter_symbol = realloc(config->filter_symbol, size);
	config->size_symbol   = realloc(config->size_symbol, size);

	if (!config->filter_symbol || !config->size_symbol) {
		ERRMSG("Can't get memory to read config symbols.\n");
		return FALSE;
	}
	config->filter_symbol[idx] = NULL;
	config->size_symbol[idx]   = NULL;

	config->filter_symbol[idx] =
			create_config_entry(token, FILTER_ENTRY, line);
	if (!config->filter_symbol[idx]) {
		ERRMSG("Error at line %d: Failed to read filter symbol\n",
									line);
		return FALSE;
	}

	/*
	 * Save the symbol expression string for generation of eraseinfo data
	 * later while writing dumpfile.
	 */
	config->filter_symbol[idx]->symbol_expr = strdup(token);

	if (config->iter_entry) {
		if (strcmp(config->filter_symbol[idx]->name,
				config->iter_entry->name)) {
			ERRMSG("Config error at %d: unused iteration"
				" variable '%s'.\n", line,
				config->iter_entry->name);
			return FALSE;
		}
		config->filter_symbol[idx]->flag &= ~SYMBOL_ENTRY;
		config->filter_symbol[idx]->flag |= VAR_ENTRY;
		config->filter_symbol[idx]->refer_to = config->iter_entry;
	}
	if (get_config_token("nullify", 0, &line, NULL, NULL)) {
		config->filter_symbol[idx]->nullify = 1;

	} else if (get_config_token("size", 0, &line, NULL, NULL)) {
		if (!read_size_entry(config, line, idx))
			return FALSE;
	}
	return TRUE;
}

static int
add_traversal_entry(struct config_entry *ce, char *member, int line)
{
	if (!ce)
		return FALSE;

	while (ce->next)
		ce = ce->next;

	ce->next = create_config_entry(member, LIST_ENTRY, line);
	if (ce->next == NULL) {
		ERRMSG("Error at line %d: Failed to read 'via' member\n",
									line);
		return FALSE;
	}

	ce->next->flag |= TRAVERSAL_ENTRY;
	ce->next->flag &= ~SYMBOL_ENTRY;
	return TRUE;
}

static int
read_list_entry(struct config *config, int line)
{
	char *token = get_config_token(NULL, 0, &line, NULL, NULL);

	if (!token || IS_KEYWORD(token)) {
		ERRMSG("Config error at %d: expected list symbol after"
		" 'in' keyword.\n", line);
		return FALSE;
	}
	config->list_entry = create_config_entry(token, LIST_ENTRY, line);
	if (!config->list_entry) {
		ERRMSG("Error at line %d: Failed to read list symbol\n",
									line);
		return FALSE;
	}
	/* Check if user has provided 'via' or 'within' keyword */
	if (get_config_token("via", 0, &line, NULL, NULL)) {
		/* next token is traversal member NextMember */
		token = get_config_token(NULL, 0, &line, NULL, NULL);
		if (!token) {
			ERRMSG("Config error at %d: expected member name after"
			" 'via' keyword.\n", line);
			return FALSE;
		}
		if (!add_traversal_entry(config->list_entry, token, line))
			return FALSE;
	}
	else if (get_config_token("within", 0, &line, NULL, NULL)) {
		char *s_name, *lh_member;
		/* next value is StructName:ListHeadMember */
		s_name = get_config_token(NULL, 0, &line, NULL, NULL);
		if (!s_name || IS_KEYWORD(s_name)) {
			ERRMSG("Config error at %d: expected struct name after"
			" 'within' keyword.\n", line);
			return FALSE;
		}
		lh_member = strchr(s_name, ':');
		if (lh_member) {
			*lh_member++ = '\0';
			if (!strlen(lh_member)) {
				ERRMSG("Config error at %d: expected list_head"
					" member after ':'.\n", line);
				return FALSE;
			}
			config->iter_entry->next =
				create_config_entry(lh_member,
							ITERATION_ENTRY, line);
			if (!config->iter_entry->next)
				return FALSE;
			config->iter_entry->next->flag &= ~SYMBOL_ENTRY;
		}
		if (!strlen(s_name)) {
			ERRMSG("Config error at %d: Invalid token found "
				"after 'within' keyword.\n", line);
			return FALSE;
		}
		config->iter_entry->type_name = strdup(s_name);
	}
	return TRUE;
}

/*
 * Read the iteration entry (LoopConstruct). The syntax is:
 *
 *	for <id> in {<ArrayVar> |
 *		    <StructVar> via <NextMember> |
 *		    <ListHeadVar> within <StructName>:<ListHeadMember>}
 *		erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
 *		[erase <id>...]
 *		[...]
 *	endfor
 */
static int
read_iteration_entry(struct config *config, int line)
{
	int eof = NOT_REACH_END;
	char *token = get_config_token(NULL, 0, &line, NULL, NULL);

	if (!token || IS_KEYWORD(token)) {
		ERRMSG("Config error at %d: expected iteration VAR entry after"
		" 'for' keyword.\n", line);
		return FALSE;
	}
	config->iter_entry =
		create_config_entry(token, ITERATION_ENTRY, line);
	if (!config->iter_entry) {
		ERRMSG("Error at line %d: "
			"Failed to read iteration VAR entry.\n", line);
		return FALSE;
	}
	if (!get_config_token("in", 0, &line, NULL, NULL)) {
		char *token;
		token = get_config_token(NULL, 0, &line, NULL, NULL);
		if (token)
			ERRMSG("Config error at %d: Invalid token '%s'.\n",
								line, token);
		ERRMSG("Config error at %d: expected token 'in'.\n", line);
		return FALSE;
	}
	if (!read_list_entry(config, line))
		return FALSE;

	while (!get_config_token("endfor", 0, &line, NULL, &eof) && !eof) {
		if (get_config_token("erase", 0, &line, NULL, NULL)) {
			if (!read_erase_cmd_entry(config, line))
				return FALSE;
		} else {
			token = get_config_token(NULL, 0, &line, NULL, NULL);
			ERRMSG("Config error at %d: "
				"Invalid token '%s'.\n", line, token);
			return FALSE;
		}
	}
	if (eof != NOT_REACH_END) {
		ERRMSG("Config error at %d: No matching 'endfor' found.\n",
									line);
		return FALSE;
	}
	return TRUE;
}

/*
 * Configuration file 'makedumpfile.conf' contains filter commands.
 * Every individual filter command is considered as a config entry.
 * A config entry can be provided on a single line or multiple lines.
 */
static struct config *
get_config(int skip)
{
	struct config *config;
	char *token = NULL;
	static int line_count = 0;
	char *cur_module = NULL;
	int eof = NOT_REACH_END;
	unsigned char flag = CONFIG_NEW_CMD;

	if (skip)
		flag |= CONFIG_SKIP_SECTION;

	if ((config = calloc(1, sizeof(struct config))) == NULL)
		return NULL;

	if (get_config_token("erase", flag, &line_count, &cur_module, &eof)) {
		if (cur_module)
			config->module_name = strdup(cur_module);

		if (!read_erase_cmd_entry(config, line_count))
			goto err_out;

	} else if (get_config_token("for", 0, &line_count, &cur_module, &eof)) {
		if (cur_module)
			config->module_name = strdup(cur_module);

		if (!read_iteration_entry(config, line_count))
			goto err_out;
	} else {
		if (eof == NOT_REACH_END) {
			token = get_config_token(NULL, 0, &line_count,
								NULL, NULL);
			ERRMSG("Config error at %d: Invalid token '%s'.\n",
							line_count, token);
		}
		goto err_out;
	}
	return config;
err_out:
	if (config)
		free_config(config);
	return NULL;
}

static unsigned long
read_pointer_value(unsigned long long vaddr)
{
	unsigned long val;

	if (!readmem(VADDR, vaddr, &val, sizeof(val))) {
		ERRMSG("Can't read pointer value\n");
		return 0;
	}
	return val;
}

static long
get_strlen(unsigned long long vaddr)
{
	char buf[BUFSIZE + 1];
	long len = 0;

	/*
	 * Determine the string length for 'char' pointer.
	 * BUFSIZE(1024) is the upper limit for string length.
	 */
	if (readmem(VADDR, vaddr, buf, BUFSIZE)) {
		buf[BUFSIZE] = '\0';
		len = strlen(buf);
	}
	return len;
}

static int
resolve_config_entry(struct config_entry *ce, unsigned long long base_vaddr,
						char *base_struct_name)
{
	unsigned long long symbol;

	if (ce->flag & SYMBOL_ENTRY) {
		/* find the symbol info */
		if (!ce->name)
			return FALSE;

		/*
		 * If we are looking for module symbol then traverse through
		 * mod_st.modules for symbol lookup
		 */
		if (sym_in_module(ce->name, &symbol))
			ce->sym_addr = symbol;
		else
			ce->sym_addr = get_symbol_addr(ce->name);
		if (!ce->sym_addr) {
			ERRMSG("Config error at %d: Can't find symbol '%s'.\n",
							ce->line, ce->name);
			return FALSE;
		}
		ce->sym_addr += get_kaslr_offset(ce->sym_addr);
		ce->type_name = get_symbol_type_name(ce->name,
					DWARF_INFO_GET_SYMBOL_TYPE,
					&ce->size, &ce->type_flag);
		if (ce->type_flag & TYPE_ARRAY) {
			ce->array_length = get_array_length(ce->name, NULL,
					DWARF_INFO_GET_SYMBOL_ARRAY_LENGTH);
			if (ce->array_length < 0)
				ce->array_length = 0;
		}
	} else if (ce->flag & VAR_ENTRY) {
		/* iteration variable.
		 * read the value from ce->refer_to
		 */
		ce->vaddr     = ce->refer_to->vaddr;
		ce->sym_addr  = ce->refer_to->sym_addr;
		ce->size      = ce->refer_to->size;
		ce->type_flag = ce->refer_to->type_flag;
		if (!ce->type_name)
			ce->type_name = strdup(ce->refer_to->type_name);

		/* This entry has been changed hence next entry needs to
		 * be resolved accordingly.
		 */
		if (ce->next)
			ce->next->flag &= ~ENTRY_RESOLVED;
		return TRUE;
	} else {
		/* find the member offset */
		ce->offset = get_member_offset(base_struct_name,
				ce->name, DWARF_INFO_GET_MEMBER_OFFSET);
		ce->sym_addr = base_vaddr + ce->offset;
		ce->type_name = get_member_type_name(base_struct_name,
				ce->name, DWARF_INFO_GET_MEMBER_TYPE,
				&ce->size, &ce->type_flag);
		if (ce->type_flag & TYPE_ARRAY) {
			ce->array_length = get_array_length(base_struct_name,
					ce->name,
					DWARF_INFO_GET_MEMBER_ARRAY_LENGTH);
			if (ce->array_length < 0)
				ce->array_length = 0;
		}
	}
	if (ce->type_name == NULL) {
		if (!(ce->flag & SYMBOL_ENTRY))
			ERRMSG("Config error at %d: struct '%s' has no member"
				" with name '%s'.\n",
				ce->line, base_struct_name, ce->name);
		return FALSE;
	}
	if (!strcmp(ce->type_name, "list_head")) {
		ce->type_flag |= TYPE_LIST_HEAD;
		/* If this list head expression is a LIST entry then
		 * mark the next entry as TRAVERSAL_ENTRY, if any.
		 * Error out if next entry is not a last node.
		 */
		if ((ce->flag & LIST_ENTRY) && ce->next) {
			if (ce->next->next) {
				ERRMSG("Config error at %d: Only one traversal"
					" entry is allowed for list_head type"
					" LIST entry", ce->line);
				return FALSE;
			}
			ce->next->flag |= TRAVERSAL_ENTRY;
		}
	}
	ce->vaddr = ce->sym_addr;
	if (ce->size < 0)
		ce->size = 0;
	if ((ce->flag & LIST_ENTRY) && !ce->next) {
		/* This is the last node of LIST entry.
		 * For the list entry symbol, the allowed data types are:
		 * Array, Structure Pointer (with 'next' member) and list_head.
		 *
		 * If this is a struct or list_head data type then
		 * create a leaf node entry with 'next' member.
		 */
		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
					&& (strcmp(ce->type_name, "void")))
			return FALSE;

		if ((ce->type_flag & TYPE_LIST_HEAD)
			|| ((ce->type_flag & (TYPE_STRUCT | TYPE_ARRAY))
							== TYPE_STRUCT)) {
			if (!(ce->flag & TRAVERSAL_ENTRY)) {
				ce->next = create_config_entry("next",
							LIST_ENTRY, ce->line);
				if (ce->next == NULL)
					return FALSE;

				ce->next->flag |= TRAVERSAL_ENTRY;
				ce->next->flag &= ~SYMBOL_ENTRY;
			}
		}
		if (ce->flag & TRAVERSAL_ENTRY) {
			/* type name of traversal entry should match with
			 * that of parent node.
			 */
			if (strcmp(base_struct_name, ce->type_name))
				return FALSE;
		}
	}
	if ((ce->type_flag & (TYPE_ARRAY | TYPE_PTR)) == TYPE_PTR) {
		/* If it's a pointer variable (not array) then read the
		 * pointer value. */
		ce->vaddr = read_pointer_value(ce->sym_addr);

		/*
		 * if it is a void pointer then reset the size to 0
		 * User need to provide a size to filter data referenced
		 * by 'void *' pointer or nullify option.
		 */
		if (!strcmp(ce->type_name, "void"))
			ce->size = 0;

	}
	if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)
					&& !(ce->type_flag & TYPE_ARRAY)) {
		if (!strcmp(ce->type_name, "char"))
			ce->size = get_strlen(ce->vaddr);
	}
	if (!ce->next && (ce->flag & SIZE_ENTRY)) {
		void *val;

		/* leaf node of size entry */
		/* If it is size argument then update the size with data
		 * value of this symbol/member.
		 * Check if current symbol/member is of base data type.
		 */

		if (((ce->type_flag & (TYPE_ARRAY | TYPE_BASE)) != TYPE_BASE)
				|| (ce->size > sizeof(long))) {
			ERRMSG("Config error at %d: size symbol/member '%s' "
				"is not of base type.\n", ce->line, ce->name);
			return FALSE;
		}
		if ((val = calloc(1, ce->size)) == NULL) {
			ERRMSG("Can't get memory for size parameter\n");
			return FALSE;
		}

		if (!readmem(VADDR, ce->vaddr, val, ce->size)) {
			ERRMSG("Can't read symbol/member data value\n");
			return FALSE;
		}
		switch (ce->size) {
		case 1:
			ce->size = (long)(*((uint8_t *)val));
			break;
		case 2:
			ce->size = (long)(*((uint16_t *)val));
			break;
		case 4:
			ce->size = (long)(*((uint32_t *)val));
			break;
		case 8:
			ce->size = (long)(*((uint64_t *)val));
			break;
		}
		free(val);
	}
	ce->flag |= ENTRY_RESOLVED;
	if (ce->next)
		ce->next->flag &= ~ENTRY_RESOLVED;
	return TRUE;
}

static unsigned long long
get_config_symbol_addr(struct config_entry *ce,
			unsigned long long base_vaddr,
			char *base_struct_name)
{
	if (!(ce->flag & ENTRY_RESOLVED)) {
		if (!resolve_config_entry(ce, base_vaddr, base_struct_name))
			return 0;
	}

	if (ce->next && ce->vaddr) {
		/* Populate nullify flag down the list */
		ce->next->nullify = ce->nullify;
		return get_config_symbol_addr(ce->next, ce->vaddr,
							ce->type_name);
	} else if (!ce->next && ce->nullify) {
		/* nullify is applicable to pointer type */
		if (ce->type_flag & TYPE_PTR)
			return ce->sym_addr;
		else
			return 0;
	} else
		return ce->vaddr;
}

static long
get_config_symbol_size(struct config_entry *ce,
			unsigned long long base_vaddr,
			char *base_struct_name)
{
	if (!(ce->flag & ENTRY_RESOLVED)) {
		if (!resolve_config_entry(ce, base_vaddr, base_struct_name))
			return 0;
	}

	if (ce->next && ce->vaddr)
		return get_config_symbol_size(ce->next, ce->vaddr,
							ce->type_name);
	else {
		if (ce->type_flag & TYPE_ARRAY) {
			if (ce->type_flag & TYPE_PTR)
				return ce->array_length * get_pointer_size();
			else
				return ce->array_length * ce->size;
		}
		return ce->size;
	}
}

static int
get_next_list_entry(struct config_entry *ce, unsigned long long base_vaddr,
			char *base_struct_name, struct config_entry *out_ce)
{
	unsigned long vaddr = 0;

	/* This function only deals with LIST_ENTRY config entry. */
	if (!(ce->flag & LIST_ENTRY))
		return FALSE;

	if (!(ce->flag & ENTRY_RESOLVED)) {
		if (!resolve_config_entry(ce, base_vaddr, base_struct_name))
			return FALSE;
	}

	if (!ce->next) {
		/* leaf node. */
		if (ce->type_flag & TYPE_ARRAY) {
			if (ce->index == ce->array_length)
				return FALSE;

			if (ce->type_flag & TYPE_PTR) {
				/* Array of pointers.
				 *
				 * Array may contain NULL pointers at some
				 * indexes. Hence jump to the next non-null
				 * address value.
				 */
				while (ce->index < ce->array_length) {
					vaddr = read_pointer_value(ce->vaddr +
						(ce->index * get_pointer_size()));
					if (vaddr)
						break;
					ce->index++;
				}
				if (ce->index == ce->array_length)
					return FALSE;
				out_ce->sym_addr = ce->vaddr + (ce->index *
							get_pointer_size());
				out_ce->vaddr = vaddr;
				if (!strcmp(ce->type_name, "char"))
					out_ce->size = get_strlen(vaddr);
				else
					out_ce->size = ce->size;
			} else {
				out_ce->sym_addr = ce->vaddr +
							(ce->index * ce->size);
				out_ce->vaddr = out_ce->sym_addr;
				out_ce->size  = ce->size;
			}
			ce->index++;
		} else {
			if (ce->vaddr == ce->cmp_addr)
				return FALSE;

			out_ce->vaddr = ce->vaddr;
			/* Set the leaf node as unresolved, so that
			 * it will be resolved every time when
			 * get_next_list_entry is called untill
			 * it hits the exit condiftion.
			 */
			ce->flag &= ~ENTRY_RESOLVED;
		}
		return TRUE;

	} else if ((ce->next->next == NULL) &&
				!(ce->next->type_flag & TYPE_ARRAY)) {
		/* the next node is leaf node. for non-array element
		 * Set the sym_addr and addr of this node with that of
		 * leaf node.
		 */
		if (!(ce->type_flag & TYPE_LIST_HEAD)) {
			if (!ce->vaddr || ce->vaddr == ce->next->cmp_addr)
				return FALSE;

			if (!ce->next->cmp_addr) {
				/* safeguard against circular
				 * link-list
				 */
				ce->next->cmp_addr = ce->vaddr;
			}
			out_ce->vaddr    = ce->vaddr;
			out_ce->sym_addr = ce->sym_addr;
			out_ce->size     = ce->size;

			ce->sym_addr = ce->next->sym_addr;
			ce->vaddr    = ce->next->vaddr;

			/* Force resolution of traversal node */
			if (ce->vaddr && !resolve_config_entry(ce->next,
					ce->vaddr, ce->type_name))
				return FALSE;

			return TRUE;
		} else {
			ce->sym_addr = ce->next->sym_addr;
			ce->vaddr    = ce->next->vaddr;
		}
	}

	if (ce->next && ce->vaddr)
		return get_next_list_entry(ce->next, ce->vaddr,
						ce->type_name, out_ce);
	return FALSE;
}

static int
resolve_list_entry(struct config_entry *ce, unsigned long long base_vaddr,
			char *base_struct_name, char **out_type_name,
			unsigned char *out_type_flag)
{
	if (!(ce->flag & ENTRY_RESOLVED)) {
		if (!resolve_config_entry(ce, base_vaddr, base_struct_name))
			return FALSE;
	}

	if (ce->next && (ce->next->flag & TRAVERSAL_ENTRY) &&
				(ce->type_flag & TYPE_ARRAY)) {
		/*
		 * We are here because user has provided
		 * traversal member for ArrayVar using 'via' keyword.
		 *
		 * Print warning and continue.
		 */
		ERRMSG("Warning: line %d: 'via' keyword not required "
			"for ArrayVar.\n", ce->next->line);
		free_config_entry(ce->next);
		ce->next = NULL;
	}
	if ((ce->type_flag & TYPE_LIST_HEAD) && ce->next &&
			(ce->next->flag & TRAVERSAL_ENTRY)) {
		/* set cmp_addr for list empty condition.  */
		ce->next->cmp_addr = ce->sym_addr;
	}
	if (ce->next && ce->vaddr) {
		return resolve_list_entry(ce->next, ce->vaddr,
				ce->type_name, out_type_name, out_type_flag);
	}
	else {
		ce->index = 0;
		if (out_type_name)
			*out_type_name = ce->type_name;
		if (out_type_flag)
			*out_type_flag = ce->type_flag;
	}
	return TRUE;
}

/*
 * Insert the filter info node using insertion sort.
 * If filter node for a given paddr is aready present then update the size
 * and delete the fl_info node passed.
 *
 * Return 1 on successfull insertion.
 * Return 0 if filter node with same paddr is found.
 */
static int
insert_filter_info(struct filter_info *fl_info)
{
	struct filter_info *prev = NULL;
	struct filter_info *ptr  = filter_info;

	if (!ptr) {
		filter_info = fl_info;
		return 1;
	}

	while (ptr) {
		if (fl_info->paddr <= ptr->paddr)
			break;
		prev = ptr;
		ptr  = ptr->next;
	}
	if (ptr && (fl_info->paddr == ptr->paddr)) {
		if (fl_info->size > ptr->size)
			ptr->size = fl_info->size;
		free(fl_info);
		return 0;
	}

	if (prev) {
		fl_info->next = ptr;
		prev->next    = fl_info;
	}
	else {
		fl_info->next = filter_info;
		filter_info   = fl_info;
	}
	return 1;
}

/*
 * Create an erase info node for each erase command. One node per erase
 * command even if it is part of loop construct.
 * For erase commands that are not part of loop construct, the num_sizes will
 * always be 1
 * For erase commands that are part of loop construct, the num_sizes may be
 * 1 or >1 depending on number iterations. This function will called multiple
 * times depending on iterations. At first invokation create a node and
 * increment num_sizes for subsequent invokations.
 *
 * The valid erase info node starts from index value 1. (index 0 is invalid
 * index).
 *
 *            Index 0     1        2        3
 *             +------+--------+--------+--------+
 * erase_info->|Unused|        |        |        |......
 *             +------+--------+--------+--------+
 *                        |        .        .        .....
 *                        V
 *                   +---------+
 *                   | char*   |----> Original erase command string
 *                   +---------+
 *                   |num_sizes|
 *                   +---------+      +--+--+--+
 *                   | sizes   |----> |  |  |  |... Sizes array of num_sizes
 *                   +---------+      +--+--+--+
 *
 * On success, return the index value of erase node for given erase command.
 * On failure, return 0.
 */
static int
add_erase_info_node(struct config_entry *filter_symbol)
{
	int idx = filter_symbol->erase_info_idx;

	/*
	 * Check if node is already created, if yes, increment the num_sizes.
	 */
	if (idx) {
		erase_info[idx].num_sizes++;
		return idx;
	}

	/* Allocate a new node. */
	DEBUG_MSG("Allocating new erase info node for command \"%s\"\n",
			filter_symbol->symbol_expr);
	idx = num_erase_info++;
	erase_info = realloc(erase_info,
			sizeof(struct erase_info) * num_erase_info);
	if (!erase_info) {
		ERRMSG("Can't get memory to create erase information.\n");
		return 0;
	}

	memset(&erase_info[idx], 0, sizeof(struct erase_info));
	erase_info[idx].symbol_expr = filter_symbol->symbol_expr;
	erase_info[idx].num_sizes   = 1;

	filter_symbol->symbol_expr    = NULL;
	filter_symbol->erase_info_idx = idx;

	return idx;
}

/* Return the index value in sizes array for given erase command index. */
static inline int
get_size_index(int ei_idx)
{
	if (ei_idx)
		return erase_info[ei_idx].num_sizes - 1;
	return 0;
}

static int
update_filter_info(struct config_entry *filter_symbol,
			struct config_entry *size_symbol)
{
	unsigned long long sym_addr;
	long size;
	struct filter_info *fl_info;

	sym_addr = get_config_symbol_addr(filter_symbol, 0, NULL);
	if (message_level & ML_PRINT_DEBUG_MSG)
		print_config_entry(filter_symbol);
	if (!sym_addr)
		return FALSE;

	if (filter_symbol->nullify)
		size = get_pointer_size();
	else if (size_symbol) {
		size = get_config_symbol_size(size_symbol, 0, NULL);
		if (message_level & ML_PRINT_DEBUG_MSG)
			print_config_entry(size_symbol);
	} else
		size = get_config_symbol_size(filter_symbol, 0, NULL);

	if (size <= 0)
		return FALSE;

	if ((fl_info = calloc(1, sizeof(struct filter_info))) == NULL) {
		ERRMSG("Can't allocate filter info\n");
		return FALSE;
	}
	fl_info->vaddr   = sym_addr;
	fl_info->paddr   = vaddr_to_paddr(sym_addr);
	fl_info->size    = size;
	fl_info->nullify = filter_symbol->nullify;
	fl_info->erase_ch = 'X';

	if (insert_filter_info(fl_info)) {
		fl_info->erase_info_idx = add_erase_info_node(filter_symbol);
		fl_info->size_idx = get_size_index(fl_info->erase_info_idx);
	}
	return TRUE;
}

int
update_filter_info_raw(unsigned long long sym_addr, int ch, int len)
{
	struct filter_info *fl_info;

	fl_info = calloc(1, sizeof(struct filter_info));
	if (fl_info == NULL) {
		ERRMSG("Can't allocate filter info\n");
		return FALSE;
	}

	fl_info->vaddr   = sym_addr;
	fl_info->paddr   = vaddr_to_paddr(sym_addr);
	fl_info->size    = len;
	fl_info->nullify = 0;
	fl_info->erase_ch = ch;

	if (insert_filter_info(fl_info)) {
		/* TODO
		 * Add support to update erase information to the
		 * resulting dump file
		 */
		fl_info->erase_info_idx = 0;
		fl_info->size_idx = 0;
	}
	return TRUE;
}

static int
initialize_iteration_entry(struct config_entry *ie,
				char *type_name, unsigned char type_flag)
{
	if (!(ie->flag & ITERATION_ENTRY))
		return FALSE;

	if (type_flag & TYPE_LIST_HEAD) {
		if (!ie->type_name) {
			ERRMSG("Config error at %d: Use 'within' keyword "
				"to specify StructName:ListHeadMember.\n",
				ie->line);
			return FALSE;
		}
		/*
		 * If the LIST entry is of list_head type and user has not
		 * specified the member name where iteration entry is hooked
		 * on to list_head, then we default to member name 'list'.
		 */
		if (!ie->next) {
			ie->next = create_config_entry("list", ITERATION_ENTRY,
								ie->line);
			ie->next->flag &= ~SYMBOL_ENTRY;
		}

		/*
		 * For list_head find out the size of the StructName and
		 * populate ie->size now. For array and link list we get the
		 * size info from config entry returned by
		 * get_next_list_entry().
		 */
		ie->size = get_structure_size(ie->type_name, 0);
		if (ie->size == FAILED_DWARFINFO) {
			ERRMSG("Config error at %d: "
				"Can't get size for type: %s.\n",
				ie->line, ie->type_name);
			return FALSE;

		} else if (ie->size == NOT_FOUND_STRUCTURE) {
			ERRMSG("Config error at %d: "
				"Can't find structure: %s.\n",
				ie->line, ie->type_name);
			return FALSE;
		}

		if (!resolve_config_entry(ie->next, 0, ie->type_name))
			return FALSE;

		if (strcmp(ie->next->type_name, "list_head")) {
			ERRMSG("Config error at %d: "
				"Member '%s' is not of 'list_head' type.\n",
				ie->next->line, ie->next->name);
			return FALSE;
		}
		ie->type_flag = TYPE_STRUCT;
	} else {
		if (ie->type_name) {
			/* looks like user has used 'within' keyword for
			 * non-list_head VAR. Print the warning and continue.
			 */
			ERRMSG("Warning: line %d: 'within' keyword not "
				"required for ArrayVar/StructVar.\n", ie->line);
			free(ie->type_name);

			/* remove the next list_head member from iteration
			 * entry that would have added as part of 'within'
			 * keyword processing.
			 */
			if (ie->next) {
				free_config_entry(ie->next);
				ie->next = NULL;
			}
		}
		/*
		 * Set type flag for iteration entry. The iteration entry holds
		 * individual element from array/list, hence strip off the
		 * array type flag bit.
		 */
		ie->type_name = strdup(type_name);
		ie->type_flag = type_flag;
		ie->type_flag &= ~TYPE_ARRAY;
	}
	return TRUE;
}

static int
list_entry_empty(struct config_entry *le, struct config_entry *ie)
{
	struct config_entry ce;

	/* Error out if arguments are not correct */
	if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) {
		ERRMSG("Invalid arguments\n");
		return TRUE;
	}

	memset(&ce, 0, sizeof(struct config_entry));
	/* get next available entry from LIST entry. */
	if (!get_next_list_entry(le, 0, NULL, &ce))
		return TRUE;

	if (ie->next) {
		/* we are dealing with list_head */
		ie->next->vaddr = ce.vaddr;
		ie->vaddr       = ce.vaddr - ie->next->offset;
	} else {
		ie->vaddr    = ce.vaddr;
		ie->sym_addr = ce.sym_addr;
		ie->size     = ce.size;
	}
	return FALSE;
}

/*
 * Process the config entry that has been read by get_config.
 * return TRUE on success
 */
static int
process_config(struct config *config)
{
	int i;
	unsigned char type_flag;
	char *type_name = NULL;

	if (config->list_entry) {
		/*
		 * We are dealing with 'for' command.
		 * - First resolve list entry.
		 * - Initialize iteration entry for iteration.
		 * - Populate iteration entry untill list entry empty.
		 */
		if (!resolve_list_entry(config->list_entry, 0, NULL,
					&type_name, &type_flag)) {
			return FALSE;
		}
		if (!initialize_iteration_entry(config->iter_entry,
						type_name, type_flag)) {
			return FALSE;
		}

		while (!list_entry_empty(config->list_entry,
						config->iter_entry)) {
			for (i = 0; i < config->num_filter_symbols; i++)
				update_filter_info(config->filter_symbol[i],
						   config->size_symbol[i]);
		}
	} else
		update_filter_info(config->filter_symbol[0],
				   config->size_symbol[0]);

	return TRUE;
}

static void
print_filter_info()
{
	struct filter_info *fl_info = filter_info;

	DEBUG_MSG("\n");
	while (fl_info) {
		DEBUG_MSG("filter address: paddr (%llx), sym_addr (%llx),"
			" Size (%ld)\n",
			fl_info->paddr, fl_info->vaddr, fl_info->size);
		fl_info = fl_info->next;
	}
}

static void
init_filter_config()
{
	filter_config.name_filterconfig = info->name_filterconfig;
	filter_config.file_filterconfig = info->file_filterconfig;
	filter_config.saved_token = NULL;
	filter_config.token       = NULL;
	filter_config.cur_module  = NULL;
	filter_config.new_section = 0;
	filter_config.line_count  = 0;
}

/*
 * Read and process each config entry (filter commands) from filter config
 * file. If no module debuginfo found for specified module section then skip
 * to next module section.
 */
static int
process_config_file(const char *name_config)
{
	struct config *config;
	int skip_section = 0;

	if (!name_config)
		return FALSE;

	if ((info->file_filterconfig = fopen(name_config, "r")) == NULL) {
		ERRMSG("Can't open config file(%s). %s\n",
		    name_config, strerror(errno));
		return FALSE;
	}

	init_filter_config();

	while((config = get_config(skip_section)) != NULL) {
		skip_section = 0;
		if (config->module_name &&
				strcmp(config->module_name, "vmlinux")) {
			/*
			 * if Module debuginfo is not available, then skip to
			 * next module section.
			 */
			if (!set_dwarf_debuginfo(config->module_name,
				  info->system_utsname.release, NULL, -1)) {
				ERRMSG("Skipping to next Module section\n");
				skip_section = 1;
				free_config(config);
				continue;
			}
		} else {
			set_dwarf_debuginfo("vmlinux", NULL,
				info->name_vmlinux, info->fd_vmlinux);
		}
		process_config(config);
		free_config(config);
	}

	fclose(info->file_filterconfig);
	print_filter_info();
	return TRUE;
}

/*
 * Search for symbol in modules as well as vmlinux
 */
unsigned long long
get_symbol_addr_all(char *name) {

	short vmlinux_searched = 0;
	unsigned long long symbol_addr = 0;
	unsigned int i, current_mod;
	struct module_info *modules;

	/* Search in vmlinux if debuginfo is set to vmlinux */
	if (!strcmp(get_dwarf_module_name(), "vmlinux")) {
		symbol_addr = get_symbol_addr(name);
		if (symbol_addr)
			return symbol_addr;

		vmlinux_searched = 1;
	}

	/*
	 * Proceed the search in modules. Try in the module
	 * which resulted in a hit in the previous search
	 */

	modules = mod_st.modules;
	current_mod = mod_st.current_mod;

	if (strcmp(get_dwarf_module_name(), modules[current_mod].name)) {
		if (!set_dwarf_debuginfo(modules[current_mod].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Cannot set to current module %s\n",
					modules[current_mod].name);
			return NOT_FOUND_SYMBOL;
		}
	}

	symbol_addr = find_module_symbol(&modules[current_mod], name);
	if (symbol_addr)
		return symbol_addr;

	/* Search in all modules */
	for (i = 0; i < mod_st.num_modules; i++) {

		/* Already searched. Skip */
		if (i == current_mod)
			continue;

		if (!set_dwarf_debuginfo(modules[i].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Skipping Module section %s\n", modules[i].name);
			continue;
		}

		symbol_addr = find_module_symbol(&modules[i], name);

		if (!symbol_addr)
			continue;

		/*
		 * Symbol found. Set the current_mod to this module index, a
		 * minor optimization for fast lookup next time
		 */
		mod_st.current_mod = i;
		return symbol_addr;
	}

	/* Symbol not found in any module. Set debuginfo back to vmlinux  */
	set_dwarf_debuginfo("vmlinux", NULL, info->name_vmlinux,
			info->fd_vmlinux);

	/*
	 * Search vmlinux if not already searched. This can happen when
	 * this function is called with debuginfo set to a particular
	 * kernel module and we are looking for symbol in vmlinux
	 */
	if (!vmlinux_searched)
		return get_symbol_addr(name);
	else
		return NOT_FOUND_SYMBOL;
}


/*
 * Search for domain in modules as well as vmlinux
 */
long
get_domain_all(char *symname, int cmd, unsigned long long *die) {

	short vmlinux_searched = 0;
	long size = 0;
	unsigned int i, current_mod;
	struct module_info *modules;

	/* Search in vmlinux if debuginfo is set to vmlinux */
	if (!strcmp(get_dwarf_module_name(), "vmlinux")) {
		size = get_domain(symname, cmd, die);
		if (size > 0 && die)
			return size;

		vmlinux_searched = 1;
	}

	/*
	 * Proceed the search in modules. Try in the module
	 * which resulted in a hit in the previous search
	 */

	modules = mod_st.modules;
	current_mod = mod_st.current_mod;

	if (strcmp(get_dwarf_module_name(), modules[current_mod].name)) {
		if (!set_dwarf_debuginfo(modules[current_mod].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Cannot set to current module %s\n",
					modules[current_mod].name);
			return NOT_FOUND_STRUCTURE;
		}
	}

	size = get_domain(symname, cmd, die);
	if (size > 0 && die)
		return size;

	/* Search in all modules */
	for (i = 0; i < mod_st.num_modules; i++) {

		/* Already searched. Skip */
		if (i == current_mod)
			continue;

		if (!set_dwarf_debuginfo(modules[i].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Skipping Module section %s\n", modules[i].name);
			continue;
		}

		size = get_domain(symname, cmd, die);

		if (size <= 0 || !die)
			continue;

		/*
		 * Domain found. Set the current_mod to this module index, a
		 * minor optimization for fast lookup next time
		 */
		mod_st.current_mod = i;
		return size;
	}

	/* Domain not found in any module. Set debuginfo back to vmlinux */
	set_dwarf_debuginfo("vmlinux", NULL, info->name_vmlinux,
			info->fd_vmlinux);

	if (!vmlinux_searched)
		return get_domain(symname, cmd, die);
	else
		return NOT_FOUND_STRUCTURE;
}

/*
 * Search for die in modules as well as vmlinux
 */
int
get_die_nfields_all(unsigned long long die_off)
{
	short vmlinux_searched = 0;
	long nfields = -1;
	unsigned int i, current_mod;
	struct module_info *modules;

	/* Search in vmlinux if debuginfo is set to vmlinux */
	if (!strcmp(get_dwarf_module_name(), "vmlinux")) {
		nfields = get_die_nfields(die_off);
		if (nfields > 0)
			return nfields;

		vmlinux_searched = 1;
	}

	/*
	 * Proceed the search in modules. Try in the module
	 * which resulted in a hit in the previous search
	 */

	modules = mod_st.modules;
	current_mod = mod_st.current_mod;

	if (strcmp(get_dwarf_module_name(), modules[current_mod].name)) {
		if (!set_dwarf_debuginfo(modules[current_mod].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Cannot set to current module %s\n",
					modules[current_mod].name);
			return -1;
		}
	}

	nfields = get_die_nfields(die_off);
	if (nfields > 0)
		return nfields;

	/* Search in all modules */
	for (i = 0; i < mod_st.num_modules; i++) {

		/* Already searched. Skip */
		if (i == current_mod)
			continue;

		if (!set_dwarf_debuginfo(modules[i].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Skipping Module section %s\n", modules[i].name);
			continue;
		}

		nfields = get_die_nfields(die_off);

		if (nfields < 0)
			continue;

		/*
		 * Die found. Set the current_mod to this module index,
		 * a minor optimization for fast lookup next time
		 */
		mod_st.current_mod = i;
		return nfields;
	}

	/* Die not found in any module. Set debuginfo back to vmlinux */
	set_dwarf_debuginfo("vmlinux", NULL, info->name_vmlinux,
			info->fd_vmlinux);

	if (!vmlinux_searched)
		return get_die_nfields(die_off);
	else
		return -1;

}

/*
 * Search for die member in modules as well as vmlinux
 */
int
get_die_member_all(unsigned long long die_off, int index, long *offset,
		char **name, int *nbits, int *fbits, unsigned long long *m_die)
{
	short vmlinux_searched = 0;
	long size = -1;
	unsigned int i, current_mod;
	struct module_info *modules;

	/* Search in vmlinux if debuginfo is set to vmlinux */
	if (!strcmp(get_dwarf_module_name(), "vmlinux")) {
		size = get_die_member(die_off, index, offset, name,
				nbits, fbits, m_die);
		if (size >= 0)
			return size;

		vmlinux_searched = 1;
	}

	/*
	 * Proceed the search in modules. Try in the module
	 * which resulted in a hit in the previous search
	 */

	modules = mod_st.modules;
	current_mod = mod_st.current_mod;

	if (strcmp(get_dwarf_module_name(), modules[current_mod].name)) {
		if (!set_dwarf_debuginfo(modules[current_mod].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Cannot set to current module %s\n",
					modules[current_mod].name);
			return -1;
		}
	}

	size = get_die_member(die_off, index, offset, name,
				nbits, fbits, m_die);
	if (size >= 0)
		return size;

	/* Search in all modules */
	for (i = 0; i < mod_st.num_modules; i++) {

		/* Already searched. Skip */
		if (i == current_mod)
			continue;

		if (!set_dwarf_debuginfo(modules[i].name,
				info->system_utsname.release, NULL, -1)) {
			ERRMSG("Skipping Module section %s\n", modules[i].name);
			continue;
		}

		size = get_die_member(die_off, index, offset, name,
				nbits, fbits, m_die);

		if (size < 0)
			continue;

		/*
		 * Die member found. Set the current_mod to this module index,
		 * a minor optimization for fast lookup next time
		 */
		mod_st.current_mod = i;
		return size;
	}

	/* Die member not found in any module. Set debuginfo back to vmlinux */
	set_dwarf_debuginfo("vmlinux", NULL, info->name_vmlinux,
			info->fd_vmlinux);

	if (!vmlinux_searched)
		return get_die_member(die_off, index, offset, name,
				nbits, fbits, m_die);
	else
		return -1;
}

/* Process the eppic macro using eppic library */
static int
process_eppic_file(char *name_config)
{
	void *handle;
	void (*eppic_load)(char *), (*eppic_unload)(char *);
	int (*eppic_init)();

	/*
	 * Dynamically load the eppic_makedumpfile.so library.
	 */
	handle = dlopen("eppic_makedumpfile.so", RTLD_LAZY);
	if (!handle) {
		ERRMSG("dlopen failed: %s\n", dlerror());
		return FALSE;
	}

	/* TODO
	 * Support specifying eppic macros in makedumpfile.conf file
	 */

	eppic_init = dlsym(handle, "eppic_init");
	if (!eppic_init) {
		ERRMSG("Could not find eppic_init function\n");
		return FALSE;
	}

	eppic_load = dlsym(handle, "eppic_load");
	if (!eppic_load) {
		ERRMSG("Could not find eppic_load function\n");
		return FALSE;
	}

	eppic_unload = dlsym(handle, "eppic_unload");
	if (!eppic_unload)
		ERRMSG("Could not find eppic_unload function\n");

	if (eppic_init(&eppic_cb)) {
		ERRMSG("Init failed \n");
		return FALSE;
	}

	/* Load/compile, execute and unload the eppic macro */
	eppic_load(name_config);
	eppic_unload(name_config);

	if (dlclose(handle))
		ERRMSG("dlclose failed: %s\n", dlerror());

	return TRUE;
}

static void
split_filter_info(struct filter_info *prev, unsigned long long next_paddr,
						size_t size)
{
	struct filter_info *new;

	if ((new = calloc(1, sizeof(struct filter_info))) == NULL) {
		ERRMSG("Can't allocate memory to split filter info\n");
		return;
	}

	/*
	 * copy over existing data from prev node and only update fields
	 * that differ. This approach will take care of copying over of any
	 * future member addition to filter_info structure.
	 */
	*new = *prev;
	new->paddr          = next_paddr;
	new->size           = size;
	prev->next          = new;
}

static void
update_erase_info(struct filter_info *fi)
{
	struct erase_info *ei;

	if (!fi->erase_info_idx)
		return;

	ei = &erase_info[fi->erase_info_idx];

	if (!ei->sizes) {
		/* First time, allocate sizes array */
		ei->sizes = calloc(ei->num_sizes, sizeof(long));
		if (!ei->sizes) {
			ERRMSG("Can't allocate memory for erase info sizes\n");
			return;
		}
	}
	ei->erased = 1;
	if (!fi->nullify)
		ei->sizes[fi->size_idx] += fi->size;
	else
		ei->sizes[fi->size_idx] = -1;
}

static int
extract_filter_info(unsigned long long start_paddr,
			unsigned long long end_paddr,
			struct filter_info *fl_info)
{
	struct filter_info *fi = filter_info;
	struct filter_info *prev = NULL;
	size_t size1, size2;

	if (!fl_info)
		return FALSE;

	while (fi) {
		if ((fi->paddr >= start_paddr) && (fi->paddr < end_paddr)) {
			size1 = end_paddr - fi->paddr;
			if (fi->size <= size1)
				break;
			size2 = fi->size - size1;
			fi->size = size1;
			split_filter_info(fi, fi->paddr + size1, size2);
			break;
		}
		prev = fi;
		fi   = fi->next;
	}
	if (!fi)
		return FALSE;

	*fl_info      = *fi;
	fl_info->next = NULL;

	/* Delete this node */
	if (!prev)
		filter_info = fi->next;
	else
		prev->next  = fi->next;
	update_erase_info(fi);
	free(fi);

	return TRUE;
}

/*
 * External functions.
 */
int
gather_filter_info(void)
{
	int ret = TRUE;

	/*
	 * Before processing filter config file, load the symbol data of
	 * loaded modules from vmcore.
	 */
	set_dwarf_debuginfo("vmlinux", NULL,
			    info->name_vmlinux, info->fd_vmlinux);
	if (!load_module_symbols())
		return FALSE;

	/*
	 * XXX: We support specifying both makedumpfile.conf and
	 * eppic macro at the same time. Whether to retain or discard the
	 * functionality provided by makedumpfile.conf is open for
	 * discussion
	 */
	if (info->name_filterconfig)
		ret = process_config_file(info->name_filterconfig);

	if (info->name_eppic_config)
		ret &= process_eppic_file(info->name_eppic_config);

	/*
	 * Remove modules symbol information, we dont need now.
	 * Reset the dwarf debuginfo to vmlinux to close open file
	 * descripter of module debuginfo file, if any.
	 */
	clean_module_symbols();
	set_dwarf_debuginfo("vmlinux", NULL,
			    info->name_vmlinux, info->fd_vmlinux);
	return ret;
}

void
clear_filter_info(void)
{
	struct filter_info *prev, *fi = filter_info;
	int i;

	/* Delete filter_info nodes that are left out. */
	while (fi) {
		prev = fi;
		fi = fi->next;
		free(prev);
	}
	filter_info = NULL;

	if (erase_info == NULL)
		return;

	for (i = 1; i < num_erase_info; i++) {
		free(erase_info[i].symbol_expr);
		free(erase_info[i].sizes);
	}
	free(erase_info);
	erase_info = NULL;
}

/*
 * Filter buffer if the physical address is in filter_info.
 */
void
filter_data_buffer(unsigned char *buf, unsigned long long paddr,
					size_t size)
{
	struct filter_info fl_info;
	unsigned char *buf_ptr;

	while (extract_filter_info(paddr, paddr + size, &fl_info)) {
		buf_ptr = buf + (fl_info.paddr - paddr);
		if (fl_info.nullify)
			memset(buf_ptr, 0, fl_info.size);
		else
			memset(buf_ptr, fl_info.erase_ch, fl_info.size);
	}
}

/*
 * Filter buffer if the physical address is in filter_info.
 */
void
filter_data_buffer_parallel(unsigned char *buf, unsigned long long paddr,
					size_t size, pthread_mutex_t *mutex)
{
	struct filter_info fl_info;
	unsigned char *buf_ptr;
	int found = FALSE;

	while (TRUE) {
		pthread_mutex_lock(mutex);
		found = extract_filter_info(paddr, paddr + size, &fl_info);
		pthread_mutex_unlock(mutex);

		if (found) {
			buf_ptr = buf + (fl_info.paddr - paddr);
			if (fl_info.nullify)
				memset(buf_ptr, 0, fl_info.size);
			else
				memset(buf_ptr, fl_info.erase_ch, fl_info.size);
		} else {
			break;
		}
	}
}

unsigned long
get_size_eraseinfo(void)
{
	unsigned long size_eraseinfo = 0;
	char size_str[MAX_SIZE_STR_LEN];
	struct erase_info *ei;
	struct filter_info *fl_info = filter_info;

	while (fl_info) {

		if (!fl_info->erase_info_idx)
			continue;
		ei = &erase_info[fl_info->erase_info_idx];
		if (fl_info->nullify)
			sprintf(size_str, "nullify\n");
		else
			sprintf(size_str, "size %ld\n", fl_info->size);

		size_eraseinfo += strlen("erase ") +
				strlen(ei->symbol_expr) + 1 +
				strlen(size_str);
		fl_info = fl_info->next;
	}

	return size_eraseinfo;
}