1313#include < type_traits>
1414
1515#define KOCHERGA_VERSION_MAJOR 0 // NOLINT
16- #define KOCHERGA_VERSION_MINOR 1 // NOLINT
16+ #define KOCHERGA_VERSION_MINOR 2 // NOLINT
1717
1818namespace kocherga
1919{
@@ -226,13 +226,6 @@ class IROMBackend
226226
227227// --------------------------------------------------------------------------------------------------------------------
228228
229- // / Internal use only.
230- namespace detail
231- {
232- static constexpr auto BitsPerByte = 8U ;
233-
234- static constexpr std::chrono::microseconds DefaultTransferIDTimeout{2'000'000 }; // /< Default taken from Specification.
235-
236229// / This is used to verify integrity of the application and other data.
237230// / Note that the firmware CRC verification is a computationally expensive process that needs to be completed
238231// / in a limited time interval, which should be minimized. This class has been carefully manually optimized to
@@ -278,7 +271,7 @@ class CRC64
278271 for (auto it = std::rbegin (out); it != rend; ++it)
279272 {
280273 *it = static_cast <std::uint8_t >(x);
281- x >>= BitsPerByte ;
274+ x >>= 8U ;
282275 }
283276 return out;
284277 }
@@ -297,6 +290,15 @@ class CRC64
297290 std::uint64_t crc_ = Xor;
298291};
299292
293+ // --------------------------------------------------------------------------------------------------------------------
294+
295+ // / Internal use only.
296+ namespace detail
297+ {
298+ static constexpr auto BitsPerByte = 8U ;
299+
300+ static constexpr std::chrono::microseconds DefaultTransferIDTimeout{2'000'000 }; // /< Default taken from Specification.
301+
300302// / Detects the application in the ROM, verifies its integrity, and retrieves the information about it.
301303class AppLocator final
302304{
@@ -306,17 +308,18 @@ class AppLocator final
306308 {}
307309
308310 // / Returns the AppInfo if the app is found and its integrity is intact. Otherwise, returns an empty option.
309- [[nodiscard]] auto identifyApplication () const -> std::optional<AppInfo>
311+ // / If the allow_legacy parameter is set, legacy app descriptors will be accepted, too.
312+ [[nodiscard]] auto identifyApplication (const bool allow_legacy = false ) const -> std::optional<AppInfo>
310313 {
311314 for (std::size_t offset = 0 ; offset < max_app_size_; offset += AppDescriptor::MagicSize)
312315 {
313316 AppDescriptor desc{};
314317 if (sizeof (desc) == backend_.read (offset, reinterpret_cast <std::byte*>(&desc), sizeof (desc)))
315318 {
316- if ( desc.isValid (max_app_size_) &&
317- validateImageCRC (offset + AppDescriptor::CRCOffset,
318- static_cast <std::size_t >(desc.getAppInfo ().image_size ),
319- desc.getAppInfo ().image_crc ))
319+ const bool match = desc.isValid (max_app_size_) || (allow_legacy && desc. isValidLegacy (max_app_size_));
320+ if (match && validateImageCRC (offset + AppDescriptor::CRCOffset,
321+ static_cast <std::size_t >(desc.getAppInfo ().image_size ),
322+ desc.getAppInfo ().image_crc ))
320323 {
321324 return desc.getAppInfo ();
322325 }
@@ -345,6 +348,14 @@ class AppLocator final
345348 ((app_info.image_size % MagicSize) == 0 );
346349 }
347350
351+ [[nodiscard]] auto isValidLegacy (const std::size_t max_app_size) const -> bool
352+ {
353+ static constexpr auto SizeAlignmentRequirement = 4U ; // /< Relaxed requirement to enhance compatibility.
354+ return std::equal (signature.begin (), signature.end (), ReferenceSignature.begin ()) &&
355+ (app_info.image_size > 0 ) && (app_info.image_size <= max_app_size) &&
356+ ((app_info.image_size % SizeAlignmentRequirement) == 0 );
357+ }
358+
348359 [[nodiscard]] auto getAppInfo () const -> const AppInfo& { return app_info; }
349360
350361 private:
@@ -542,6 +553,8 @@ class Presenter final : public IReactor
542553 return false ;
543554 }
544555
556+ [[nodiscard]] auto getNumberOfNodes () const -> std::uint8_t { return num_nodes_; }
557+
545558 [[nodiscard]] auto trigger (const INode* const node,
546559 const NodeID file_server_node_id,
547560 const std::size_t app_image_file_path_length,
@@ -988,23 +1001,33 @@ class Bootloader : public detail::IController
9881001 // / sit idle until instructed otherwise, or if the application itself commands the bootloader to begin the update.
9891002 // / The flag affects only the initial verification and has no effect on all subsequent checks; for example,
9901003 // / after the application is updated and validated, it will be booted after BootDelay regardless of this flag.
1004+ // /
1005+ // / If the allow_legacy_app_descriptors option is set, the bootloader will also accept legacy descriptors alongside
1006+ // / the new format. This option should be set only if the bootloader is introduced to a product that was using
1007+ // / the old app descriptor format in the past; refer to the PX4 Brickproof Bootloader for details. If you are not
1008+ // / sure, leave the default value.
9911009 Bootloader (IROMBackend& rom_backend,
9921010 const SystemInfo& system_info,
9931011 const std::size_t max_app_size,
9941012 const bool linger,
995- const std::chrono::seconds boot_delay = std::chrono::seconds(0 )) :
1013+ const std::chrono::seconds boot_delay = std::chrono::seconds(0 ),
1014+ const bool allow_legacy_app_descriptors = false ) :
9961015 max_app_size_ (max_app_size),
9971016 boot_delay_ (boot_delay),
9981017 backend_ (rom_backend),
9991018 presentation_ (system_info, *this ),
1000- linger_ (linger)
1019+ linger_ (linger),
1020+ allow_legacy_app_descriptors_ (allow_legacy_app_descriptors)
10011021 {}
10021022
10031023 // / Nodes shall be registered using this method after the instance is constructed.
10041024 // / The return value is true on success, false if there are too many nodes already or this node is already
10051025 // / registered (no effect in this case).
10061026 [[nodiscard]] auto addNode (INode* const node) -> bool { return presentation_.addNode (node); }
10071027
1028+ // / The number of nodes added with addNode(). Zero by default (obviously).
1029+ [[nodiscard]] auto getNumberOfNodes () const -> std::uint8_t { return presentation_.getNumberOfNodes (); }
1030+
10081031 // / Non-blocking periodic state update.
10091032 // / The outer logic should invoke this method after any hardware event (for example, if WFE/WFI is used on an
10101033 // / ARM platform), and periodically at least once per second. Typically, it would be invoked from the main loop.
@@ -1089,7 +1112,7 @@ class Bootloader : public detail::IController
10891112 {
10901113 backend_.endWrite ();
10911114 }
1092- app_info_ = detail::AppLocator (backend_, max_app_size_).identifyApplication ();
1115+ app_info_ = detail::AppLocator (backend_, max_app_size_).identifyApplication (allow_legacy_app_descriptors_ );
10931116 final_.reset ();
10941117 if (app_info_)
10951118 {
@@ -1121,11 +1144,11 @@ class Bootloader : public detail::IController
11211144 if (State::AppUpdateInProgress == state_)
11221145 {
11231146 backend_.endWrite (); // Cycle the state to re-init ROM if needed.
1124- pending_log_ = {detail::dsdl::Diagnostic::Severity::Warning, " Ongoing update process restarted" };
1147+ pending_log_ = {detail::dsdl::Diagnostic::Severity::Warning, " Ongoing software update restarted" };
11251148 }
11261149 else
11271150 {
1128- pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " Update started" };
1151+ pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " Software update started" };
11291152 }
11301153 state_ = State::AppUpdateInProgress;
11311154 rom_offset_ = 0 ;
@@ -1139,7 +1162,8 @@ class Bootloader : public detail::IController
11391162 {
11401163 if (!response)
11411164 {
1142- pending_log_ = {detail::dsdl::Diagnostic::Severity::Critical, " File request timeout or remote error" };
1165+ pending_log_ = {detail::dsdl::Diagnostic::Severity::Critical,
1166+ " Software image file request timeout or file server error" };
11431167 reset (false );
11441168 }
11451169 else
@@ -1153,11 +1177,7 @@ class Bootloader : public detail::IController
11531177 }
11541178 else
11551179 {
1156- if (ok)
1157- {
1158- pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " File transfer completed" };
1159- }
1160- else
1180+ if (!ok)
11611181 {
11621182 pending_log_ = {detail::dsdl::Diagnostic::Severity::Critical, " ROM write failure" };
11631183 }
@@ -1172,6 +1192,7 @@ class Bootloader : public detail::IController
11721192 IROMBackend& backend_;
11731193 detail::Presenter presentation_;
11741194 const bool linger_;
1195+ const bool allow_legacy_app_descriptors_;
11751196
11761197 std::chrono::microseconds uptime_{};
11771198 bool inited_ = false ;
@@ -1226,15 +1247,15 @@ class VolatileStorage
12261247{
12271248public:
12281249 // / The amount of memory required to store the data. This is the size of the container plus 8 bytes for the CRC.
1229- static constexpr auto StorageSize = sizeof (Container) + detail:: CRC64::Size;
1250+ static constexpr auto StorageSize = sizeof (Container) + CRC64::Size; // NOLINT
12301251
12311252 explicit VolatileStorage (std::uint8_t * const location) : ptr_(location) {}
12321253
12331254 // / Checks if the data is available and reads it, then erases the storage to prevent deja-vu.
12341255 // / Returns an empty option if no data is available (in that case the storage is not erased).
12351256 [[nodiscard]] auto take () -> std::optional<Container>
12361257 {
1237- detail:: CRC64 crc;
1258+ CRC64 crc;
12381259 crc.update (ptr_, StorageSize);
12391260 if (crc.isResidueCorrect ())
12401261 {
@@ -1250,15 +1271,13 @@ class VolatileStorage
12501271 void store (const Container& data)
12511272 {
12521273 (void ) std::memmove (ptr_, &data, sizeof (Container));
1253- detail:: CRC64 crc;
1274+ CRC64 crc;
12541275 crc.update (ptr_, sizeof (Container));
12551276 const auto crc_ptr = ptr_ + sizeof (Container); // NOLINT NOSONAR pointer arithmetic
1256- (void ) std::memmove (crc_ptr, crc.getBytes ().data (), detail:: CRC64::Size);
1277+ (void ) std::memmove (crc_ptr, crc.getBytes ().data (), CRC64::Size);
12571278 }
12581279
12591280protected:
1260- static_assert (std::is_trivial_v<Container>, " Container shall be a trivial type." );
1261-
12621281 static constexpr std::uint8_t EraseFillValue = 0xCA ;
12631282
12641283 std::uint8_t * const ptr_;
0 commit comments