uf2.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #include "uf2.h"
  2. #include <furi.h>
  3. #define UF2_BLOCK_SIZE (512UL)
  4. #define UF2_DATA_SIZE (476UL)
  5. #define UF2_CHECKSUM_SIZE (16UL)
  6. #define UF2_MAGIC_START_0 (0x0A324655UL)
  7. #define UF2_MAGIC_START_1 (0x9E5D5157UL)
  8. #define UF2_MAGIC_END (0x0AB16F30UL)
  9. #define TAG "VgmUf2"
  10. typedef enum {
  11. Uf2FlagNotMainFlash = 1UL << 0,
  12. Uf2FlagFileContainer = 1UL << 12,
  13. Uf2FlagFamilyIdPresent = 1UL << 13,
  14. Uf2FlagChecksumPresent = 1UL << 14,
  15. Uf2FlagExtensionPresent = 1UL << 15,
  16. } Uf2Flag;
  17. typedef struct {
  18. uint32_t magic_start[2];
  19. uint32_t flags;
  20. uint32_t target_addr;
  21. uint32_t payload_size;
  22. uint32_t block_no;
  23. uint32_t num_blocks;
  24. union {
  25. uint32_t file_size;
  26. uint32_t family_id;
  27. };
  28. } Uf2BlockHeader;
  29. typedef union {
  30. uint8_t payload[UF2_DATA_SIZE];
  31. struct {
  32. uint8_t reserved[UF2_DATA_SIZE - 24];
  33. uint32_t start_addr;
  34. uint32_t region_len;
  35. uint8_t checksum[UF2_CHECKSUM_SIZE];
  36. };
  37. } Uf2BlockData;
  38. typedef struct {
  39. uint32_t magic_end;
  40. } Uf2BlockTrailer;
  41. static bool uf2_block_header_read(Uf2BlockHeader* header, File* file) {
  42. const size_t size_read = storage_file_read(file, header, sizeof(Uf2BlockHeader));
  43. return size_read == sizeof(Uf2BlockHeader);
  44. }
  45. static bool
  46. uf2_block_header_verify(const Uf2BlockHeader* header, uint32_t family_id, size_t payload_size) {
  47. bool success = false;
  48. do {
  49. if(header->magic_start[0] != UF2_MAGIC_START_0) break;
  50. if(header->magic_start[1] != UF2_MAGIC_START_1) break;
  51. if(header->flags & Uf2FlagNotMainFlash) {
  52. FURI_LOG_E(TAG, "Non-flash blocks are not supported (block #%lu)", header->block_no);
  53. break;
  54. }
  55. if(header->flags & Uf2FlagFamilyIdPresent) {
  56. if(header->family_id != family_id) {
  57. FURI_LOG_E(
  58. TAG,
  59. "Family ID expected: %lX, got: %lX (block #%lu)",
  60. family_id,
  61. header->family_id,
  62. header->block_no);
  63. break;
  64. }
  65. }
  66. if(header->payload_size != payload_size) {
  67. FURI_LOG_E(
  68. TAG,
  69. "Only %zu-byte block payloads are supported (block #%lu)",
  70. payload_size,
  71. header->block_no);
  72. break;
  73. }
  74. if(header->target_addr % payload_size != 0) {
  75. FURI_LOG_E(
  76. TAG,
  77. "Only %zu-byte aligned are allowed (block #%lu)",
  78. payload_size,
  79. header->block_no);
  80. break;
  81. }
  82. success = true;
  83. } while(false);
  84. return success;
  85. }
  86. static bool uf2_block_header_skip(File* file) {
  87. return storage_file_seek(file, sizeof(Uf2BlockHeader), false);
  88. }
  89. static bool uf2_block_payload_skip(File* file) {
  90. return storage_file_seek(file, sizeof(Uf2BlockData), false);
  91. }
  92. static bool uf2_block_trailer_skip(File* file) {
  93. return storage_file_seek(file, sizeof(Uf2BlockTrailer), false);
  94. }
  95. static bool uf2_block_payload_read(File* file, void* payload_data, size_t payload_size) {
  96. bool success = false;
  97. do {
  98. const size_t size_read = storage_file_read(file, payload_data, payload_size);
  99. if(size_read != payload_size) break;
  100. if(!storage_file_seek(file, UF2_DATA_SIZE - payload_size, false)) break;
  101. success = true;
  102. } while(false);
  103. return success;
  104. }
  105. static bool uf2_block_trailer_read(Uf2BlockTrailer* trailer, File* file) {
  106. const size_t size_read = storage_file_read(file, trailer, sizeof(Uf2BlockTrailer));
  107. return size_read == sizeof(Uf2BlockTrailer);
  108. }
  109. static bool uf2_block_trailer_verify(const Uf2BlockTrailer* trailer) {
  110. return trailer->magic_end == UF2_MAGIC_END;
  111. }
  112. bool uf2_get_block_count(File* file, uint32_t* block_count) {
  113. const size_t file_size = storage_file_size(file);
  114. if(file_size == 0) {
  115. FURI_LOG_E(TAG, "File size is zero");
  116. return false;
  117. } else if(file_size % UF2_BLOCK_SIZE != 0) {
  118. FURI_LOG_E(TAG, "File size is not a multiple of %lu bytes", UF2_BLOCK_SIZE);
  119. return false;
  120. }
  121. *block_count = file_size / UF2_BLOCK_SIZE;
  122. return true;
  123. }
  124. bool uf2_verify_block(File* file, uint32_t family_id, size_t payload_size) {
  125. Uf2BlockHeader header;
  126. Uf2BlockTrailer trailer;
  127. if(!uf2_block_header_read(&header, file)) return false;
  128. if(!uf2_block_header_verify(&header, family_id, payload_size)) return false;
  129. if(!uf2_block_payload_skip(file)) return false;
  130. if(!uf2_block_trailer_read(&trailer, file)) return false;
  131. if(!uf2_block_trailer_verify(&trailer)) return false;
  132. return true;
  133. }
  134. bool uf2_read_block(File* file, void* payload_data, size_t payload_size) {
  135. if(!uf2_block_header_skip(file)) return false;
  136. if(!uf2_block_payload_read(file, payload_data, payload_size)) return false;
  137. if(!uf2_block_trailer_skip(file)) return false;
  138. return true;
  139. }