blob: 4122a0cd27cbf9c92d7875c10eb51c867780128d [file] [log] [blame]
// 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;
}