13
13
#include < type_traits>
14
14
15
15
#define KOCHERGA_VERSION_MAJOR 0 // NOLINT
16
- #define KOCHERGA_VERSION_MINOR 1 // NOLINT
16
+ #define KOCHERGA_VERSION_MINOR 2 // NOLINT
17
17
18
18
namespace kocherga
19
19
{
@@ -226,13 +226,6 @@ class IROMBackend
226
226
227
227
// --------------------------------------------------------------------------------------------------------------------
228
228
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
-
236
229
// / This is used to verify integrity of the application and other data.
237
230
// / Note that the firmware CRC verification is a computationally expensive process that needs to be completed
238
231
// / in a limited time interval, which should be minimized. This class has been carefully manually optimized to
@@ -278,7 +271,7 @@ class CRC64
278
271
for (auto it = std::rbegin (out); it != rend; ++it)
279
272
{
280
273
*it = static_cast <std::uint8_t >(x);
281
- x >>= BitsPerByte ;
274
+ x >>= 8U ;
282
275
}
283
276
return out;
284
277
}
@@ -297,6 +290,15 @@ class CRC64
297
290
std::uint64_t crc_ = Xor;
298
291
};
299
292
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
+
300
302
// / Detects the application in the ROM, verifies its integrity, and retrieves the information about it.
301
303
class AppLocator final
302
304
{
@@ -306,17 +308,18 @@ class AppLocator final
306
308
{}
307
309
308
310
// / 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>
310
313
{
311
314
for (std::size_t offset = 0 ; offset < max_app_size_; offset += AppDescriptor::MagicSize)
312
315
{
313
316
AppDescriptor desc{};
314
317
if (sizeof (desc) == backend_.read (offset, reinterpret_cast <std::byte*>(&desc), sizeof (desc)))
315
318
{
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 ))
320
323
{
321
324
return desc.getAppInfo ();
322
325
}
@@ -345,6 +348,14 @@ class AppLocator final
345
348
((app_info.image_size % MagicSize) == 0 );
346
349
}
347
350
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
+
348
359
[[nodiscard]] auto getAppInfo () const -> const AppInfo& { return app_info; }
349
360
350
361
private:
@@ -542,6 +553,8 @@ class Presenter final : public IReactor
542
553
return false ;
543
554
}
544
555
556
+ [[nodiscard]] auto getNumberOfNodes () const -> std::uint8_t { return num_nodes_; }
557
+
545
558
[[nodiscard]] auto trigger (const INode* const node,
546
559
const NodeID file_server_node_id,
547
560
const std::size_t app_image_file_path_length,
@@ -988,23 +1001,33 @@ class Bootloader : public detail::IController
988
1001
// / sit idle until instructed otherwise, or if the application itself commands the bootloader to begin the update.
989
1002
// / The flag affects only the initial verification and has no effect on all subsequent checks; for example,
990
1003
// / 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.
991
1009
Bootloader (IROMBackend& rom_backend,
992
1010
const SystemInfo& system_info,
993
1011
const std::size_t max_app_size,
994
1012
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 ) :
996
1015
max_app_size_ (max_app_size),
997
1016
boot_delay_ (boot_delay),
998
1017
backend_ (rom_backend),
999
1018
presentation_ (system_info, *this ),
1000
- linger_ (linger)
1019
+ linger_ (linger),
1020
+ allow_legacy_app_descriptors_ (allow_legacy_app_descriptors)
1001
1021
{}
1002
1022
1003
1023
// / Nodes shall be registered using this method after the instance is constructed.
1004
1024
// / The return value is true on success, false if there are too many nodes already or this node is already
1005
1025
// / registered (no effect in this case).
1006
1026
[[nodiscard]] auto addNode (INode* const node) -> bool { return presentation_.addNode (node); }
1007
1027
1028
+ // / The number of nodes added with addNode(). Zero by default (obviously).
1029
+ [[nodiscard]] auto getNumberOfNodes () const -> std::uint8_t { return presentation_.getNumberOfNodes (); }
1030
+
1008
1031
// / Non-blocking periodic state update.
1009
1032
// / The outer logic should invoke this method after any hardware event (for example, if WFE/WFI is used on an
1010
1033
// / 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
1089
1112
{
1090
1113
backend_.endWrite ();
1091
1114
}
1092
- app_info_ = detail::AppLocator (backend_, max_app_size_).identifyApplication ();
1115
+ app_info_ = detail::AppLocator (backend_, max_app_size_).identifyApplication (allow_legacy_app_descriptors_ );
1093
1116
final_.reset ();
1094
1117
if (app_info_)
1095
1118
{
@@ -1121,11 +1144,11 @@ class Bootloader : public detail::IController
1121
1144
if (State::AppUpdateInProgress == state_)
1122
1145
{
1123
1146
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" };
1125
1148
}
1126
1149
else
1127
1150
{
1128
- pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " Update started" };
1151
+ pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " Software update started" };
1129
1152
}
1130
1153
state_ = State::AppUpdateInProgress;
1131
1154
rom_offset_ = 0 ;
@@ -1139,7 +1162,8 @@ class Bootloader : public detail::IController
1139
1162
{
1140
1163
if (!response)
1141
1164
{
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" };
1143
1167
reset (false );
1144
1168
}
1145
1169
else
@@ -1153,11 +1177,7 @@ class Bootloader : public detail::IController
1153
1177
}
1154
1178
else
1155
1179
{
1156
- if (ok)
1157
- {
1158
- pending_log_ = {detail::dsdl::Diagnostic::Severity::Notice, " File transfer completed" };
1159
- }
1160
- else
1180
+ if (!ok)
1161
1181
{
1162
1182
pending_log_ = {detail::dsdl::Diagnostic::Severity::Critical, " ROM write failure" };
1163
1183
}
@@ -1172,6 +1192,7 @@ class Bootloader : public detail::IController
1172
1192
IROMBackend& backend_;
1173
1193
detail::Presenter presentation_;
1174
1194
const bool linger_;
1195
+ const bool allow_legacy_app_descriptors_;
1175
1196
1176
1197
std::chrono::microseconds uptime_{};
1177
1198
bool inited_ = false ;
@@ -1226,15 +1247,15 @@ class VolatileStorage
1226
1247
{
1227
1248
public:
1228
1249
// / 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
1230
1251
1231
1252
explicit VolatileStorage (std::uint8_t * const location) : ptr_(location) {}
1232
1253
1233
1254
// / Checks if the data is available and reads it, then erases the storage to prevent deja-vu.
1234
1255
// / Returns an empty option if no data is available (in that case the storage is not erased).
1235
1256
[[nodiscard]] auto take () -> std::optional<Container>
1236
1257
{
1237
- detail:: CRC64 crc;
1258
+ CRC64 crc;
1238
1259
crc.update (ptr_, StorageSize);
1239
1260
if (crc.isResidueCorrect ())
1240
1261
{
@@ -1250,15 +1271,13 @@ class VolatileStorage
1250
1271
void store (const Container& data)
1251
1272
{
1252
1273
(void ) std::memmove (ptr_, &data, sizeof (Container));
1253
- detail:: CRC64 crc;
1274
+ CRC64 crc;
1254
1275
crc.update (ptr_, sizeof (Container));
1255
1276
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 );
1257
1278
}
1258
1279
1259
1280
protected:
1260
- static_assert (std::is_trivial_v<Container>, " Container shall be a trivial type." );
1261
-
1262
1281
static constexpr std::uint8_t EraseFillValue = 0xCA ;
1263
1282
1264
1283
std::uint8_t * const ptr_;
0 commit comments