566 lines
16 KiB
C++
566 lines
16 KiB
C++
/**
|
|
* Copyright (C) 2020 Xilinx, Inc
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"). You may
|
|
* not use this file except in compliance with the License. A copy of the
|
|
* License is located at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
//
|
|
// This utility is implemented by porting flash code from xbmgmt. Since that
|
|
// it's used only by non-XRT users, we do not expect to maintain and enhance
|
|
// this code a lot in the future. Hence, no cleanup effort is ever attempted
|
|
// while porting the code from xbmgmt at this point.
|
|
// If it works, don't change it.
|
|
//
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <sys/types.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <unistd.h>
|
|
|
|
#include "pcidev.h"
|
|
#include "xspi.h"
|
|
#include "xqspips.h"
|
|
#include "firmware_image.h"
|
|
|
|
const option flash_opts[] = {
|
|
// Key option to identify flash operation, must be 0
|
|
{ "primary", required_argument, nullptr, '0' },
|
|
|
|
{ "force", no_argument, nullptr, '1' },
|
|
{ "card", required_argument, nullptr, '2' },
|
|
{ "secondary", required_argument, nullptr, '3' },
|
|
{ "bar", required_argument, nullptr, '4' },
|
|
{ "bar-offset", required_argument, nullptr, '5' },
|
|
{ nullptr, 0, nullptr, 0 },
|
|
};
|
|
|
|
const option reset_opts[] = {
|
|
// Key option to identify reset operation, must be 0
|
|
{ "factory-reset", no_argument, nullptr, '0' },
|
|
|
|
{ "force", no_argument, nullptr, '1' },
|
|
{ "card", required_argument, nullptr, '2' },
|
|
{ "dual-flash", no_argument, nullptr, '3' },
|
|
{ "bar", required_argument, nullptr, '4' },
|
|
{ "bar-offset", required_argument, nullptr, '5' },
|
|
{ nullptr, 0, nullptr, 0 },
|
|
};
|
|
|
|
const option qspips_erase_opts[] = {
|
|
// Key option to identify flash operation, must be 0
|
|
{ "qspips-erase", no_argument, nullptr, '0' },
|
|
|
|
{ "card", required_argument, nullptr, '1' },
|
|
{ "offset", required_argument, nullptr, '2' },
|
|
{ "length", required_argument, nullptr, '3' },
|
|
{ "flash-type", required_argument, nullptr, '4' },
|
|
{ "bar", required_argument, nullptr, '5' },
|
|
{ "bar-offset", required_argument, nullptr, '6' },
|
|
{ "force", no_argument, nullptr, '7' },
|
|
{ nullptr, 0, nullptr, 0 },
|
|
};
|
|
|
|
const option qspips_flash_opts[] = {
|
|
// Key option to identify flash operation, must be 0
|
|
{ "qspips-flash", no_argument, nullptr, '0' },
|
|
|
|
{ "card", required_argument, nullptr, '1' },
|
|
{ "input", required_argument, nullptr, '2' },
|
|
{ "offset", required_argument, nullptr, '3' },
|
|
{ "flash-type", required_argument, nullptr, '4' },
|
|
{ "bar", required_argument, nullptr, '5' },
|
|
{ "bar-offset", required_argument, nullptr, '6' },
|
|
{ "force", no_argument, nullptr, '7' },
|
|
{ nullptr, 0, nullptr, 0 },
|
|
};
|
|
|
|
const option qspips_readback_opts[] = {
|
|
// Key option to identify flash operation, must be 0
|
|
{ "qspips-read", no_argument, nullptr, '0' },
|
|
|
|
{ "card", required_argument, nullptr, '1' },
|
|
{ "output", required_argument, nullptr, '2' },
|
|
{ "offset", required_argument, nullptr, '3' },
|
|
{ "length", required_argument, nullptr, '4' },
|
|
{ "flash-type", required_argument, nullptr, '5' },
|
|
{ "bar", required_argument, nullptr, '6' },
|
|
{ "bar-offset", required_argument, nullptr, '7' },
|
|
{ nullptr, 0, nullptr, 0 },
|
|
};
|
|
|
|
static const char *option_key(const option *opts)
|
|
{
|
|
while (opts->name != nullptr) {
|
|
if (opts->val == '0')
|
|
return opts->name;
|
|
opts++;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool is_op(const option *opts, int argc, char *argv[])
|
|
{
|
|
const char *key = option_key(opts);
|
|
std::string optkey = "--";
|
|
|
|
if (key == nullptr)
|
|
return false;
|
|
|
|
optkey += key;
|
|
for (int i = 0; i < argc; i++) {
|
|
if (optkey.compare(argv[i]) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void sudoOrDie()
|
|
{
|
|
const char* SudoMessage = "ERROR: root privileges required.";
|
|
if ((getuid() == 0) || (geteuid() == 0))
|
|
return;
|
|
std::cout << SudoMessage << std::endl;
|
|
exit(-EPERM);
|
|
}
|
|
|
|
static bool canProceed()
|
|
{
|
|
std::string input;
|
|
bool answered = false;
|
|
bool proceed = false;
|
|
|
|
while (!answered) {
|
|
std::cout << "Are you sure you wish to proceed? [y/n]: ";
|
|
std::cin >> input;
|
|
answered = (input.compare("y") == 0 || input.compare("n") == 0);
|
|
}
|
|
|
|
proceed = (input.compare("y") == 0);
|
|
if (!proceed)
|
|
std::cout << "Action canceled." << std::endl;
|
|
return proceed;
|
|
}
|
|
|
|
static void printHelp(const char *fname)
|
|
{
|
|
auto tmp = std::unique_ptr<char, decltype(std::free)*>{strdup(fname), std::free};
|
|
|
|
std::cout << "Usage: " << std::endl;
|
|
|
|
std::cout << basename(tmp.get()) << " [options]" << std::endl;
|
|
std::cout << "\nOptions:" << std::endl;
|
|
|
|
std::cout << "\n\"SPI flash\"\n";
|
|
std::cout << " --primary, <MCS-path>, must be 1st option\n";
|
|
std::cout << " [--secondary <MCS-path>], default is empty\n";
|
|
std::cout << " --card <BDF>\n";
|
|
std::cout << " [--force, yes for prompt]\n";
|
|
std::cout << " [--bar <BAR-index-for-QSPI>], default is 0\n";
|
|
std::cout << " [--bar-offset <BAR-offset-for-QSPI>], default is 0x40000\n";
|
|
|
|
std::cout << "\n\"SPI factory-reset\"\n";
|
|
std::cout << " --factory-reset, must be 1st option\n";
|
|
std::cout << " [--dual-flash]\n";
|
|
std::cout << " --card <BDF>\n";
|
|
std::cout << " [--force, yes for prompt]\n";
|
|
std::cout << " [--bar <BAR-index-for-QSPI>], default is 0\n";
|
|
std::cout << " [--bar-offset <BAR-offset-for-QSPI>], default is 0x40000\n";
|
|
|
|
std::cout << "\n\"QSPIPS erase\"\n";
|
|
std::cout << " --qspips-erase, must be 1st option\n";
|
|
std::cout << " --card <BDF>\n";
|
|
std::cout << " [--offset <offset-on-flash-to-start-with>], default is 0\n";
|
|
std::cout << " [--length <length-to-read>], default is 96MB\n";
|
|
std::cout << " [--flash-type <qspips-flash-type>], default is qspi_ps_x2_single\n";
|
|
std::cout << " [--bar <BAR-index-for-QSPIPS>], default is 0\n";
|
|
std::cout << " [--bar-offset <BAR-offset-for-QSPIPS>], default is 0x40000\n";
|
|
std::cout << " [--force, yes for prompt]\n";
|
|
|
|
std::cout << "\n\"QSPIPS flash\"\n";
|
|
std::cout << " --qspips-flash, must be 1st option\n";
|
|
std::cout << " --input <path-to-qspips-BOOT-BIN-file>\n";
|
|
std::cout << " --card <BDF>\n";
|
|
std::cout << " [--offset <offset-on-flash-to-start-with>], default is 0\n";
|
|
std::cout << " [--flash-type <qspips-flash-type>], default is qspi_ps_x2_single\n";
|
|
std::cout << " [--bar <BAR-index-for-QSPIPS>], default is 0\n";
|
|
std::cout << " [--bar-offset <BAR-offset-for-QSPIPS>], default is 0x40000\n";
|
|
std::cout << " [--force, yes for prompt]\n";
|
|
|
|
std::cout << "\n\"QSPIPS read back\"\n";
|
|
std::cout << " --qspips-read, must be 1st option\n";
|
|
std::cout << " --output <output-file-to-save-read-contents>\n";
|
|
std::cout << " --card <BDF>\n";
|
|
std::cout << " [--offset <offset-on-flash-to-start-with>], default is 0\n";
|
|
std::cout << " [--length <length-to-read>], default is 128MB\n";
|
|
std::cout << " [--flash-type <qspips-flash-type>], default is qspi_ps_x2_single\n";
|
|
std::cout << " [--bar <BAR-index-for-QSPIPS>], default is 0\n";
|
|
std::cout << " [--bar-offset <BAR-offset-for-QSPIPS>], default is 0x40000\n";
|
|
}
|
|
|
|
int reset(int argc, char *argv[])
|
|
{
|
|
bool force = false;
|
|
std::string bdf;
|
|
const char *fname = argv[0];
|
|
int bar = 0;
|
|
size_t baroff = INVALID_OFFSET;
|
|
bool dualflash = false;
|
|
|
|
sudoOrDie();
|
|
|
|
while (true) {
|
|
const auto opt = getopt_long(argc, argv, "", reset_opts, nullptr);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case '2':
|
|
bdf = std::string(optarg);
|
|
break;
|
|
case '1':
|
|
force = true;
|
|
break;
|
|
case '0':
|
|
break;
|
|
case '3':
|
|
dualflash = true;
|
|
break;
|
|
case '4':
|
|
bar = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '5':
|
|
baroff = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
default:
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (bdf.empty()) {
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
std::cout
|
|
<< "About to revert to golden image for card " << bdf << std::endl;
|
|
|
|
if (!force && !canProceed())
|
|
return -ECANCELED;
|
|
|
|
pcidev::pci_device dev(bdf, bar, baroff);
|
|
XSPI_Flasher xspi(&dev, dualflash);
|
|
return xspi.revertToMFG();
|
|
}
|
|
|
|
int flash(int argc, char *argv[])
|
|
{
|
|
int ret = 0;
|
|
bool force = false;
|
|
std::string primary_file;
|
|
std::string secondary_file;
|
|
std::string bdf;
|
|
const char *fname = argv[0];
|
|
int bar = 0;
|
|
size_t baroff = INVALID_OFFSET;
|
|
|
|
sudoOrDie();
|
|
|
|
while (true) {
|
|
const auto opt = getopt_long(argc, argv, "", flash_opts, nullptr);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case '2':
|
|
bdf = std::string(optarg);
|
|
break;
|
|
case '1':
|
|
force = true;
|
|
break;
|
|
case '0':
|
|
primary_file = std::string(optarg);
|
|
break;
|
|
case '3':
|
|
secondary_file = std::string(optarg);
|
|
break;
|
|
case '4':
|
|
bar = std::stoi(optarg);
|
|
break;
|
|
case '5':
|
|
baroff = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
default:
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (bdf.empty() || primary_file.empty()) {
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
std::cout
|
|
<< "About to flash below MCS bitstream onto card "
|
|
<< bdf << ":" << std::endl;
|
|
std::cout << primary_file << std::endl;
|
|
if (!secondary_file.empty())
|
|
std::cout << secondary_file << std::endl;
|
|
|
|
if (!force && !canProceed())
|
|
return -ECANCELED;
|
|
|
|
pcidev::pci_device dev(bdf, bar, baroff);
|
|
XSPI_Flasher xspi(&dev, !secondary_file.empty());
|
|
|
|
if (secondary_file.empty()) {
|
|
firmwareImage pri(primary_file.c_str());
|
|
if (pri.fail())
|
|
return -EINVAL;
|
|
ret = xspi.xclUpgradeFirmware1(pri);
|
|
} else {
|
|
firmwareImage pri(primary_file.c_str());
|
|
firmwareImage sec(secondary_file.c_str());
|
|
if (pri.fail() || sec.fail())
|
|
return -EINVAL;
|
|
ret = xspi.xclUpgradeFirmware2(pri, sec);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int qspips_erase(int argc, char *argv[])
|
|
{
|
|
std::string bdf;
|
|
const char *fname = argv[0];
|
|
int bar = 0;
|
|
size_t baroff = INVALID_OFFSET;
|
|
std::string flash_type;
|
|
size_t offset = 0, len = GOLDEN_BASE;
|
|
bool force = false;
|
|
|
|
sudoOrDie();
|
|
|
|
while (true) {
|
|
const auto opt = getopt_long(argc, argv, "", qspips_erase_opts, nullptr);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case '0':
|
|
break;
|
|
case '1':
|
|
bdf = std::string(optarg);
|
|
break;
|
|
case '2':
|
|
offset = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '3':
|
|
len = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '4':
|
|
flash_type = std::string(optarg);
|
|
break;
|
|
case '5':
|
|
bar = std::stoi(optarg);
|
|
break;
|
|
case '6':
|
|
baroff = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '7':
|
|
force = true;
|
|
break;
|
|
default:
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (bdf.empty()) {
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
std::cout << "About to erase flash"
|
|
<< " [0x" << std::hex << offset << ",0x" << offset+len
|
|
<< "] on card " << bdf << std::endl;
|
|
|
|
if (offset + len > GOLDEN_BASE)
|
|
std::cout << "\nThis might erase golden image if there is !!\n" << std::endl;
|
|
|
|
if (!force && !canProceed())
|
|
return -ECANCELED;
|
|
|
|
pcidev::pci_device dev(bdf, bar, baroff, flash_type);
|
|
XQSPIPS_Flasher qspips(&dev);
|
|
|
|
return qspips.xclErase(offset, len);
|
|
}
|
|
|
|
int qspips_flash(int argc, char *argv[])
|
|
{
|
|
std::string bdf;
|
|
const char *fname = argv[0];
|
|
int bar = 0;
|
|
size_t baroff = INVALID_OFFSET;
|
|
size_t offset = 0;
|
|
std::string flash_type;
|
|
std::string bin_file;
|
|
bool force = false;
|
|
|
|
sudoOrDie();
|
|
|
|
while (true) {
|
|
const auto opt = getopt_long(argc, argv, "", qspips_flash_opts, nullptr);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case '0':
|
|
break;
|
|
case '1':
|
|
bdf = std::string(optarg);
|
|
break;
|
|
case '2':
|
|
bin_file = std::string(optarg);
|
|
break;
|
|
case '3':
|
|
offset = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '4':
|
|
flash_type = std::string(optarg);
|
|
break;
|
|
case '5':
|
|
bar = std::stoi(optarg);
|
|
break;
|
|
case '6':
|
|
baroff = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '7':
|
|
force = true;
|
|
break;
|
|
default:
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (bdf.empty() || bin_file.empty()) {
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
firmwareImage bin(bin_file.c_str());
|
|
if (bin.fail())
|
|
return -EINVAL;
|
|
std::cout
|
|
<< "About to program flash on card " << bdf << " at offset 0x" << std::hex << offset << std::dec << std::endl;
|
|
|
|
if (!force && !canProceed())
|
|
return -ECANCELED;
|
|
|
|
pcidev::pci_device dev(bdf, bar, baroff, flash_type);
|
|
XQSPIPS_Flasher qspips(&dev);
|
|
|
|
return qspips.xclUpgradeFirmware(bin, offset);
|
|
}
|
|
|
|
int qspips_readback(int argc, char *argv[])
|
|
{
|
|
std::string bdf;
|
|
const char *fname = argv[0];
|
|
int bar = 0;
|
|
size_t baroff = INVALID_OFFSET;
|
|
size_t offset = 0, len = FLASH_SIZE;
|
|
std::string flash_type;
|
|
std::string output;
|
|
|
|
sudoOrDie();
|
|
|
|
while (true) {
|
|
const auto opt = getopt_long(argc, argv, "", qspips_readback_opts, nullptr);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case '0':
|
|
break;
|
|
case '1':
|
|
bdf = std::string(optarg);
|
|
break;
|
|
case '2':
|
|
output = std::string(optarg);
|
|
break;
|
|
case '3':
|
|
offset = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '4':
|
|
len = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
case '5':
|
|
flash_type = std::string(optarg);
|
|
break;
|
|
case '6':
|
|
bar = std::stoi(optarg);
|
|
break;
|
|
case '7':
|
|
baroff = std::stoi(optarg, nullptr, 0);
|
|
break;
|
|
default:
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
if (bdf.empty() || output.empty()) {
|
|
printHelp(fname);
|
|
return -EINVAL;
|
|
}
|
|
|
|
std::cout << "Read out flash"
|
|
<< " [0x" << std::hex << offset << ",0x" << offset+len
|
|
<< "] on card " << bdf << " to " << output << std::dec << std::endl;
|
|
|
|
pcidev::pci_device dev(bdf, bar, baroff, flash_type);
|
|
XQSPIPS_Flasher qspips(&dev);
|
|
|
|
return qspips.xclReadBack(output, offset, len);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
try {
|
|
if (is_op(reset_opts, argc, argv))
|
|
return reset(argc, argv);
|
|
|
|
if (is_op(flash_opts, argc, argv))
|
|
return flash(argc, argv);
|
|
|
|
if (is_op(qspips_erase_opts, argc, argv))
|
|
return qspips_erase(argc, argv);
|
|
|
|
if (is_op(qspips_flash_opts, argc, argv))
|
|
return qspips_flash(argc, argv);
|
|
|
|
if (is_op(qspips_readback_opts, argc, argv))
|
|
return qspips_readback(argc, argv);
|
|
|
|
} catch (const std::exception& ex) {
|
|
std::cout << "Failed to flash: " << ex.what() << std::endl;
|
|
return -EINVAL;
|
|
}
|
|
|
|
printHelp(argv[0]);
|
|
return -EINVAL;
|
|
}
|