diff options
author | Andrew Waterman <andrew@sifive.com> | 2019-03-31 00:49:57 -0700 |
---|---|---|
committer | Andrew Waterman <andrew@sifive.com> | 2019-03-31 00:50:15 -0700 |
commit | f49618ca9d674ec7e596118f85027c4b503862ac (patch) | |
tree | 3b6ad73cbe8760309f930b743950a2853ad17668 /fesvr/htif.cc | |
parent | 61cb96df00067ba61cf3816c74c18aef5677197a (diff) | |
download | spike-f49618ca9d674ec7e596118f85027c4b503862ac.zip spike-f49618ca9d674ec7e596118f85027c4b503862ac.tar.gz spike-f49618ca9d674ec7e596118f85027c4b503862ac.tar.bz2 |
Add fesvr; only globally install fesvr headers/libsstatic-link
Diffstat (limited to 'fesvr/htif.cc')
-rw-r--r-- | fesvr/htif.cc | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/fesvr/htif.cc b/fesvr/htif.cc new file mode 100644 index 0000000..d9ff341 --- /dev/null +++ b/fesvr/htif.cc @@ -0,0 +1,329 @@ +// See LICENSE for license details. + +#include "htif.h" +#include "rfb.h" +#include "elfloader.h" +#include "encoding.h" +#include <algorithm> +#include <assert.h> +#include <vector> +#include <queue> +#include <iostream> +#include <fstream> +#include <iomanip> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <getopt.h> + +/* Attempt to determine the execution prefix automatically. autoconf + * sets PREFIX, and pconfigure sets __PCONFIGURE__PREFIX. */ +#if !defined(PREFIX) && defined(__PCONFIGURE__PREFIX) +# define PREFIX __PCONFIGURE__PREFIX +#endif + +#ifndef TARGET_ARCH +# define TARGET_ARCH "riscv64-unknown-elf" +#endif + +#ifndef TARGET_DIR +# define TARGET_DIR "/" TARGET_ARCH "/bin/" +#endif + +static volatile bool signal_exit = false; +static void handle_signal(int sig) +{ + if (sig == SIGABRT || signal_exit) // someone set up us the bomb! + exit(-1); + signal_exit = true; + signal(sig, &handle_signal); +} + +htif_t::htif_t() + : mem(this), entry(DRAM_BASE), sig_addr(0), sig_len(0), + tohost_addr(0), fromhost_addr(0), exitcode(0), stopped(false), + syscall_proxy(this) +{ + signal(SIGINT, &handle_signal); + signal(SIGTERM, &handle_signal); + signal(SIGABRT, &handle_signal); // we still want to call static destructors +} + +htif_t::htif_t(int argc, char** argv) : htif_t() +{ + parse_arguments(argc, argv); + register_devices(); +} + +htif_t::htif_t(const std::vector<std::string>& args) : htif_t() +{ + int argc = args.size() + 1; + char * argv[argc]; + argv[0] = (char *) "htif"; + for (unsigned int i = 0; i < args.size(); i++) { + argv[i+1] = (char *) args[i].c_str(); + } + + parse_arguments(argc, argv); + register_devices(); +} + +htif_t::~htif_t() +{ + for (auto d : dynamic_devices) + delete d; +} + +void htif_t::start() +{ + if (!targs.empty() && targs[0] != "none") + load_program(); + + reset(); +} + +void htif_t::load_program() +{ + std::string path; + if (access(targs[0].c_str(), F_OK) == 0) + path = targs[0]; + else if (targs[0].find('/') == std::string::npos) + { + std::string test_path = PREFIX TARGET_DIR + targs[0]; + if (access(test_path.c_str(), F_OK) == 0) + path = test_path; + } + + if (path.empty()) + throw std::runtime_error( + "could not open " + targs[0] + + " (did you misspell it? If VCS, did you forget +permissive/+permissive-off?)"); + + // temporarily construct a memory interface that skips writing bytes + // that have already been preloaded through a sideband + class preload_aware_memif_t : public memif_t { + public: + preload_aware_memif_t(htif_t* htif) : memif_t(htif), htif(htif) {} + + void write(addr_t taddr, size_t len, const void* src) override + { + if (!htif->is_address_preloaded(taddr, len)) + memif_t::write(taddr, len, src); + } + + private: + htif_t* htif; + } preload_aware_memif(this); + + std::map<std::string, uint64_t> symbols = load_elf(path.c_str(), &preload_aware_memif, &entry); + + if (symbols.count("tohost") && symbols.count("fromhost")) { + tohost_addr = symbols["tohost"]; + fromhost_addr = symbols["fromhost"]; + } else { + fprintf(stderr, "warning: tohost and fromhost symbols not in ELF; can't communicate with target\n"); + } + + // detect torture tests so we can print the memory signature at the end + if (symbols.count("begin_signature") && symbols.count("end_signature")) + { + sig_addr = symbols["begin_signature"]; + sig_len = symbols["end_signature"] - sig_addr; + } +} + +void htif_t::stop() +{ + if (!sig_file.empty() && sig_len) // print final torture test signature + { + std::vector<uint8_t> buf(sig_len); + mem.read(sig_addr, sig_len, &buf[0]); + + std::ofstream sigs(sig_file); + assert(sigs && "can't open signature file!"); + sigs << std::setfill('0') << std::hex; + + const addr_t incr = 16; + assert(sig_len % incr == 0); + for (addr_t i = 0; i < sig_len; i += incr) + { + for (addr_t j = incr; j > 0; j--) + sigs << std::setw(2) << (uint16_t)buf[i+j-1]; + sigs << '\n'; + } + + sigs.close(); + } + + stopped = true; +} + +void htif_t::clear_chunk(addr_t taddr, size_t len) +{ + char zeros[chunk_max_size()]; + memset(zeros, 0, chunk_max_size()); + + for (size_t pos = 0; pos < len; pos += chunk_max_size()) + write_chunk(taddr + pos, std::min(len - pos, chunk_max_size()), zeros); +} + +int htif_t::run() +{ + start(); + + auto enq_func = [](std::queue<reg_t>* q, uint64_t x) { q->push(x); }; + std::queue<reg_t> fromhost_queue; + std::function<void(reg_t)> fromhost_callback = + std::bind(enq_func, &fromhost_queue, std::placeholders::_1); + + if (tohost_addr == 0) { + while (true) + idle(); + } + + while (!signal_exit && exitcode == 0) + { + if (auto tohost = mem.read_uint64(tohost_addr)) { + mem.write_uint64(tohost_addr, 0); + command_t cmd(mem, tohost, fromhost_callback); + device_list.handle_command(cmd); + } else { + idle(); + } + + device_list.tick(); + + if (!fromhost_queue.empty() && mem.read_uint64(fromhost_addr) == 0) { + mem.write_uint64(fromhost_addr, fromhost_queue.front()); + fromhost_queue.pop(); + } + } + + stop(); + + return exit_code(); +} + +bool htif_t::done() +{ + return stopped; +} + +int htif_t::exit_code() +{ + return exitcode >> 1; +} + +void htif_t::parse_arguments(int argc, char ** argv) +{ + optind = 0; // reset optind as HTIF may run getopt _after_ others + while (1) { + static struct option long_options[] = { HTIF_LONG_OPTIONS }; + int option_index = 0; + int c = getopt_long(argc, argv, "-h", long_options, &option_index); + + if (c == -1) break; + retry: + switch (c) { + case 'h': usage(argv[0]); + throw std::invalid_argument("User quered htif_t help text"); + case HTIF_LONG_OPTIONS_OPTIND: + if (optarg) dynamic_devices.push_back(new rfb_t(atoi(optarg))); + else dynamic_devices.push_back(new rfb_t); + break; + case HTIF_LONG_OPTIONS_OPTIND + 1: + // [TODO] Remove once disks are supported again + throw std::invalid_argument("--disk/+disk unsupported (use a ramdisk)"); + dynamic_devices.push_back(new disk_t(optarg)); + break; + case HTIF_LONG_OPTIONS_OPTIND + 2: + sig_file = optarg; + break; + case HTIF_LONG_OPTIONS_OPTIND + 3: + syscall_proxy.set_chroot(optarg); + break; + case '?': + if (!opterr) + break; + throw std::invalid_argument("Unknown argument (did you mean to enable +permissive parsing?)"); + case 1: { + std::string arg = optarg; + if (arg == "+rfb") { + c = HTIF_LONG_OPTIONS_OPTIND; + optarg = nullptr; + } + else if (arg.find("+rfb=") == 0) { + c = HTIF_LONG_OPTIONS_OPTIND; + optarg = optarg + 5; + } + else if (arg.find("+disk=") == 0) { + c = HTIF_LONG_OPTIONS_OPTIND + 1; + optarg = optarg + 6; + } + else if (arg.find("+signature=") == 0) { + c = HTIF_LONG_OPTIONS_OPTIND + 2; + optarg = optarg + 11; + } + else if (arg.find("+chroot=") == 0) { + c = HTIF_LONG_OPTIONS_OPTIND + 3; + optarg = optarg + 8; + } + else if (arg.find("+permissive-off") == 0) { + if (opterr) + throw std::invalid_argument("Found +permissive-off when not parsing permissively"); + opterr = 1; + break; + } + else if (arg.find("+permissive") == 0) { + if (!opterr) + throw std::invalid_argument("Found +permissive when already parsing permissively"); + opterr = 0; + break; + } + else { + if (!opterr) + break; + else { + optind--; + goto done_processing; + } + } + goto retry; + } + } + } + +done_processing: + while (optind < argc) + targs.push_back(argv[optind++]); + if (!targs.size()) { + usage(argv[0]); + throw std::invalid_argument("No binary specified (Did you forget it? Did you forget '+permissive-off' if running with +permissive?)"); + } +} + +void htif_t::register_devices() +{ + device_list.register_device(&syscall_proxy); + device_list.register_device(&bcd); + for (auto d : dynamic_devices) + device_list.register_device(d); +} + +void htif_t::usage(const char * program_name) +{ + printf("Usage: %s [EMULATOR OPTION]... [VERILOG PLUSARG]... [HOST OPTION]... BINARY [TARGET OPTION]...\n ", + program_name); + fputs("\ +Run a BINARY on the Rocket Chip emulator.\n\ +\n\ +Mandatory arguments to long options are mandatory for short options too.\n\ +\n\ +EMULATOR OPTIONS\n\ + Consult emulator.cc if using Verilator or VCS documentation if using VCS\n\ + for available options.\n\ +EMUALTOR VERILOG PLUSARGS\n\ + Consult generated-src*/*.plusArgs for available options\n\ +", stdout); + fputs("\n" HTIF_USAGE_OPTIONS, stdout); +} |