blob: 54cea65cb44615ff4564162efb5cf0bcd73eb6e1 [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 <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::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.)R";
/** SPI flash configuration options. */
struct SpiFlashOpts {
/** Input file in binary format. */
std::string input;
/** Target SPI device handle. */
std::string target;
/** Set to true to target Verilator environment. */
bool verilator = false;
/** 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;
}
/** 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'},
{"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:h?", long_options, nullptr);
if (c == -1) {
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->ftdi_options.device_serial_number = optarg;
break;
case 's':
options->verilator = true;
options->target = optarg;
break;
case '?':
case 'h':
PrintUsage(argc, argv);
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;
}
std::unique_ptr<SpiInterface> spi;
if (spi_flash_options.verilator) {
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;
if (!GetFileContents(spi_flash_options.input, &options.code)) {
return 1;
}
Updater updater(options, std::move(spi));
return updater.Run() ? 0 : 1;
}