| /*************************************************************************** |
| * __________ __ ___. |
| * 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; |
| } |