rockbox/utils/regtools/regtool.cpp

631 lines
19 KiB
C++
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2016 by Amaury Pouly
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "soc_desc.hpp"
#include "soc_desc_v1.hpp"
#include <cstdio>
#include <cstdlib>
#include <map>
#include <set>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cstring>
#include <dirent.h>
#include <getopt.h>
#include <regex>
using namespace soc_desc;
int g_verbose = 0;
bool g_inline = false;
bool g_print_zero = false;
bool g_regex_mode = false;
std::regex_constants::syntax_option_type g_regex_flags;
std::regex_constants::syntax_option_type parse_regex_mode(const std::string& mode)
{
std::regex_constants::syntax_option_type flags;
size_t index = 0;
while(index < mode.size())
{
size_t end = mode.find(',', index);
if(end == std::string::npos)
end = mode.size();
std::string opt = mode.substr(index, end - index);
if(opt == "icase")
flags |= std::regex_constants::icase;
else if(opt == "ECMAScript")
flags |= std::regex_constants::ECMAScript;
else if(opt == "basic")
flags |= std::regex_constants::basic;
else if(opt == "extended")
flags |= std::regex_constants::extended;
else if(opt == "awk")
flags |= std::regex_constants::awk;
else if(opt == "grep")
flags |= std::regex_constants::grep;
else if(opt == "egrep")
flags |= std::regex_constants::egrep;
else
{
fprintf(stderr, "Invalid regex option '%s'\n", opt.c_str());
exit(1);
}
index = end + 1;
}
return flags;
}
void print_path(node_ref_t node, bool nl = true)
{
printf("%s", node.soc().get()->name.c_str());
std::vector< std::string > path = node.path();
for(size_t i = 0; i < path.size(); i++)
printf(".%s", path[i].c_str());
if(nl)
printf("\n");
}
template<typename T>
std::string to_str(const T& t)
{
std::ostringstream oss;
oss << t;
return oss.str();
}
std::string get_path(node_inst_t inst)
{
if(!inst.is_root())
{
std::string path = get_path(inst.parent()) + "." + inst.name();
if(inst.is_indexed())
path += "[" + to_str(inst.index()) + "]";
return path;
}
else
return inst.soc().get()->name;
}
void print_inst(node_inst_t inst, bool addr = true, bool nl = true)
{
if(!inst.is_root())
{
print_inst(inst.parent(), false);
printf(".%s", inst.name().c_str());
if(inst.is_indexed())
printf("[%u]", (unsigned)inst.index());
}
else
printf("%s", inst.soc().get()->name.c_str());
if(addr)
{
printf(" @ %#x", inst.addr());
if(nl)
printf("\n");
}
}
void find_insts(std::vector< node_inst_t >& list, node_inst_t inst, soc_addr_t addr)
{
if(inst.addr() == addr)
{
/* only keep matches that are registers */
if(inst.node().reg().valid())
list.push_back(inst);
}
std::vector< node_inst_t > children = inst.children();
for(size_t i = 0; i < children.size(); i++)
find_insts(list, children[i], addr);
}
void find_insts(std::vector< node_inst_t >& insts,
std::vector< soc_t >& soc_list, soc_addr_t addr)
{
for(size_t i = 0; i < soc_list.size(); i++)
find_insts(insts, soc_ref_t(&soc_list[i]).root_inst(), addr);
}
const size_t NO_INDEX = (size_t)-1;
/* index is set to NO_INDEX if there is no index */
bool parse_name(std::string& name, std::string& component, size_t& index)
{
size_t i = 0;
/* name must be of the form [a-zA-Z0-9_]+ */
while(i < name.size() && (isalnum(name[i]) || name[i] == '_'))
i++;
if(i == 0)
return false;
component = name.substr(0, i);
/* must stop at the end, or on '.' or on '[' */
if(name.size() == i)
{
index = NO_INDEX;
name = name.substr(i);
return true;
}
else if(name[i] == '.')
{
index = NO_INDEX;
name = name.substr(i + 1);
return true;
}
else if(name[i] == '[')
{
/* parse index */
char *end;
index = strtoul(name.c_str() + i + 1, &end, 0);
/* must stop on ']'. Also strtoul is happy with an empty string, check for this */
if(*end != ']' || end == name.c_str() + i + 1)
return false;
i = end + 1 - name.c_str();
/* check if we have a '.' after that, or the end */
if(i < name.size() && name[i] != '.')
return false;
name = name.substr(i + 1);
return true;
}
else
return false;
}
int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::regex& regex)
{
/* only keep matches that are registers */
if(inst.node().reg().valid())
{
std::string path = get_path(inst);
if(regex_match(path, regex))
matches.push_back(inst);
}
std::vector< node_inst_t > children = inst.children();
for(size_t i = 0; i < children.size(); i++)
find_insts(matches, children[i], regex);
return 0;
}
int find_insts_regex(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list, std::string str)
{
auto flags = g_regex_flags | std::regex_constants::optimize; /* we will match a lot */
/* any error during construction or mayching will throw exception, and print
* an error message so don't catch them */
std::regex regex(str, flags);
for(size_t i = 0; i < soc_list.size(); i++)
if(find_insts(matches, soc_ref_t(&soc_list[i]).root_inst(), regex) != 0)
return 1;
return 0;
}
int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::string name)
{
if(name.empty())
{
if(inst.node().reg().valid())
matches.push_back(inst);
return 0;
}
std::string component;
size_t index;
bool ok = parse_name(name, component, index);
if(!ok)
{
fprintf(stderr, "invalid name '%s'\n", name.c_str());
return 1;
}
if(index == NO_INDEX)
inst = inst.child(component);
else
inst = inst.child(component, index);
if(inst.valid())
return find_insts(matches, inst, name);
else
return 0;
}
int find_insts(std::vector< node_inst_t >& matches, soc_t& soc, std::string name)
{
return find_insts(matches, soc_ref_t(&soc).root_inst(), name);
}
int find_insts(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list,
std::string name)
{
/* regex mode is special */
if(g_regex_mode)
return find_insts_regex(matches, soc_list, name);
/* if name is an integer, parse it */
char *end;
unsigned long addr = strtoul(name.c_str(), &end, 0);
if(*end == 0)
{
find_insts(matches, soc_list, addr);
return 0;
}
/* else assume it's a name */
std::string name_copy = name;
std::string component;
size_t index;
bool ok = parse_name(name_copy, component, index);
if(!ok)
{
fprintf(stderr, "invalid name '%s'\n", name.c_str());
return 1;
}
/* a soc cannot be indexed */
for(size_t i = 0; i < soc_list.size(); i++)
{
int ret;
if(index == NO_INDEX && soc_list[i].name == component)
ret = find_insts(matches, soc_list[i], name_copy);
else
ret = find_insts(matches, soc_list[i], name);
if(ret != 0)
return ret;
}
return 0;
}
void do_describe(node_inst_t inst, soc_word_t *decode_val)
{
if(decode_val)
printf(" = %#lx", (unsigned long)*decode_val);
if(g_inline)
printf(" {");
else
printf("\n");
std::vector< field_ref_t > fields = inst.node().reg().fields();
bool first = true;
soc_word_t mask = 0; /* mask of all decoded bits */
/* special index fields.size() means "undecoded bits" */
for(size_t i = 0; i <= fields.size(); i++)
{
unsigned long val = 0;
bool dont_print = false;
field_t f;
if(i == fields.size())
{
/* create fake field */
f.name = "undecoded bits";
f.pos = 0;
f.width = inst.node().reg().get()->width;
val = decode_val ? *decode_val & ~mask : 0;
/* only print if decoding and something was left */
dont_print = (val == 0);
}
else
{
f = *fields[i].get();
val = decode_val ? f.extract(*decode_val) : 0;
/* don't print zero fields unless asked */
dont_print = decode_val && !g_print_zero && val == 0;
}
if(dont_print)
continue;
if(g_inline)
printf("%s", first ? " " : ", ");
else
printf(" ");
printf("%s", f.name.c_str());
first = false;
if(f.width == 1)
printf("[%zu]", f.pos);
else
printf("[%zu:%zu]", f.pos + f.width - 1, f.pos);
/* decode if needed */
if(decode_val)
{
/* track what we decoded */
mask |= ~f.bitmask();
printf(" = %#lx", val);
}
/* newline if need */
if(!g_inline)
printf("\n");
}
if(g_inline)
printf(" }\n");
}
int do_find(std::vector< soc_t >& soc_list, int argc, char **argv, bool describe,
soc_word_t *decode_val = nullptr)
{
if(argc != 1)
{
fprintf(stderr, "action 'find' takes on argument: the name or address of a register\n");
return 1;
}
std::vector< node_inst_t > matches;
int ret = find_insts(matches, soc_list, argv[0]);
if(ret != 0)
return 0;
/* print matches */
if(matches.size() > 0)
{
for(size_t i = 0; i < matches.size(); i++)
{
print_inst(matches[i], true, !describe);
if(describe)
do_describe(matches[i], decode_val);
}
return 0;
}
else
{
fprintf(stderr, "No matches\n");
return 1;
}
return 0;
}
int do_decode(std::vector< soc_t >& soc_list, int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "action 'decode' takes two arguments: the register and the value\n");
return 1;
}
char *end;
soc_word_t val = strtoul(argv[1], &end, 0);
if(*end)
{
fprintf(stderr, "invalid value '%s'\n", argv[1]);
return 1;
}
return do_find(soc_list, argc - 1, argv, true, &val);
}
void print_context(const error_context_t& ctx)
{
for(size_t j = 0; j < ctx.count(); j++)
{
err_t e = ctx.get(j);
switch(e.level())
{
case err_t::INFO: printf("[INFO]"); break;
case err_t::WARNING: printf("[WARN]"); break;
case err_t::FATAL: printf("[FATAL]"); break;
default: printf("[UNK]"); break;
}
if(e.location().size() != 0)
printf(" %s:", e.location().c_str());
printf(" %s\n", e.message().c_str());
}
}
int usage()
{
printf("usage: regtool [options] <action> [args]\n");
printf("options:\n");
printf(" -h Display this help\n");
printf(" -f <regfile> Load a register file\n");
printf(" -d <regdir> Specify a directory where to look for register files\n");
printf(" -v Increase verbosity level\n");
printf(" -s <soc list> Limit search to a one or more socs (comma separated)\n");
printf(" -i Describe/decode in one line\n");
printf(" -z Print fields even when the value is zero\n");
printf(" -r <mode> Enable regex mode\n");
printf("\n");
printf("actions:\n");
printf(" find <addr> Find all registers that match this address\n");
printf(" find <name> Find the registers that match this name\n");
printf(" describe <reg> Describe a register (either found by address or name)\n");
printf(" decode <reg> <val> Decode a register value\n");
printf("By default, regtool will look for register files in desc/, but if\n");
printf("any file or directory is specified, regtool will ignore the default directory\n");
printf("Adresses can be in decimal or hexadecimal (using 0x prefix).\n");
printf("Names can be fully qualified with a soc name or left ambiguous.\n");
printf(" <reg> ::= <soc>.<regpath> | <regpath>\n");
printf(" <regpath> ::= (<regcomp>.)*<regcomp>\n");
printf(" <regcomp> ::= <name> | <name>[<index>]\n");
printf("In regex mode, all commands expect a regular expression that is matched\n");
printf("against the full path (<soc>.<regpath>) of every register. The regex must\n");
printf("follow the C++11 standard and accepts the following, comma-separated options:\n");
printf(" icase Case insensitive match\n");
printf(" ECMAScript Use ECMAScript grammar\n");
printf(" basic Use basic POSIX grammar\n");
printf(" extended Use extended basic grammar\n");
printf(" awk Use awk grammar\n");
printf(" grep Use grep grammar\n");
printf(" egrep Use egrep grammar\n");
printf("Examples:\n");
printf(" regtool -d desc/ -s stmp3700,imx233 find 0x8001c310\n");
printf(" regtool find DIGCTL.CHIPID\n");
printf(" regtool describe imx233.DIGCTL.CHIPID\n");
printf(" regtool -f desc/regs-stmp3780.xml decode 0x8001c310 0x37800001\n");
printf(" regtool -r awk find 'imx233\\.LCDIF\\..*'\n");
return 1;
}
std::string my_dirname(const std::string& filename)
{
size_t idx = filename.find_last_of("/\\");
if(idx == std::string::npos)
return filename;
else
return filename.substr(0, idx);
}
/* warn controls whether we warn about error at lowest verbosity: we warn
* for files explicitely loaded but not the one listed in directories */
bool load_file(std::vector< soc_t >& soc_list, const std::string& file, bool user_load)
{
if(g_verbose >= 2)
fprintf(stderr, "Loading file '%s'...\n", file.c_str());
error_context_t ctx;
soc_t s;
bool ret = parse_xml(file.c_str(), s, ctx);
if(g_verbose >= 1 || user_load)
{
if(ctx.count() != 0)
fprintf(stderr, "In file %s:\n", file.c_str());
print_context(ctx);
}
if(!ret)
{
if(g_verbose >= 1 || user_load)
fprintf(stderr, "Cannot parse file '%s'\n", file.c_str());
return !user_load; /* error only if user loaded */
}
soc_list.push_back(s);
return true;
}
bool load_dir(std::vector< soc_t >& soc_list, const std::string& dirname, bool user_load)
{
if(g_verbose >= 2)
fprintf(stderr, "Loading directory '%s'...\n", dirname.c_str());
DIR *dir = opendir(dirname.c_str());
if(dir == NULL)
{
if(g_verbose >= 1 || user_load)
fprintf(stderr, "Warning: cannot open directory '%s'\n", dirname.c_str());
return !user_load; /* error only if user loaded */
}
struct dirent *ent;
while((ent = readdir(dir)))
{
std::string name(ent->d_name);
/* only list *.xml */
if(name.size() < 4 || name.substr(name.size() - 4) != ".xml")
continue;
if(!load_file(soc_list, dirname + "/" + name, false))
{
closedir(dir);
return false;
}
}
closedir(dir);
return true;
}
void add_socs_to_list(std::set< std::string >& soc_list, const std::string& list)
{
size_t idx = 0;
while(idx < list.size())
{
size_t next = list.find(',', idx);
if(next == std::string::npos)
next = list.size();
soc_list.insert(list.substr(idx, next - idx));
idx = next + 1;
}
}
int main(int argc, char **argv)
{
std::vector< std::string > g_soc_dir;
std::vector< std::string > g_soc_files;
std::vector< soc_t > g_soc_list;
std::set< std::string> g_allowed_soc;
if(argc <= 1)
return usage();
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "hf:d:vs:izr:", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case 'h':
return usage();
case 's':
add_socs_to_list(g_allowed_soc, optarg);
break;
case 'd':
g_soc_dir.push_back(std::string(optarg));
break;
case 'f':
g_soc_files.push_back(std::string(optarg));
break;
case 'v':
g_verbose++;
break;
case 'i':
g_inline = true;
break;
case 'z':
g_print_zero = true;
break;
case 'r':
g_regex_mode = true;
g_regex_flags = parse_regex_mode(optarg);
break;
default:
abort();
}
}
if(argc == optind)
{
fprintf(stderr, "You need at least one action\n");
return 2;
}
/* if no file or directory, add default */
if(g_soc_files.empty() && g_soc_dir.empty())
load_dir(g_soc_list, my_dirname(argv[0]) + "/desc", false);
/* load directories */
for(size_t i = 0; i < g_soc_dir.size(); i++)
load_dir(g_soc_list, g_soc_dir[i], true);
/* load files */
for(size_t i = 0; i < g_soc_files.size(); i++)
load_file(g_soc_list, g_soc_files[i], true);
/* filter soc list */
if(g_allowed_soc.size() > 0)
{
for(size_t i = 0; i < g_soc_list.size(); i++)
{
if(g_allowed_soc.find(g_soc_list[i].name) == g_allowed_soc.end())
{
std::swap(g_soc_list[i], g_soc_list.back());
g_soc_list.pop_back();
i--;
}
}
}
/* print */
if(g_verbose >= 1)
{
fprintf(stderr, "Available socs after filtering:");
for(size_t i = 0; i < g_soc_list.size(); i++)
fprintf(stderr, " %s", g_soc_list[i].name.c_str());
fprintf(stderr, "\n");
}
std::string action = argv[optind];
argc -= optind + 1;
argv += optind + 1;
if(action == "find")
return do_find(g_soc_list, argc, argv, false);
if(action == "describe")
return do_find(g_soc_list, argc, argv, true);
if(action == "decode")
return do_decode(g_soc_list, argc, argv);
fprintf(stderr, "unknown action '%s'\n", action.c_str());
return 1;
}