Add bootloader extension

This commit is contained in:
Thomas Stoddard
2026-10-21 19:22:50 -04:00
committed by Kyle Schwarz
parent 4db27aa187
commit 6ac041cadc
61 changed files with 13816 additions and 68 deletions
@@ -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_
@@ -1,17 +0,0 @@
#ifndef __ICSNEO_DEVICE_EXTENSIONS_BUILTIN_H_
#define __ICSNEO_DEVICE_EXTENSIONS_BUILTIN_H_
@LIBICSNEO_EXT_CODE_INCS@
#include "icsneo/device/device.h"
#include <memory>
namespace icsneo {
static void AddBuiltInExtensionsTo(const std::shared_ptr<icsneo::Device>& device) {
(void)device; // [[maybe_unused]]
@LIBICSNEO_EXT_CODE@
}
}
#endif