mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-11-04 12:59:07 +01:00
Add bootloader extension
This commit is contained in:
committed by
Kyle Schwarz
parent
4db27aa187
commit
6ac041cadc
@@ -0,0 +1,42 @@
|
||||
#ifndef __BOOTLOADER_API_H_
|
||||
#define __BOOTLOADER_API_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/chip.h"
|
||||
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
|
||||
#include "icsneo/device/extensions/bootloader/parser/ief.h"
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
using Logger = icsneo::Device::OpenStatusHandler;
|
||||
|
||||
bool requiresUpdate(icsneo::Device& device, const FWRepo& fw);
|
||||
bool update(
|
||||
icsneo::Device& device,
|
||||
const FWRepo& fw,
|
||||
Logger logger = [](icsneo::Device::OpenStatusType, const std::string&, std::optional<double>) { return icsneo::Device::OpenDirective::Continue; },
|
||||
bool alreadyInBootloader = false,
|
||||
bool forceFlash = false
|
||||
);
|
||||
|
||||
bool enterBootloader(icsneo::Device& device);
|
||||
bool exitBootloader(icsneo::Device& device, Logger logger, bool updatedChips);
|
||||
bool verifyConnection(icsneo::Device& device, Logger logger);
|
||||
|
||||
bool isSFPChip(const icsneo::Bootloader::Chip& chip);
|
||||
bool isSFPIEF(const icsneo::Bootloader::IEF& ief);
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_API_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef __BOOTLOADER_BYTESVIEW_H_
|
||||
#define __BOOTLOADER_BYTESVIEW_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class BytesView {
|
||||
private:
|
||||
const uint8_t* ptr = nullptr;
|
||||
size_t length = 0;
|
||||
public:
|
||||
BytesView() = default;
|
||||
BytesView(const uint8_t* buf, size_t size) : ptr(buf), length(size) {}
|
||||
|
||||
const uint8_t* begin() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const uint8_t* end() const {
|
||||
return ptr + length;
|
||||
}
|
||||
|
||||
const uint8_t* data() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return length;
|
||||
}
|
||||
|
||||
const uint8_t& operator[](size_t index) const {
|
||||
return ptr[index];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_BYTESVIEW_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef __BOOTLOADER_CHIP_H_
|
||||
#define __BOOTLOADER_CHIP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/chipinfo.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
struct Chip {
|
||||
uint8_t expansionSlot = 0;
|
||||
ChipInfo info;
|
||||
|
||||
Chip(const ChipInfo& info) : info(info) {}
|
||||
|
||||
ChipID getID() const { return info.id; }
|
||||
std::string getName() const { return info.name ? info.name : ""; }
|
||||
std::string getIEFName() const { return info.iefName ? info.iefName : ""; }
|
||||
const std::vector<const char*>& getMultiIEFName() const { return info.iefSegments; }
|
||||
FirmwareType getFirmwareType() const { return info.fwType; }
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_CHIP_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef __BOOTLOADER_APPLICATION_H_
|
||||
#define __BOOTLOADER_APPLICATION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/communication.h"
|
||||
#include "icsneo/device/extensions/bootloader/parser/ief.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* The software update protocol used with the application
|
||||
* on RED devices, does not require entering the bootloader.
|
||||
*/
|
||||
class Application : public Communication {
|
||||
public:
|
||||
Application(icsneo::Device& dev, const ChipInfo& chip, bool doAuthChecks = false, bool encrypt = false)
|
||||
: Communication(dev), targetChip(chip), doAuthChecks(doAuthChecks), encrypted(encrypt) {}
|
||||
|
||||
void setExpansionSlot(uint8_t slot) { targetChip.expansionSlot = slot; }
|
||||
|
||||
bool enterApplication() override;
|
||||
bool hasFinalizationStep() const override {
|
||||
return true;
|
||||
}
|
||||
bool finalize() override;
|
||||
bool flash(const FWRepo& repo, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
|
||||
bool open() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool close() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool authenticate();
|
||||
bool sendSoftware(const std::vector<uint8_t>& bin, const icsneo::Device::OpenStatusHandler& handler);
|
||||
bool sendSoftware(const std::vector<IEF::Sector>& sectors, uint32_t blockSize, const icsneo::Device::OpenStatusHandler& handler);
|
||||
bool validateFirmware(const DeviceAppVersion& chipSrcVer);
|
||||
bool validateComponent();
|
||||
|
||||
private:
|
||||
// Sub-commands of ExtendedCommand::SoftwareUpdate
|
||||
enum class Update : uint8_t {
|
||||
Write = 0,
|
||||
Erase = 1,
|
||||
GetProgress = 2,
|
||||
CheckVersion = 5,
|
||||
ValidateComponent = 6,
|
||||
Finalize = 7,
|
||||
CheckComponentVersion = 9
|
||||
};
|
||||
|
||||
// Prefixes the arguments of every ExtendedCommand::SoftwareUpdate request, and is echoed back with progress
|
||||
#pragma pack(push, 2)
|
||||
struct SoftwareUpdateCommandHeader {
|
||||
uint32_t componentIdentifier; // unique id for the software component - analogous to what used to be "chip id"
|
||||
Update commandType;
|
||||
uint8_t expansionSlot;
|
||||
uint32_t offset;
|
||||
uint32_t commandSizeOrProgress; // size
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
Chip targetChip;
|
||||
const bool doAuthChecks;
|
||||
const bool encrypted;
|
||||
|
||||
using StatusHandler = std::function<void(double progress)>;
|
||||
bool sendCommand(Update command, const StatusHandler& handler, uint32_t offset = 0,
|
||||
const std::vector<uint8_t>& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)) {
|
||||
return sendCommand(command, offset, static_cast<uint32_t>(args.size()), handler, timeout, args);
|
||||
}
|
||||
bool sendCommand(Update command, uint32_t offset, uint32_t length, const StatusHandler& handler,
|
||||
std::chrono::milliseconds timeout = std::chrono::milliseconds(3000), std::vector<uint8_t> args = {});
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_APPLICATION_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef __BOOTLOADER_BOOTLOADERVERSION_H_
|
||||
#define __BOOTLOADER_BOOTLOADERVERSION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct BootloaderVersionResponse {
|
||||
uint8_t blMajor;
|
||||
uint8_t blMinor;
|
||||
uint16_t reserved1;
|
||||
uint8_t hwMajor;
|
||||
uint8_t hwMinor;
|
||||
uint8_t hwPlatform;
|
||||
uint16_t reserved2;
|
||||
uint8_t manDay;
|
||||
uint8_t manMonth;
|
||||
uint16_t manYear;
|
||||
uint8_t serialNum[8];
|
||||
uint8_t hwId;
|
||||
uint8_t reserved3;
|
||||
uint8_t zynqId;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_BOOTLOADERVERSION_H_
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef __BOOTLOADER_COMMUNICATION_H_
|
||||
#define __BOOTLOADER_COMMUNICATION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/chip.h"
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class FWRepo;
|
||||
|
||||
/**
|
||||
* A bootloader protocol for one chip.
|
||||
*
|
||||
* Authentication is a challenge/response handled by libicsbl: icsbl_generate_seed_v1() derives
|
||||
* a challenge (seed.a) and the reply the device must send back (seed.b) from the serial number.
|
||||
* Firmware is encrypted for a specific device with icsbl_encrypt_v1(), also keyed by the serial number.
|
||||
*/
|
||||
class Communication {
|
||||
public:
|
||||
Communication(icsneo::Device& dev) : device(dev) {}
|
||||
virtual ~Communication() = default;
|
||||
|
||||
virtual bool enterApplication() = 0;
|
||||
virtual bool hasFinalizationStep() const = 0;
|
||||
virtual bool finalize() = 0;
|
||||
virtual bool flash(const FWRepo& repo, const icsneo::Device::OpenStatusHandler& handler) = 0;
|
||||
|
||||
virtual bool open() = 0;
|
||||
virtual bool close() = 0;
|
||||
|
||||
protected:
|
||||
static uint32_t readLE32(const uint8_t* data);
|
||||
static void writeLE32(uint8_t* data, uint32_t val);
|
||||
icsneo::Device& device;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_COMMUNICATION_H_
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef __BOOTLOADER_RADCOMMUNICATION_H_
|
||||
#define __BOOTLOADER_RADCOMMUNICATION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/communication.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* The bootloader protocol used by RAD devices.
|
||||
*
|
||||
* Requests are a command byte followed by the payload, responses are the command byte,
|
||||
* a Response::Type byte and then the payload. Newer RAD bootloaders (the "Zynq binary
|
||||
* flasher") also prefix each request with ZynqFlasherSync and a 16-bit little-endian
|
||||
* length of the command byte plus payload.
|
||||
*/
|
||||
class RADCommunication : public Communication {
|
||||
protected:
|
||||
static constexpr size_t MaxMessageLength = 1518;
|
||||
static constexpr uint8_t ZynqFlasherSync[4] = {0xAA, 0xAA, 0x55, 0x55};
|
||||
static constexpr size_t ZynqFlasherHeaderSize = sizeof(ZynqFlasherSync) + sizeof(uint16_t);
|
||||
|
||||
struct Request {
|
||||
enum class Type : uint8_t {
|
||||
BootloaderVersion = 0x01,
|
||||
EnterApplication = 0x02,
|
||||
BeginTransfer = 0x03,
|
||||
FWPayload = 0x04,
|
||||
EndTransfer = 0x05,
|
||||
ProgramFlash = 0x06,
|
||||
DownloadedBinInfo = 0x07,
|
||||
ProgrammingStatus = 0x08,
|
||||
Authenticate = 0x09,
|
||||
SanityCheck = 0x0A,
|
||||
VerifyCRC = 0x0B,
|
||||
ValidateVersion = 0x0F,
|
||||
Invalid = 0xFF
|
||||
} type = Type::Invalid;
|
||||
|
||||
uint8_t* payload = nullptr;
|
||||
size_t expectedResponseLen = 0;
|
||||
};
|
||||
|
||||
Request makeRequest(Request::Type type, size_t payloadSize = 0, size_t expectedResponseLen = 0);
|
||||
|
||||
struct Response {
|
||||
enum class Type {
|
||||
Failure = 0x00,
|
||||
Success = 0x01
|
||||
} type = Type::Failure;
|
||||
|
||||
uint8_t* payload = nullptr;
|
||||
size_t size = 0;
|
||||
|
||||
bool done = false;
|
||||
|
||||
operator bool() const {
|
||||
return done && type == Type::Success;
|
||||
}
|
||||
};
|
||||
|
||||
Response& sendRequest(const Request& request, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
|
||||
bool sendRequestNoResponse();
|
||||
|
||||
icsneo::Device::OpenStatusHandler logger; // Progress updates, set in flash()
|
||||
Response lastResponse; // Last response received from sendRequest
|
||||
bool supportsZynqBinaryFlasher = false; // Dictates how messages are formatted, this gets set in initialize()
|
||||
|
||||
std::vector<uint8_t> rxBuffer;
|
||||
std::vector<uint8_t> txBuffer;
|
||||
Chip targetChip; // Chip to flash
|
||||
|
||||
bool loadZips(const FWRepo& fw, std::vector<uint8_t>& chipSoftware);
|
||||
|
||||
bool initialize();
|
||||
bool sanityCheck();
|
||||
bool getBootloaderVersion();
|
||||
bool validateChipVersion(const icsneo::DeviceAppVersion& srcVer);
|
||||
virtual bool authenticate();
|
||||
bool sendSoftware(const std::vector<uint8_t>& chipSoftware);
|
||||
bool getProgrammingStatus(size_t sizeToValidate);
|
||||
bool supportsFlashBuffer() const;
|
||||
public:
|
||||
RADCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash);
|
||||
|
||||
bool enterApplication() override;
|
||||
bool finalize() override;
|
||||
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
|
||||
bool open() override;
|
||||
bool close() override;
|
||||
bool hasFinalizationStep() const override;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_RADCOMMUNICATION_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
|
||||
#define __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/radcommunication.h"
|
||||
#include "icsneo/device/extensions/bootloader/communication/application.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class RADMultiChipCommunication : public RADCommunication {
|
||||
private:
|
||||
std::unique_ptr<Application> standardComm;
|
||||
const bool doAuthChecks;
|
||||
const uint8_t slot;
|
||||
bool authenticate() override;
|
||||
public:
|
||||
RADMultiChipCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash, bool auth, uint8_t slot = UINT8_MAX);
|
||||
|
||||
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
bool open() override;
|
||||
bool close() override;
|
||||
bool enterApplication() override;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
|
||||
#define __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/communication.h"
|
||||
#include "icsneo/device/extensions/bootloader/communication/bootloaderversion.h"
|
||||
#include "icsneo/device/extensions/bootloader/parser/ief.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* The bootloader protocol used by peripheral chips on the RAD-Galaxy 2, tunneled through
|
||||
* Command::PeripheralBootloader. Requests are the chip id, the Request and the arguments.
|
||||
* Replies are a byte, ReplySuccess, then for bootloader requests ReplyAccepted, then the payload.
|
||||
*/
|
||||
class RADPeriphCommunication : public Communication {
|
||||
protected:
|
||||
static constexpr size_t ReplySuccessOffset = 1;
|
||||
static constexpr size_t ReplyAcceptedOffset = 2;
|
||||
static constexpr uint8_t ReplySuccess = 0x01;
|
||||
static constexpr uint8_t ReplyAccepted = 0x77;
|
||||
static constexpr size_t ReplyHeaderSize = 3;
|
||||
// Requests that write to the bootloader have three leading zero bytes before their arguments
|
||||
static constexpr size_t BootloaderArgsOffset = 3;
|
||||
// Flash sectors are sent as two requests, the first with this many bytes and the second with the rest
|
||||
static constexpr size_t FlashSectorFirstChunkSize = 1024;
|
||||
|
||||
enum class Mode : uint8_t {
|
||||
NoAnswer = 0x00u,
|
||||
MainApplication = 0xAFu,
|
||||
Bootloader = 0xFEu
|
||||
} mode = Mode::NoAnswer;
|
||||
|
||||
Chip targetChip;
|
||||
uint32_t serialNumber = 0;
|
||||
public:
|
||||
enum class Request : uint8_t {
|
||||
SwitchToApp = 0x01u,
|
||||
SwitchToBootloader = 0x02u,
|
||||
Version = 0x12u,
|
||||
Authenticate = 0x13u,
|
||||
FlashSector = 0x14u,
|
||||
FirmwareValidate = 0x16u,
|
||||
Erase = 0x20u,
|
||||
Echo = 0xAFu,
|
||||
Mode = 0xADu,
|
||||
};
|
||||
|
||||
struct Response {
|
||||
bool success = false;
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
operator bool() const {
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
bool waitForConnect(std::chrono::milliseconds timeout = std::chrono::milliseconds(30000));
|
||||
|
||||
bool sendRequestNoResponse(Request req, std::vector<uint8_t>&& args = {});
|
||||
bool sendRequestNoResponse(Request req, ChipID id, std::vector<uint8_t>&& args = {});
|
||||
Response sendRequest(Request req, std::vector<uint8_t>&& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
|
||||
Response sendRequest(Request req, ChipID id, std::vector<uint8_t>&& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
|
||||
|
||||
Mode getMode();
|
||||
bool sendPeriphTest();
|
||||
bool readSerialNumber();
|
||||
bool authenticate();
|
||||
bool validateFirmware();
|
||||
bool flashSector(const IEF::Sector& sector);
|
||||
|
||||
RADPeriphCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash);
|
||||
~RADPeriphCommunication();
|
||||
|
||||
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
bool open() override;
|
||||
bool close() override;
|
||||
bool enterApplication() override;
|
||||
bool finalize() override;
|
||||
bool hasFinalizationStep() const override;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef __BOOTLOADER_RED_H_
|
||||
#define __BOOTLOADER_RED_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/redbase.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* The bootloader communication protocol used for non-FPGA
|
||||
* chips on neoVI and ValueCAN devices.
|
||||
*
|
||||
* Zynq doesn't count as an FPGA chip for the sake of
|
||||
* this, so it's handled here.
|
||||
*/
|
||||
class REDCommunication : public REDCommunicationBase {
|
||||
public:
|
||||
REDCommunication(icsneo::Device& dev, const ChipInfo& chip, bool auth, bool encryptWithDeviceKey = true);
|
||||
bool enterApplication() override { return sendCommand(uint8_t(Command::EnterApplication)); }
|
||||
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
bool open() override;
|
||||
|
||||
private:
|
||||
const bool doAuthChecks;
|
||||
const bool encryptFirmwareWithDeviceKey;
|
||||
size_t blockSize = 2048;
|
||||
|
||||
enum class Command : uint8_t {
|
||||
GetVersion = 0x12,
|
||||
Authenticate = 0x13,
|
||||
Flash = 0x14,
|
||||
ValidateFirmware = 0x16,
|
||||
Erase = 0x20,
|
||||
EnterApplication = 0x21,
|
||||
ValidateSWVersion = 0x27,
|
||||
};
|
||||
|
||||
// The reply to Command::GetVersion
|
||||
struct VersionResponse {
|
||||
uint8_t bl_major, bl_minor, app_major, app_minor, flags, prod_id;
|
||||
uint8_t hardware_rev_major, hardware_rev_minor;
|
||||
uint8_t man_day, man_month;
|
||||
uint16_t man_year;
|
||||
uint8_t serial_num[8];
|
||||
uint16_t block_size;
|
||||
uint8_t last_error_code;
|
||||
uint8_t pad_byte;
|
||||
|
||||
static constexpr size_t EncodedSize = 24;
|
||||
|
||||
VersionResponse() = default;
|
||||
VersionResponse(const REDPacketizer::RxFrame& r);
|
||||
} currentVersion;
|
||||
|
||||
// Called from open()
|
||||
bool authenticate(bool firstTime = true);
|
||||
bool getVersion();
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_RED_H_
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef __BOOTLOADER_REDBASE_H_
|
||||
#define __BOOTLOADER_REDBASE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/communication.h"
|
||||
#include "icsneo/device/extensions/bootloader/communication/redpacketizer.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* Implementation shared by REDCommunication and REDCoreCommunication
|
||||
*
|
||||
* Requests are framed as:
|
||||
* uint8_t REDPacketizer::Magic
|
||||
* uint16_t chip id (little-endian)
|
||||
* uint16_t length of the command byte, arguments and padding (little-endian)
|
||||
* uint8_t command
|
||||
* uint8_t arguments[]
|
||||
* uint8_t optional PaddingByte, see sendCommandRaw()
|
||||
*/
|
||||
class REDCommunicationBase : public Communication {
|
||||
public:
|
||||
REDCommunicationBase(icsneo::Device& dev, const ChipInfo& chip);
|
||||
~REDCommunicationBase() {
|
||||
// This must happen before the Packetizer is destroyed
|
||||
device.com->resumeReads();
|
||||
}
|
||||
|
||||
bool open() override;
|
||||
bool close() override;
|
||||
bool hasFinalizationStep() const override {
|
||||
return false;
|
||||
}
|
||||
bool finalize() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr uint8_t PaddingByte = 0x55;
|
||||
|
||||
Chip targetChip;
|
||||
|
||||
std::vector<REDPacketizer::RxFrame> lastData;
|
||||
REDPacketizer rp;
|
||||
|
||||
bool handleData();
|
||||
bool sendCommand(uint8_t cmd, std::chrono::seconds timeout, bool clearLastResponse = true) { return sendCommand(cmd, {}, timeout, clearLastResponse); }
|
||||
bool sendCommand(uint8_t cmd, std::vector<uint8_t> args = {}, std::chrono::seconds timeout = std::chrono::seconds(1), bool clearLastResponse = true);
|
||||
virtual bool sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args = {});
|
||||
bool waitForRx(std::chrono::milliseconds timeout);
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_REDBASE_H_
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef __BOOTLOADER_REDCORE_H_
|
||||
#define __BOOTLOADER_REDCORE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/communication/redbase.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* The bootloader communication protocol used for FPGA
|
||||
* chips on neoVI and ValueCAN devices.
|
||||
*
|
||||
* Zynq doesn't count as an FPGA chip for the sake of
|
||||
* this, it's just targetting the non-hybrid FPGAs.
|
||||
*
|
||||
* This was previously called FIRE 2 Core Flasher, but
|
||||
* it does more than just FIRE 2 nowadays.
|
||||
*/
|
||||
class REDCoreCommunication : public REDCommunicationBase {
|
||||
public:
|
||||
using REDCommunicationBase::REDCommunicationBase;
|
||||
|
||||
bool enterApplication() override { return false; }
|
||||
bool flash(const FWRepo& fw, const icsneo::Device::OpenStatusHandler& handler) override;
|
||||
private:
|
||||
/**
|
||||
* Core flasher commands are tunneled through a RED request using CoreFlasherCommand,
|
||||
* the arguments are CoreFlasherMagic, a 16-bit little-endian length, then the core command and its arguments
|
||||
*/
|
||||
static constexpr uint8_t CoreFlasherCommand = 0xCD;
|
||||
static constexpr uint8_t CoreFlasherMagic = 0xEF;
|
||||
|
||||
enum class Command : uint8_t {
|
||||
SendFirmware = 0,
|
||||
StartFirmwareFlash = 1,
|
||||
GetFlashProgress = 2,
|
||||
};
|
||||
|
||||
bool sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args = {}) override;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_REDCORE_H_
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef __BOOTLOADER_REDPACKETIZER_H_
|
||||
#define __BOOTLOADER_REDPACKETIZER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/core/ringbuffer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* Splits bytes from a RED bootloader into frames:
|
||||
* uint8_t Magic
|
||||
* uint8_t Response
|
||||
* uint16_t length (little-endian)
|
||||
* uint8_t data[length]
|
||||
*/
|
||||
class REDPacketizer {
|
||||
public:
|
||||
static constexpr uint8_t Magic = 0xAA;
|
||||
|
||||
enum class Response : uint8_t {
|
||||
Accepted = 0x77,
|
||||
Rejected = 0x7F,
|
||||
};
|
||||
|
||||
struct RxFrame {
|
||||
uint16_t len = 0;
|
||||
Response resp = Response::Rejected;
|
||||
std::vector<uint8_t> data;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call with bytes received from the device.
|
||||
* The returned boolean indicates if there is output waiting in output()
|
||||
*/
|
||||
bool input(icsneo::RingBuffer& bytes);
|
||||
std::vector<RxFrame> output();
|
||||
private:
|
||||
enum ReadState { HEADER, CMD, LENGTH_LOWER, LENGTH_UPPER, DATA };
|
||||
ReadState state = HEADER;
|
||||
std::vector<RxFrame> working;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_REDPACKETIZER_H_
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef __BOOTLOADER_FWBIN_H_
|
||||
#define __BOOTLOADER_FWBIN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class FWBin : public FWRepo {
|
||||
public:
|
||||
FWBin() = default;
|
||||
|
||||
bool load(const std::string& path);
|
||||
|
||||
const FirmwareSourceVersion& getVersion() const override { return version; }
|
||||
bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const override;
|
||||
std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const override;
|
||||
|
||||
static uint32_t HashDjb2(const std::string& str);
|
||||
|
||||
private:
|
||||
static constexpr const uint32_t SignatureHash = 0;
|
||||
|
||||
struct VersionTags {
|
||||
const char* majorTag = nullptr;
|
||||
const char* minorTag = nullptr;
|
||||
|
||||
operator bool() const {
|
||||
return majorTag && minorTag;
|
||||
}
|
||||
};
|
||||
|
||||
static VersionTags GetVersionTags(const std::string& iefName);
|
||||
|
||||
struct TagData {
|
||||
enum class Type : uint8_t {
|
||||
DataBytes = 0,
|
||||
DataLength = 1,
|
||||
IEFVersion = 2,
|
||||
Signature = 3,
|
||||
Unknown = 0xFF
|
||||
};
|
||||
|
||||
Type type = Type::Unknown;
|
||||
std::streampos arrayPosition = 0;
|
||||
uint32_t arrayLength = 0;
|
||||
|
||||
bool read(std::ifstream& stream, std::vector<uint8_t>& out) const;
|
||||
};
|
||||
|
||||
bool readTag(const std::string& tag, std::vector<uint8_t>& out) const;
|
||||
|
||||
mutable std::ifstream file;
|
||||
FirmwareSourceVersion version;
|
||||
std::map<uint32_t, TagData> tags;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_FWBIN_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef __BOOTLOADER_FWBUNDLE_H_
|
||||
#define __BOOTLOADER_FWBUNDLE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class FWBundle : public FWRepo {
|
||||
public:
|
||||
FWBundle() = default;
|
||||
|
||||
bool load(const std::string& path);
|
||||
static bool IsFWBundle(const std::string& path);
|
||||
|
||||
const FirmwareSourceVersion& getVersion() const override { return version; }
|
||||
bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const override;
|
||||
std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const override;
|
||||
|
||||
private:
|
||||
struct ChipBinary {
|
||||
std::vector<uint8_t> data;
|
||||
FirmwareSourceVersion version;
|
||||
};
|
||||
|
||||
FirmwareSourceVersion version;
|
||||
std::unordered_map<uint32_t, ChipBinary> binaries;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_FWBUNDLE_H_
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef __BOOTLOADER_FWLOADER_H_
|
||||
#define __BOOTLOADER_FWLOADER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class FWLoader {
|
||||
public:
|
||||
// Detects whether the file is a firmware bundle or a fw.bin and loads it, returns nullptr on failure
|
||||
static std::unique_ptr<FWRepo> Load(const std::string& path);
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_FWLOADER_H_
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef __BOOTLOADER_FWREPO_H_
|
||||
#define __BOOTLOADER_FWREPO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/version.h"
|
||||
#include "icsneo/device/deviceversion.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class FWRepo {
|
||||
public:
|
||||
FWRepo() = default;
|
||||
virtual ~FWRepo() = default;
|
||||
|
||||
virtual bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const = 0;
|
||||
virtual std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const = 0;
|
||||
virtual const FirmwareSourceVersion& getVersion() const = 0;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_FWREPO_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef __BOOTLOADER_FWZIP_H_
|
||||
#define __BOOTLOADER_FWZIP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class ZipHandle;
|
||||
|
||||
class FWZip {
|
||||
private:
|
||||
std::unique_ptr<ZipHandle> handle;
|
||||
std::vector<uint8_t> zipData;
|
||||
std::optional<size_t> uncompressedSize = std::nullopt;
|
||||
public:
|
||||
FWZip(std::vector<uint8_t>&& tagData);
|
||||
~FWZip();
|
||||
|
||||
std::optional<size_t> size() const { return uncompressedSize; }
|
||||
bool readAll(uint8_t* out);
|
||||
bool readAll(std::vector<uint8_t>& out);
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_FWZIP_H_
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef __BOOTLOADER_IEF_H_
|
||||
#define __BOOTLOADER_IEF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/device/extensions/bootloader/bytesview.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class IEF {
|
||||
public:
|
||||
struct Version {
|
||||
uint8_t appMajor;
|
||||
uint8_t appMinor;
|
||||
uint8_t prodId;
|
||||
uint8_t chipId;
|
||||
};
|
||||
using Sector = std::pair<uint32_t, BytesView>;
|
||||
|
||||
IEF() = default;
|
||||
|
||||
bool load(std::vector<uint8_t>&& bytes);
|
||||
|
||||
const Version& getVersion() const { return version; }
|
||||
const std::vector<Sector>& getSectors() const { return sectors; }
|
||||
// IEFs are comprised of equally sized blocks of data - take the size of the first
|
||||
uint32_t getBlockSize() const { return sectors.empty() ? 0 : static_cast<uint32_t>(sectors.front().second.size()); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data;
|
||||
Version version = {};
|
||||
std::vector<Sector> sectors;
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_IEF_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef __BOOTLOADER_LOGGING_H_
|
||||
#define __BOOTLOADER_LOGGING_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
namespace Log {
|
||||
|
||||
void Trace(const std::string& subject, const std::string& msg);
|
||||
void Debug(const std::string& subject, const std::string& msg);
|
||||
void Info(const std::string& subject, const std::string& msg);
|
||||
void Warning(const std::string& subject, const std::string& msg);
|
||||
void Error(const std::string& subject, const std::string& msg);
|
||||
|
||||
} // namespace Log
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_LOGGING_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef __BOOTLOADER_VERSION_H_
|
||||
#define __BOOTLOADER_VERSION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
/**
|
||||
* Tracks the Vehicle Spy 3 version that a firmware
|
||||
* package is intended for.
|
||||
*/
|
||||
struct FirmwareSourceVersion {
|
||||
using ValueType = uint8_t;
|
||||
ValueType major = 0;
|
||||
ValueType minor = 0;
|
||||
ValueType release = 0;
|
||||
ValueType build = 0;
|
||||
bool load(const std::string& toParse);
|
||||
};
|
||||
|
||||
} // namespace Bootloader
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __BOOTLOADER_VERSION_H_
|
||||
Reference in New Issue
Block a user