flipper_application.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #include "flipper_application.h"
  2. #include "elf/elf_file.h"
  3. #include <notification/notification_messages.h>
  4. #include "application_assets.h"
  5. #define TAG "fapp"
  6. struct FlipperApplication {
  7. ELFDebugInfo state;
  8. FlipperApplicationManifest manifest;
  9. ELFFile* elf;
  10. FuriThread* thread;
  11. };
  12. /* For debugger access to app state */
  13. FlipperApplication* last_loaded_app = NULL;
  14. FlipperApplication*
  15. flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
  16. FlipperApplication* app = malloc(sizeof(FlipperApplication));
  17. app->elf = elf_file_alloc(storage, api_interface);
  18. app->thread = NULL;
  19. return app;
  20. }
  21. void flipper_application_free(FlipperApplication* app) {
  22. furi_assert(app);
  23. if(app->thread) {
  24. furi_thread_join(app->thread);
  25. furi_thread_free(app->thread);
  26. }
  27. last_loaded_app = NULL;
  28. elf_file_clear_debug_info(&app->state);
  29. elf_file_free(app->elf);
  30. free(app);
  31. }
  32. static FlipperApplicationPreloadStatus
  33. flipper_application_validate_manifest(FlipperApplication* app) {
  34. if(!flipper_application_manifest_is_valid(&app->manifest)) {
  35. return FlipperApplicationPreloadStatusInvalidManifest;
  36. }
  37. if(!flipper_application_manifest_is_target_compatible(&app->manifest)) {
  38. return FlipperApplicationPreloadStatusTargetMismatch;
  39. }
  40. if(!flipper_application_manifest_is_compatible(
  41. &app->manifest, elf_file_get_api_interface(app->elf))) {
  42. return FlipperApplicationPreloadStatusApiMismatch;
  43. }
  44. return FlipperApplicationPreloadStatusSuccess;
  45. }
  46. static bool flipper_application_process_manifest_section(
  47. File* file,
  48. size_t offset,
  49. size_t size,
  50. void* context) {
  51. FlipperApplicationManifest* manifest = context;
  52. if(size < sizeof(FlipperApplicationManifest)) {
  53. return false;
  54. }
  55. if(manifest == NULL) {
  56. return true;
  57. }
  58. return storage_file_seek(file, offset, true) &&
  59. storage_file_read(file, manifest, size) == size;
  60. }
  61. // we can't use const char* as context because we will lose the const qualifier
  62. typedef struct {
  63. const char* path;
  64. } FlipperApplicationPreloadAssetsContext;
  65. static bool flipper_application_process_assets_section(
  66. File* file,
  67. size_t offset,
  68. size_t size,
  69. void* context) {
  70. FlipperApplicationPreloadAssetsContext* preload_context = context;
  71. return flipper_application_assets_load(file, preload_context->path, offset, size);
  72. }
  73. static FlipperApplicationPreloadStatus
  74. flipper_application_load(FlipperApplication* app, const char* path, bool load_full) {
  75. if(!elf_file_open(app->elf, path)) {
  76. return FlipperApplicationPreloadStatusInvalidFile;
  77. }
  78. // if we are loading full file
  79. if(load_full) {
  80. // load section table
  81. if(!elf_file_load_section_table(app->elf)) {
  82. return FlipperApplicationPreloadStatusInvalidFile;
  83. }
  84. // load assets section
  85. FlipperApplicationPreloadAssetsContext preload_context = {.path = path};
  86. if(elf_process_section(
  87. app->elf,
  88. ".fapassets",
  89. flipper_application_process_assets_section,
  90. &preload_context) == ElfProcessSectionResultCannotProcess) {
  91. return FlipperApplicationPreloadStatusInvalidFile;
  92. }
  93. }
  94. // load manifest section
  95. if(elf_process_section(
  96. app->elf, ".fapmeta", flipper_application_process_manifest_section, &app->manifest) !=
  97. ElfProcessSectionResultSuccess) {
  98. return FlipperApplicationPreloadStatusInvalidFile;
  99. }
  100. return flipper_application_validate_manifest(app);
  101. }
  102. /* Parse headers, load manifest */
  103. FlipperApplicationPreloadStatus
  104. flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
  105. return flipper_application_load(app, path, false);
  106. }
  107. /* Parse headers, load full file */
  108. FlipperApplicationPreloadStatus
  109. flipper_application_preload(FlipperApplication* app, const char* path) {
  110. return flipper_application_load(app, path, true);
  111. }
  112. const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
  113. return &app->manifest;
  114. }
  115. FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
  116. last_loaded_app = app;
  117. ELFFileLoadStatus status = elf_file_load_sections(app->elf);
  118. switch(status) {
  119. case ELFFileLoadStatusSuccess:
  120. elf_file_init_debug_info(app->elf, &app->state);
  121. return FlipperApplicationLoadStatusSuccess;
  122. case ELFFileLoadStatusNoFreeMemory:
  123. return FlipperApplicationLoadStatusNoFreeMemory;
  124. case ELFFileLoadStatusMissingImports:
  125. return FlipperApplicationLoadStatusMissingImports;
  126. default:
  127. return FlipperApplicationLoadStatusUnspecifiedError;
  128. }
  129. }
  130. static int32_t flipper_application_thread(void* context) {
  131. elf_file_pre_run(last_loaded_app->elf);
  132. int32_t result = elf_file_run(last_loaded_app->elf, context);
  133. elf_file_post_run(last_loaded_app->elf);
  134. // wait until all notifications from RAM are completed
  135. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  136. const NotificationSequence sequence_empty = {
  137. NULL,
  138. };
  139. notification_message_block(notifications, &sequence_empty);
  140. furi_record_close(RECORD_NOTIFICATION);
  141. return result;
  142. }
  143. FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
  144. furi_check(app->thread == NULL);
  145. const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
  146. furi_check(manifest->stack_size > 0);
  147. app->thread = furi_thread_alloc_ex(
  148. manifest->name, manifest->stack_size, flipper_application_thread, args);
  149. return app->thread;
  150. }
  151. static const char* preload_status_strings[] = {
  152. [FlipperApplicationPreloadStatusSuccess] = "Success",
  153. [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
  154. [FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
  155. [FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
  156. [FlipperApplicationPreloadStatusApiMismatch] = "API version mismatch",
  157. [FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
  158. };
  159. static const char* load_status_strings[] = {
  160. [FlipperApplicationLoadStatusSuccess] = "Success",
  161. [FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
  162. [FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
  163. [FlipperApplicationLoadStatusMissingImports] = "Found unsatisfied imports",
  164. };
  165. const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
  166. if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) {
  167. return "Unknown error";
  168. }
  169. return preload_status_strings[status];
  170. }
  171. const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {
  172. if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) {
  173. return "Unknown error";
  174. }
  175. return load_status_strings[status];
  176. }