|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | #include <assert.h> | 
|  | #include <fstream> | 
|  | #include <getopt.h> | 
|  | #include <iterator> | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  |  | 
|  | #include "sw/host/spiflash/ftdi_spi_interface.h" | 
|  | #include "sw/host/spiflash/spi_interface.h" | 
|  | #include "sw/host/spiflash/updater.h" | 
|  | #include "sw/host/spiflash/verilator_spi_interface.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using opentitan::spiflash::Frame; | 
|  | using opentitan::spiflash::FtdiSpiInterface; | 
|  | using opentitan::spiflash::SpiInterface; | 
|  | using opentitan::spiflash::Updater; | 
|  | using opentitan::spiflash::VerilatorSpiInterface; | 
|  |  | 
|  | constexpr char kUsageString[] = R"R( usage options: | 
|  | --input=Input image in binary format. | 
|  |  | 
|  | FTDI Options: | 
|  | [--dev-id="vid:pid"] FTDI device ID. | 
|  | vid: Vendor ID in string hex format. | 
|  | pid: Product ID in string hex format. | 
|  | [--dev-sn=string] FTDI serial number. Requires --dev-id to be set. | 
|  |  | 
|  | Verilator Options: | 
|  | [--verilator=filehandle] Enables Verilator mode with SPI filehandle. | 
|  |  | 
|  | DV Options: | 
|  | [--dump-frames=filehandle] Dump binary SPI flash frames in binary format. | 
|  | )R"; | 
|  |  | 
|  | /** SPI flash list of supported command actions. */ | 
|  | enum class SpiFlashAction { | 
|  | /** Invalid command action. */ | 
|  | kInvalid = 0, | 
|  |  | 
|  | /** Run SPI flash in FTDI mode. */ | 
|  | kFtdi, | 
|  |  | 
|  | /** Run SPI flash in Verilator mode. */ | 
|  | kVerilator, | 
|  |  | 
|  | /** Covert input binrary into frames. */ | 
|  | kDumpFrames, | 
|  |  | 
|  | /** Print usage information/help. */ | 
|  | kPrintUsage, | 
|  | }; | 
|  |  | 
|  | /** SPI flash configuration options. */ | 
|  | struct SpiFlashOpts { | 
|  | /** Input file in binary format. */ | 
|  | std::string input; | 
|  |  | 
|  | /** Target SPI device handle. */ | 
|  | std::string target; | 
|  |  | 
|  | /** Output filename to dump SPI frames */ | 
|  | std::string output_filename; | 
|  |  | 
|  | /** Set to SPI flash  mode of operation */ | 
|  | SpiFlashAction action = SpiFlashAction::kInvalid; | 
|  |  | 
|  | /** FTDI configuration options. */ | 
|  | FtdiSpiInterface::Options ftdi_options; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Get `filename` contents and store them in `contents`. Using std::string for | 
|  | * `filename` because underlying open file call requires a C string. | 
|  | */ | 
|  | bool GetFileContents(const std::string &filename, std::string *contents) { | 
|  | assert(contents); | 
|  | std::ifstream file_stream(filename, std::ios::in | std::ios::binary); | 
|  | if (!file_stream) { | 
|  | std::cerr << "Unable to open: " << filename << " errno: " << errno | 
|  | << std::endl; | 
|  | return false; | 
|  | } | 
|  | std::ostringstream in_stream; | 
|  | in_stream << file_stream.rdbuf(); | 
|  | file_stream.close(); | 
|  | *contents = in_stream.str(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Store the contetns of `code` formatted int SPI flash frame binary format | 
|  | * into `output_filename` | 
|  | */ | 
|  | bool DumpFramesToFile(const std::string &code, | 
|  | const std::string &output_filename) { | 
|  | std::vector<Frame> frames; | 
|  | if (!Updater::GenerateFrames(code, &frames)) { | 
|  | return false; | 
|  | } | 
|  | std::ofstream out_stream; | 
|  | out_stream.open(output_filename, std::ofstream::out | std::ofstream::binary); | 
|  | if (!out_stream.good()) { | 
|  | std::cerr << "Unable to open file: " << output_filename << std::endl; | 
|  | return false; | 
|  | } | 
|  | for (const Frame &f : frames) { | 
|  | out_stream.write(reinterpret_cast<const char *>(&f), sizeof(Frame)); | 
|  | if (!out_stream.good()) { | 
|  | std::cerr << "Detected write error. Output file may be corrupted." | 
|  | << std::endl; | 
|  | out_stream.close(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | out_stream.close(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** Print help menu. */ | 
|  | static void PrintUsage(int argc, char *argv[]) { | 
|  | assert(argc >= 1); | 
|  | std::cerr << argv[0] << kUsageString << std::endl; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Extract the vendor and product ID from `device_id` and store the values | 
|  | * in `options`. | 
|  | * | 
|  | * This function may throw exceptions. | 
|  | * | 
|  | * @return true on success. | 
|  | */ | 
|  | bool ParseDeviceID(const std::string &device_id, SpiFlashOpts *options) { | 
|  | size_t token_pos = device_id.find(':'); | 
|  | if (token_pos == std::string::npos) { | 
|  | std::cerr << "Unable to find device separator ':' in --device-id." | 
|  | << std::endl; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::string vendor_id = device_id.substr(/*pos=*/0, /*len=*/token_pos); | 
|  | options->ftdi_options.device_vendor_id = | 
|  | std::stoi(vendor_id, /*pos=*/0, /*base=*/16); | 
|  |  | 
|  | std::string product_id = | 
|  | device_id.substr(/*pos=*/token_pos + 1, /*len=*/std::string::npos); | 
|  | options->ftdi_options.device_product_id = | 
|  | std::stoi(product_id, /*pos=*/0, /*base=*/16); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse command line arguments and store results in `options`. | 
|  | */ | 
|  | bool ParseArgs(int argc, char **argv, SpiFlashOpts *options) { | 
|  | assert(options); | 
|  | const struct option long_options[] = { | 
|  | {"input", required_argument, nullptr, 'i'}, | 
|  | {"dev-id", required_argument, nullptr, 'd'}, | 
|  | {"dev-sn", required_argument, nullptr, 'n'}, | 
|  | {"dump-frames", required_argument, nullptr, 'x'}, | 
|  | {"verilator", required_argument, nullptr, 's'}, | 
|  | {"help", no_argument, nullptr, 'h'}, | 
|  | {nullptr, no_argument, nullptr, 0}}; | 
|  |  | 
|  | while (true) { | 
|  | int c = getopt_long(argc, argv, "i:d:n:s:x:h?", long_options, nullptr); | 
|  | if (c == -1) { | 
|  | // if only input file was given default to using FTDI | 
|  | if (!options->input.empty() && | 
|  | options->action == SpiFlashAction::kInvalid) { | 
|  | options->action = SpiFlashAction::kFtdi; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | switch (c) { | 
|  | case 0: | 
|  | break; | 
|  | case 'i': | 
|  | options->input = optarg; | 
|  | break; | 
|  | case 'd': | 
|  | if (!ParseDeviceID(std::string(optarg), options)) { | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case 'n': | 
|  | options->action = SpiFlashAction::kFtdi; | 
|  | options->ftdi_options.device_serial_number = optarg; | 
|  | break; | 
|  | case 's': | 
|  | options->action = SpiFlashAction::kVerilator; | 
|  | options->target = optarg; | 
|  | break; | 
|  | case 'x': | 
|  | options->action = SpiFlashAction::kDumpFrames; | 
|  | options->output_filename = optarg; | 
|  | break; | 
|  | case '?': | 
|  | case 'h': | 
|  | options->action = SpiFlashAction::kPrintUsage; | 
|  | break; | 
|  | default:; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int main(int argc, char **argv) { | 
|  | SpiFlashOpts spi_flash_options; | 
|  | if (!ParseArgs(argc, argv, &spi_flash_options)) { | 
|  | std::cerr << "Failed to parse command line options." << std::endl; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (spi_flash_options.action == SpiFlashAction::kInvalid) { | 
|  | std::cerr << "Unable to detect valid command line arguments." << std::endl; | 
|  | PrintUsage(argc, argv); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (spi_flash_options.action == SpiFlashAction::kPrintUsage) { | 
|  | PrintUsage(argc, argv); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | std::string code; | 
|  | if (!GetFileContents(spi_flash_options.input, &code)) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (spi_flash_options.action == SpiFlashAction::kDumpFrames) { | 
|  | return DumpFramesToFile(code, spi_flash_options.output_filename) ? 0 : 1; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SpiInterface> spi; | 
|  | if (spi_flash_options.action == SpiFlashAction::kVerilator) { | 
|  | spi = std::make_unique<VerilatorSpiInterface>(spi_flash_options.target); | 
|  | } else { | 
|  | spi = std::make_unique<FtdiSpiInterface>(spi_flash_options.ftdi_options); | 
|  | } | 
|  | if (!spi->Init()) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Updater::Options options; | 
|  | options.code = code; | 
|  |  | 
|  | Updater updater(options, std::move(spi)); | 
|  | return updater.Run() ? 0 : 1; | 
|  | } |