flipper_application.c 9.3 KB

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