dfu_file.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "dfu_file.h"
  2. #include <furi_hal.h>
  3. #define VALID_WHOLE_FILE_CRC 0xFFFFFFFF
  4. #define DFU_SUFFIX_VERSION 0x011A
  5. #define DFU_DATA_BUFFER_MAX_LEN 512
  6. #define DFU_SIGNATURE "DfuSe"
  7. bool dfu_file_validate_crc(File* dfuf, const DfuPageTaskProgressCb progress_cb, void* context) {
  8. if(!storage_file_is_open(dfuf) || !storage_file_seek(dfuf, 0, true)) {
  9. return false;
  10. }
  11. furi_hal_crc_reset();
  12. uint32_t file_crc = 0;
  13. uint8_t* data_buffer = malloc(DFU_DATA_BUFFER_MAX_LEN);
  14. uint16_t data_buffer_valid_len;
  15. uint32_t file_size = storage_file_size(dfuf);
  16. /* Feed file contents per sector into CRC calc */
  17. furi_hal_crc_acquire(osWaitForever);
  18. for(uint32_t fptr = 0; fptr < file_size;) {
  19. data_buffer_valid_len = storage_file_read(dfuf, data_buffer, DFU_DATA_BUFFER_MAX_LEN);
  20. if(data_buffer_valid_len == 0) {
  21. break;
  22. }
  23. fptr += data_buffer_valid_len;
  24. if((fptr % DFU_DATA_BUFFER_MAX_LEN == 0)) {
  25. progress_cb(fptr * 100 / file_size, context);
  26. }
  27. file_crc = furi_hal_crc_feed(data_buffer, data_buffer_valid_len);
  28. }
  29. furi_hal_crc_reset();
  30. free(data_buffer);
  31. /* Last 4 bytes of DFU file = CRC of previous file contents, inverted
  32. * If we calculate whole file CRC32, incl. embedded CRC,
  33. * that should give us 0xFFFFFFFF
  34. */
  35. return file_crc == VALID_WHOLE_FILE_CRC;
  36. }
  37. uint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* reference_params) {
  38. furi_assert(reference_params);
  39. DfuPrefix dfu_prefix = {0};
  40. DfuSuffix dfu_suffix = {0};
  41. uint16_t bytes_read = 0;
  42. if(!storage_file_is_open(dfuf) || !storage_file_seek(dfuf, 0, true)) {
  43. return 0;
  44. }
  45. const uint32_t dfu_suffix_offset = storage_file_size(dfuf) - sizeof(DfuSuffix);
  46. bytes_read = storage_file_read(dfuf, &dfu_prefix, sizeof(DfuPrefix));
  47. if(bytes_read != sizeof(DfuPrefix)) {
  48. return 0;
  49. }
  50. if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature))) {
  51. return 0;
  52. }
  53. if((dfu_prefix.bVersion != 1) || (dfu_prefix.DFUImageSize != dfu_suffix_offset)) {
  54. return 0;
  55. }
  56. if(!storage_file_seek(dfuf, dfu_suffix_offset, true)) {
  57. return 0;
  58. }
  59. bytes_read = storage_file_read(dfuf, &dfu_suffix, sizeof(DfuSuffix));
  60. if(bytes_read != sizeof(DfuSuffix)) {
  61. return 0;
  62. }
  63. if((dfu_suffix.bLength != sizeof(DfuSuffix)) || (dfu_suffix.bcdDFU != DFU_SUFFIX_VERSION)) {
  64. return 0;
  65. }
  66. /* TODO: check DfuSignature?.. */
  67. if((dfu_suffix.idVendor != reference_params->vendor) ||
  68. (dfu_suffix.idProduct != reference_params->product) ||
  69. (dfu_suffix.bcdDevice != reference_params->device)) {
  70. return 0;
  71. }
  72. return dfu_prefix.bTargets;
  73. }
  74. /* Assumes file is open, valid and read pointer is set at the start of image data
  75. */
  76. static DfuUpdateBlockResult dfu_file_perform_task_for_update_pages(
  77. const DfuUpdateTask* task,
  78. File* dfuf,
  79. const ImageElementHeader* header) {
  80. furi_assert(task);
  81. furi_assert(header);
  82. task->progress_cb(0, task->context);
  83. const size_t FLASH_PAGE_SIZE = furi_hal_flash_get_page_size();
  84. const size_t FLASH_PAGE_ALIGNMENT_MASK = FLASH_PAGE_SIZE - 1;
  85. if((header->dwElementAddress & FLASH_PAGE_ALIGNMENT_MASK) != 0) {
  86. /* start address is not aligned by page boundary -- we don't support that. Yet. */
  87. return UpdateBlockResult_Failed;
  88. }
  89. if(task->address_cb && (!task->address_cb(header->dwElementAddress) ||
  90. !task->address_cb(header->dwElementAddress + header->dwElementSize))) {
  91. storage_file_seek(dfuf, header->dwElementSize, false);
  92. task->progress_cb(100, task->context);
  93. return UpdateBlockResult_Skipped;
  94. }
  95. uint8_t* fw_block = malloc(FLASH_PAGE_SIZE);
  96. uint16_t bytes_read = 0;
  97. uint32_t element_offs = 0;
  98. while(element_offs < header->dwElementSize) {
  99. uint32_t n_bytes_to_read = FLASH_PAGE_SIZE;
  100. if((element_offs + n_bytes_to_read) > header->dwElementSize) {
  101. n_bytes_to_read = header->dwElementSize - element_offs;
  102. }
  103. bytes_read = storage_file_read(dfuf, fw_block, n_bytes_to_read);
  104. if(bytes_read == 0) {
  105. break;
  106. }
  107. int16_t i_page = furi_hal_flash_get_page_number(header->dwElementAddress + element_offs);
  108. if(i_page < 0) {
  109. break;
  110. }
  111. if(!task->task_cb(i_page, fw_block, bytes_read)) {
  112. break;
  113. }
  114. element_offs += bytes_read;
  115. task->progress_cb(element_offs * 100 / header->dwElementSize, task->context);
  116. }
  117. free(fw_block);
  118. return (element_offs == header->dwElementSize) ? UpdateBlockResult_OK :
  119. UpdateBlockResult_Failed;
  120. }
  121. bool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8_t n_targets) {
  122. TargetPrefix target_prefix = {0};
  123. ImageElementHeader image_element = {0};
  124. uint16_t bytes_read = 0;
  125. if(!storage_file_seek(dfuf, sizeof(DfuPrefix), true)) {
  126. return UpdateBlockResult_Failed;
  127. };
  128. for(uint8_t i_target = 0; i_target < n_targets; ++i_target) {
  129. bytes_read = storage_file_read(dfuf, &target_prefix, sizeof(TargetPrefix));
  130. if(bytes_read != sizeof(TargetPrefix)) {
  131. return UpdateBlockResult_Failed;
  132. }
  133. /* TODO: look into TargetPrefix and validate/filter?.. */
  134. for(uint32_t i_element = 0; i_element < target_prefix.dwNbElements; ++i_element) {
  135. bytes_read = storage_file_read(dfuf, &image_element, sizeof(ImageElementHeader));
  136. if(bytes_read != sizeof(ImageElementHeader)) {
  137. return UpdateBlockResult_Failed;
  138. }
  139. if(dfu_file_perform_task_for_update_pages(task, dfuf, &image_element) ==
  140. UpdateBlockResult_Failed) {
  141. return false;
  142. }
  143. }
  144. }
  145. return true;
  146. }