flipper_application.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. void* ep_thread_args;
  12. };
  13. /* For debugger access to app state */
  14. FlipperApplication* last_loaded_app = NULL;
  15. FlipperApplication*
  16. flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
  17. FlipperApplication* app = malloc(sizeof(FlipperApplication));
  18. app->elf = elf_file_alloc(storage, api_interface);
  19. app->thread = NULL;
  20. app->ep_thread_args = NULL;
  21. return app;
  22. }
  23. bool flipper_application_is_plugin(FlipperApplication* app) {
  24. return app->manifest.stack_size == 0;
  25. }
  26. void flipper_application_free(FlipperApplication* app) {
  27. furi_assert(app);
  28. if(app->thread) {
  29. furi_thread_join(app->thread);
  30. furi_thread_free(app->thread);
  31. }
  32. if(!flipper_application_is_plugin(app)) {
  33. last_loaded_app = NULL;
  34. }
  35. elf_file_clear_debug_info(&app->state);
  36. if(elf_file_is_init_complete(app->elf)) {
  37. elf_file_call_fini(app->elf);
  38. }
  39. elf_file_free(app->elf);
  40. free(app);
  41. }
  42. static FlipperApplicationPreloadStatus
  43. flipper_application_validate_manifest(FlipperApplication* app) {
  44. if(!flipper_application_manifest_is_valid(&app->manifest)) {
  45. return FlipperApplicationPreloadStatusInvalidManifest;
  46. }
  47. if(!flipper_application_manifest_is_target_compatible(&app->manifest)) {
  48. return FlipperApplicationPreloadStatusTargetMismatch;
  49. }
  50. if(!flipper_application_manifest_is_compatible(
  51. &app->manifest, elf_file_get_api_interface(app->elf))) {
  52. return FlipperApplicationPreloadStatusApiMismatch;
  53. }
  54. return FlipperApplicationPreloadStatusSuccess;
  55. }
  56. static bool flipper_application_process_manifest_section(
  57. File* file,
  58. size_t offset,
  59. size_t size,
  60. void* context) {
  61. FlipperApplicationManifest* manifest = context;
  62. if(size < sizeof(FlipperApplicationManifest)) {
  63. return false;
  64. }
  65. if(manifest == NULL) {
  66. return true;
  67. }
  68. return storage_file_seek(file, offset, true) &&
  69. storage_file_read(file, manifest, size) == size;
  70. }
  71. // we can't use const char* as context because we will lose the const qualifier
  72. typedef struct {
  73. const char* path;
  74. } FlipperApplicationPreloadAssetsContext;
  75. static bool flipper_application_process_assets_section(
  76. File* file,
  77. size_t offset,
  78. size_t size,
  79. void* context) {
  80. FlipperApplicationPreloadAssetsContext* preload_context = context;
  81. return flipper_application_assets_load(file, preload_context->path, offset, size);
  82. }
  83. static FlipperApplicationPreloadStatus
  84. flipper_application_load(FlipperApplication* app, const char* path, bool load_full) {
  85. if(!elf_file_open(app->elf, path)) {
  86. return FlipperApplicationPreloadStatusInvalidFile;
  87. }
  88. // if we are loading full file
  89. if(load_full) {
  90. // load section table
  91. if(!elf_file_load_section_table(app->elf)) {
  92. return FlipperApplicationPreloadStatusInvalidFile;
  93. }
  94. // load assets section
  95. FlipperApplicationPreloadAssetsContext preload_context = {.path = path};
  96. if(elf_process_section(
  97. app->elf,
  98. ".fapassets",
  99. flipper_application_process_assets_section,
  100. &preload_context) == ElfProcessSectionResultCannotProcess) {
  101. return FlipperApplicationPreloadStatusInvalidFile;
  102. }
  103. }
  104. // load manifest section
  105. if(elf_process_section(
  106. app->elf, ".fapmeta", flipper_application_process_manifest_section, &app->manifest) !=
  107. ElfProcessSectionResultSuccess) {
  108. return FlipperApplicationPreloadStatusInvalidFile;
  109. }
  110. return flipper_application_validate_manifest(app);
  111. }
  112. /* Parse headers, load manifest */
  113. FlipperApplicationPreloadStatus
  114. flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
  115. return flipper_application_load(app, path, false);
  116. }
  117. /* Parse headers, load full file */
  118. FlipperApplicationPreloadStatus
  119. flipper_application_preload(FlipperApplication* app, const char* path) {
  120. return flipper_application_load(app, path, true);
  121. }
  122. const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
  123. return &app->manifest;
  124. }
  125. FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
  126. if(!flipper_application_is_plugin(app)) {
  127. last_loaded_app = app;
  128. }
  129. ELFFileLoadStatus status = elf_file_load_sections(app->elf);
  130. switch(status) {
  131. case ELFFileLoadStatusSuccess:
  132. elf_file_init_debug_info(app->elf, &app->state);
  133. return FlipperApplicationLoadStatusSuccess;
  134. case ELFFileLoadStatusNoFreeMemory:
  135. return FlipperApplicationLoadStatusNoFreeMemory;
  136. case ELFFileLoadStatusMissingImports:
  137. return FlipperApplicationLoadStatusMissingImports;
  138. default:
  139. return FlipperApplicationLoadStatusUnspecifiedError;
  140. }
  141. }
  142. static int32_t flipper_application_thread(void* context) {
  143. furi_assert(context);
  144. FlipperApplication* app = (FlipperApplication*)context;
  145. elf_file_call_init(app->elf);
  146. FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf);
  147. int32_t ret_code = entry_point(app->ep_thread_args);
  148. elf_file_call_fini(app->elf);
  149. // wait until all notifications from RAM are completed
  150. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  151. const NotificationSequence sequence_empty = {
  152. NULL,
  153. };
  154. notification_message_block(notifications, &sequence_empty);
  155. furi_record_close(RECORD_NOTIFICATION);
  156. return ret_code;
  157. }
  158. FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
  159. furi_check(app->thread == NULL);
  160. furi_check(!flipper_application_is_plugin(app));
  161. app->ep_thread_args = args;
  162. const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
  163. app->thread = furi_thread_alloc_ex(
  164. manifest->name, manifest->stack_size, flipper_application_thread, app);
  165. return app->thread;
  166. }
  167. static const char* preload_status_strings[] = {
  168. [FlipperApplicationPreloadStatusSuccess] = "Success",
  169. [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
  170. [FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
  171. [FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
  172. [FlipperApplicationPreloadStatusApiMismatch] = "API version mismatch",
  173. [FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
  174. };
  175. static const char* load_status_strings[] = {
  176. [FlipperApplicationLoadStatusSuccess] = "Success",
  177. [FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
  178. [FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
  179. [FlipperApplicationLoadStatusMissingImports] = "Found unsatisfied imports",
  180. };
  181. const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
  182. if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) {
  183. return "Unknown error";
  184. }
  185. return preload_status_strings[status];
  186. }
  187. const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {
  188. if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) {
  189. return "Unknown error";
  190. }
  191. return load_status_strings[status];
  192. }
  193. const FlipperAppPluginDescriptor*
  194. flipper_application_plugin_get_descriptor(FlipperApplication* app) {
  195. if(!flipper_application_is_plugin(app)) {
  196. return NULL;
  197. }
  198. if(!elf_file_is_init_complete(app->elf)) {
  199. elf_file_call_init(app->elf);
  200. }
  201. typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void);
  202. get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf);
  203. furi_check(lib_ep);
  204. const FlipperAppPluginDescriptor* lib_descriptor = lib_ep();
  205. FURI_LOG_D(
  206. TAG,
  207. "Library for %s, API v. %lu loaded",
  208. lib_descriptor->appid,
  209. lib_descriptor->ep_api_version);
  210. return lib_descriptor;
  211. }