diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index 650859b3..1cd71668 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) if (DEFINED ENV{ESP_IDF_VERSION}) - set(BOARD_COMPONENTS Tactility) + set(BOARD_COMPONENTS Tactility TactilityC) if("${IDF_TARGET}" STREQUAL "esp32") list(APPEND BOARD_COMPONENTS @@ -28,6 +28,7 @@ else() add_executable(AppSim ${SOURCES}) target_link_libraries(AppSim PRIVATE Tactility + PRIVATE TactilityC PRIVATE TactilityCore PRIVATE TactilityHeadless PRIVATE Simulator diff --git a/App/Source/Main.cpp b/App/Source/Main.cpp index 97f42e75..74bf91fe 100644 --- a/App/Source/Main.cpp +++ b/App/Source/Main.cpp @@ -2,6 +2,7 @@ // Apps #include "Tactility.h" +#include "TactilityC/TactilityC.h" namespace tt::service::wifi { extern void wifi_task(void*); @@ -25,6 +26,8 @@ void app_main() { .autoStartAppId = nullptr }; + tt_init_tactility_c(); // ELF bindings for side-loading on ESP32 + tt::run(config); } diff --git a/CMakeLists.txt b/CMakeLists.txt index 09c6ea57..487640f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,9 +21,13 @@ if (DEFINED ENV{ESP_IDF_VERSION}) "Boards" "App" "Tactility" + "TactilityC" + "TactilityCore" "TactilityHeadless" "Libraries/esp_lvgl_port" + "Libraries/elf_loader" "Libraries/lvgl" + "Libraries/lv_screenshot" "Libraries/M5Unified" "Libraries/M5GFX" ) @@ -53,24 +57,24 @@ project(Tactility) # Defined as regular project for PC and component for ESP if (NOT DEFINED ENV{ESP_IDF_VERSION}) add_subdirectory(Tactility) + add_subdirectory(TactilityC) + add_subdirectory(TactilityCore) add_subdirectory(TactilityHeadless) add_subdirectory(Boards/Simulator) endif() -add_subdirectory(TactilityCore) - -add_subdirectory(Libraries/lv_screenshot) - if (NOT DEFINED ENV{ESP_IDF_VERSION}) # FreeRTOS set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Boards/Simulator/Source CACHE STRING "") set(FREERTOS_PORT GCC_POSIX CACHE STRING "") add_subdirectory(Libraries/FreeRTOS-Kernel) + add_subdirectory(Libraries/lv_screenshot) target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0") target_include_directories(freertos_kernel PUBLIC Boards/Simulator/Source # for FreeRTOSConfig.h ) + # EmbedTLS set(ENABLE_TESTING OFF) set(ENABLE_PROGRAMS OFF) diff --git a/Documentation/external-apps.md b/Documentation/external-apps.md new file mode 100644 index 00000000..5a297825 --- /dev/null +++ b/Documentation/external-apps.md @@ -0,0 +1,18 @@ +# External Apps + +## Building + +Steps to build: + +- Build the main project with `idf.py build` in the root folder of the project +- Open a terminal at `ExternalApps/${AppName}` +- The first time, run `./build.sh` (during consecutive runs, you can run `idf.py build`) +- Find the executable `build/${AppName}.app.elf` and copy it to an SD card. + +## Developing apps + +Currently, only C is supported. The `TactilityC` project converts Tactility's C++ code into C. + +Methods need to be manually exposed to the ELF apps, by editing `TactilityC/Source/TactilityC.cpp` + +You can put apps in `Data/config` or `Data/assets` during development, because putting it on an SD card takes more effort. diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 635246c7..d0403d7b 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,4 +1,5 @@ # TODOs +- Loader: Use Timer instead of Thread, and move API to `tt::app::` - Gpio: Use Timer instead of Thread - I2cScannerThread: Use Timer instead of Thread - Bug: I2C Scanner is on M5Stack devices is broken @@ -21,12 +22,14 @@ - Commit fix to esp_lvgl_port to have `esp_lvgl_port_disp.c` user driver_data instead of user_data - Wifi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes activate again. - T-Deck Plus: Create separate board config +- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility. # Core Ideas - Support for displays with different DPI. Consider the layer-based system like on Android. - If present, use LED to show boot status - 2 wire speaker support - tt::app::start() and similar functions as proxies for Loader app start/stop/etc. +- USB implementation to make device act as mass storage device. # App Ideas - System logger diff --git a/ExternalApps/HelloWorld/CMakeLists.txt b/ExternalApps/HelloWorld/CMakeLists.txt new file mode 100644 index 00000000..215952fa --- /dev/null +++ b/ExternalApps/HelloWorld/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.16) + +set(TACTILITY_SKIP_SPIFFS 1) +add_definitions(-DESP_TARGET) +add_compile_definitions(ESP_TARGET) + +add_definitions(-DLV_CONF_PATH=../../../Boards/Simulator/Source/lv_conf.h) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(EXTRA_COMPONENT_DIRS "../../Libraries/elf_loader") + +project(HelloWorld) + +include(elf_loader) +project_elf(HelloWorld) + diff --git a/ExternalApps/HelloWorld/build.sh b/ExternalApps/HelloWorld/build.sh new file mode 100755 index 00000000..4ad8547a --- /dev/null +++ b/ExternalApps/HelloWorld/build.sh @@ -0,0 +1,4 @@ +rm sdkconfig +cp ../../sdkconfig sdkconfig +cat sdkconfig.override >> sdkconfig +idf.py build diff --git a/ExternalApps/HelloWorld/main/CMakeLists.txt b/ExternalApps/HelloWorld/main/CMakeLists.txt new file mode 100644 index 00000000..d4c2270b --- /dev/null +++ b/ExternalApps/HelloWorld/main/CMakeLists.txt @@ -0,0 +1,16 @@ +file(GLOB_RECURSE SOURCE_FILES Source/*.c) + +idf_component_register( + SRCS ${SOURCE_FILES} +) + +add_prebuilt_library(Tactility ../../../build/esp-idf/Tactility/libTactility.a) +add_prebuilt_library(TactilityC ../../../build/esp-idf/TactilityC/libTactilityC.a) +add_prebuilt_library(TactilityCore ../../../build/esp-idf/TactilityCore/libTactilityCore.a) +add_prebuilt_library(TactilityHeadless ../../../build/esp-idf/TactilityHeadless/libTactilityHeadless.a) +add_prebuilt_library(lvgl ../../../build/esp-idf/lvgl/liblvgl.a) + +include_directories("../../../TactilityC/Source") +include_directories("../../../Libraries/lvgl/src") + +target_link_libraries(${COMPONENT_LIB} PRIVATE TactilityC Tactility TactilityCore TactilityHeadless lvgl) diff --git a/ExternalApps/HelloWorld/main/Source/main.c b/ExternalApps/HelloWorld/main/Source/main.c new file mode 100644 index 00000000..7dbf6862 --- /dev/null +++ b/ExternalApps/HelloWorld/main/Source/main.c @@ -0,0 +1,29 @@ +#include +#include "TactilityC/app/App.h" +#include "TactilityC/lvgl/Toolbar.h" + +/** + * Note: LVGL and Tactility methods need to be exposed manually from TactilityC/Source/TactilityC.cpp + * Only C is supported for now (C++ symbols fail to link) + */ +static void onShow(AppContextHandle context, lv_obj_t* parent) { + lv_obj_t* toolbar = tt_lvgl_toolbar_create(parent, context); + lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); + + lv_obj_t* label = lv_label_create(parent); + lv_label_set_text(label, "Hello, world!"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); +} + +int main(int argc, char* argv[]) { + tt_set_app_manifest( + "Hello World", + NULL, + NULL, + NULL, + onShow, + NULL, + NULL + ); + return 0; +} diff --git a/ExternalApps/HelloWorld/sdkconfig.override b/ExternalApps/HelloWorld/sdkconfig.override new file mode 100644 index 00000000..7ec27ef3 --- /dev/null +++ b/ExternalApps/HelloWorld/sdkconfig.override @@ -0,0 +1 @@ +CONFIG_PARTITION_TABLE_SINGLE_APP=y diff --git a/Libraries/elf_loader/.component_hash b/Libraries/elf_loader/.component_hash new file mode 100644 index 00000000..43f1ad39 --- /dev/null +++ b/Libraries/elf_loader/.component_hash @@ -0,0 +1 @@ +ec9b5b8881cf5c38113ed7eab28a71ed7bfc9f477d6faef851f7f59f7a07b536 \ No newline at end of file diff --git a/Libraries/elf_loader/CHANGELOG.md b/Libraries/elf_loader/CHANGELOG.md new file mode 100644 index 00000000..77b2772c --- /dev/null +++ b/Libraries/elf_loader/CHANGELOG.md @@ -0,0 +1,5 @@ +# ChangeLog + +## v0.1.0 - 2023-08-14 + +* Add basic ELF loader component diff --git a/Libraries/elf_loader/CMakeLists.txt b/Libraries/elf_loader/CMakeLists.txt new file mode 100644 index 00000000..e6e65016 --- /dev/null +++ b/Libraries/elf_loader/CMakeLists.txt @@ -0,0 +1,26 @@ + +if(CONFIG_ELF_LOADER) + set(srcs "src/esp_elf_symbol.c" + "src/esp_elf.c" + "src/esp_elf_adapter.c") + + if(CONFIG_IDF_TARGET_ARCH_XTENSA) + list(APPEND srcs "src/arch/esp_elf_xtensa.c") + + # ESP32-S2 need to set MMU to run ELF + if(CONFIG_IDF_TARGET_ESP32S2 AND (CONFIG_ELF_LOADER_LOAD_PSRAM)) + list(APPEND srcs "src/soc/esp_elf_esp32s2.c") + endif() + endif() + + set(include_dirs "include") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES spi_flash) + +include(package_manager) +if(CONFIG_ELF_LOADER) + cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) +endif() diff --git a/Libraries/elf_loader/Kconfig b/Libraries/elf_loader/Kconfig new file mode 100644 index 00000000..7b0d022d --- /dev/null +++ b/Libraries/elf_loader/Kconfig @@ -0,0 +1,48 @@ +menu "Espressif ELF Loader Configuration" + visible if (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) + + config ELF_LOADER + bool "Enable Espressif ELF Loader" + default y + depends on (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) + help + Select this option to enable ELF Loader and show the submenu with ELF Loader configuration choices. + + if ELF_LOADER + config ELF_LOADER_CACHE_OFFSET + bool + default n + help + Select this option if D-cache and I-cache has different offset to access the same physical address. + + config ELF_LOADER_SET_MMU + bool + default n + help + Select this option if D-cache and I-cache is not symmetry。 + + config ELF_LOADER_LOAD_PSRAM + bool "Load ELF to PSRAM" + default y + depends on (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) && SPIRAM + select ELF_LOADER_CACHE_OFFSET if (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) + select ELF_LOADER_SET_MMU if IDF_TARGET_ESP32S2 + help + Load ELF file into PSRAM instead of internal SRAM. + + menu "ELF Symbols Table" + + config ELF_LOADER_LIBC_SYMBOLS + bool "Libc Symbols Table" + default y + + config ELF_LOADER_ESPIDF_SYMBOLS + bool "ESP-IDF Symbols Table" + default y + + config ELF_LOADER_CUSTOMER_SYMBOLS + bool "Customer Symbols Table" + default n + endmenu + endif +endmenu diff --git a/Libraries/elf_loader/README.md b/Libraries/elf_loader/README.md new file mode 100644 index 00000000..a662f9d1 --- /dev/null +++ b/Libraries/elf_loader/README.md @@ -0,0 +1,83 @@ +## Description + +[![Component Registry](https://components.espressif.com/components/espressif/elf_loader/badge.svg)](https://components.espressif.com/components/espressif/elf_loader) + +Espressif ELF(Executable and Linkable Format) loader is a software development kit that is developed based on the ESP-IDF, mainly used to load ELF file compiled based on ESP32 series SoCs to the executable memory area, then link and execute it. + +In this way, the application does not need compile into the whole firmware in advance. It is like running the compiled program through terminal input `./main.o` on the Ubuntu platform automatically, which realizes the separation of application and kernel. + +This ELF loader supports following SoCs: + +- ESP32 +- ESP32-S2, support running ELF in PSRAM +- ESP32-S3, support running ELF in PSRAM + +### Usage + +#### Firmware + +Add a dependency on this component in your component or project's idf_component.yml file. + + ```yml + dependencies: + espressif/elf_loader: "0.*" + ``` + +Enable ELF loader in the menuconfig: + +``` +Component config ---> + ESP-ELFLoader Configuration ---> + [*] Enable Espressif ELF Loader +``` + +Add API calls in your project as follows: + +```c +#include "esp_elf.h" + + esp_elf_t elf; + + // Your Code + + esp_elf_init(&elf); + esp_elf_relocate(&elf, elf_file_data_bytes); + esp_elf_request(&elf, 0, argc, argv); + esp_elf_deinit(&elf); +``` + +#### ELF APP + +To use this feature to compile ELF file, including the required CMake file in your project's CMakeLists.txt file after the line project(XXXX). + +```cmake +project(XXXX) + +# Add +include(elf_loader) +project_elf(XXXX) +``` + +Build the project as an ordinary ESP-IDF project, and then the ELF file named `XXXX.app.elf` is in the build directory. + +### Adding the Component to Your Project + +Please use the component manager command add-dependency to add the elf_loader component as a dependency in your project. During the CMake step, the component will be downloaded automatically. + +``` +idf.py add-dependency "espressif/elf_loader=*" +``` + +### Examples + +Please use the component manager command create-project-from-example to create a project from the example template. + +``` +idf.py create-project-from-example "espressif/elf_loader=*:elf_loader_example" +``` + +This command will download the example elf_loader_example into the current folder. You can navigate into it to build and flash the example. + +Alternatively, you can download examples from the esp-iot-solution repository: +1. [build_elf_file_example](https://github.com/espressif/esp-iot-solution/tree/master/examples/elf_loader/build_elf_file_example) +2. [elf_loader_example](https://github.com/espressif/esp-iot-solution/tree/master/examples/elf_loader/elf_loader_example) diff --git a/Libraries/elf_loader/elf_loader.cmake b/Libraries/elf_loader/elf_loader.cmake new file mode 100644 index 00000000..7999fe35 --- /dev/null +++ b/Libraries/elf_loader/elf_loader.cmake @@ -0,0 +1,62 @@ +# The script is to generate ELF for application + +# Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed +# using an underscore prefixed function of the same name. The following lines make sure that project calls +# the original project(). See https://cmake.org/pipermail/cmake/2015-October/061751.html. +function(project_elf) +endfunction() + +function(_project_elf) +endfunction() + +macro(project_elf project_name) + # Enable these options to remove unused symbols and reduce linked objects + set(cflags -nostartfiles + -nostdlib + -fPIC + -shared + -e app_main + -fdata-sections + -ffunction-sections + -Wl,--gc-sections + -fvisibility=hidden) + + # Enable this options to remove unnecessary sections in + list(APPEND cflags -Wl,--strip-all + -Wl,--strip-debug + -Wl,--strip-discarded) + + list(APPEND cflags -Dmain=app_main) + + idf_build_set_property(COMPILE_OPTIONS "${cflags}" APPEND) + + set(elf_app "${CMAKE_PROJECT_NAME}.app.elf") + + # Remove more unused sections + string(REPLACE "-elf-gcc" "-elf-strip" ${CMAKE_STRIP} ${CMAKE_C_COMPILER}) + set(strip_flags --strip-unneeded + --remove-section=.xt.lit + --remove-section=.xt.prop + --remove-section=.comment + --remove-section=.xtensa.info + --remove-section=.got.loc + --remove-section=.got) + + # Link input list of libraries to ELF + if(ELF_COMPONENTS) + foreach(c "${ELF_COMPONENTS}") + set(elf_libs "${elf_libs}" "esp-idf/${c}/lib${c}.a") + endforeach() + endif() + + set(elf_libs ${elf_libs} "${ELF_LIBS}" "esp-idf/main/libmain.a") + spaces2list(elf_libs) + + add_custom_command(OUTPUT elf_app + COMMAND ${CMAKE_C_COMPILER} ${cflags} ${elf_libs} -o ${elf_app} + COMMAND ${CMAKE_STRIP} ${strip_flags} ${elf_app} + DEPENDS idf::main + COMMENT "Build ELF: ${elf_app}" + ) + add_custom_target(elf ALL DEPENDS elf_app) +endmacro() diff --git a/Libraries/elf_loader/examples/build_elf_file_example/CMakeLists.txt b/Libraries/elf_loader/examples/build_elf_file_example/CMakeLists.txt new file mode 100644 index 00000000..98236462 --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/CMakeLists.txt @@ -0,0 +1,11 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(hello_world) + +include(elf_loader) +project_elf(hello_world) diff --git a/Libraries/elf_loader/examples/build_elf_file_example/README.md b/Libraries/elf_loader/examples/build_elf_file_example/README.md new file mode 100644 index 00000000..a73814cd --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/README.md @@ -0,0 +1,13 @@ +## Build ELF file Example + +This example shows how to build ELF file. + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. + +* Note: Only ESP32, ESP32-S2 and ESP32-S3 are supported + +### Build and Flash + +Run `idf.py build` to build project, and when compiling is done you can see `hello_world.app.elf` in `build` directory. diff --git a/Libraries/elf_loader/examples/build_elf_file_example/main/CMakeLists.txt b/Libraries/elf_loader/examples/build_elf_file_example/main/CMakeLists.txt new file mode 100644 index 00000000..34613e8e --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "main.c") diff --git a/Libraries/elf_loader/examples/build_elf_file_example/main/idf_component.yml b/Libraries/elf_loader/examples/build_elf_file_example/main/idf_component.yml new file mode 100644 index 00000000..02899c5f --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + elf_loader: + version: "0.*" + override_path: "../../../../components/elf_loader" + diff --git a/Libraries/elf_loader/examples/build_elf_file_example/main/main.c b/Libraries/elf_loader/examples/build_elf_file_example/main/main.c new file mode 100644 index 00000000..a6620b23 --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/main/main.c @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +char buffer[] = "hello world %d\n"; +static int s_cnt; + +int try_test(void) +{ + for (int i = 0; i < 10; i++) { + printf(buffer, s_cnt++); + sleep(1); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + try_test(); + + return 0; +} diff --git a/Libraries/elf_loader/examples/build_elf_file_example/sdkconfig.defaults b/Libraries/elf_loader/examples/build_elf_file_example/sdkconfig.defaults new file mode 100644 index 00000000..12dc1c31 --- /dev/null +++ b/Libraries/elf_loader/examples/build_elf_file_example/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=n diff --git a/Libraries/elf_loader/examples/elf_loader_example/CMakeLists.txt b/Libraries/elf_loader/examples/elf_loader_example/CMakeLists.txt new file mode 100644 index 00000000..3ed8812c --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(elf-loader) diff --git a/Libraries/elf_loader/examples/elf_loader_example/README.md b/Libraries/elf_loader/examples/elf_loader_example/README.md new file mode 100644 index 00000000..7bec6995 --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/README.md @@ -0,0 +1,50 @@ +## ELF Loader Example + +This example shows how to use ELF loader to run ELF file. + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. + +* Note: Only ESP32, ESP32-S2 and ESP32-S3 are supported + +### Hardware Required + +* A development board based on espressif ESP32/ESP32-S2/ESP32-S3 SoC +* A USB cable for power supply and programming + +### Configure the Project + +Open the project configuration menu (`idf.py menuconfig`). + +In the `Example Configuration` menu: + +* Enable ELF input arguments in the `Support arguments for ELF main` option if necessary. +* Set the arguments in the `Arguments of ELF main` option. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +## Example Output + +As you run the example, you will see the following log: + +``` +I (0) cpu_start: Starting scheduler on APP CPU. +I (382) elf_loader: Start to relocate ELF +I (392) elf_loader: Start to run ELF +hello world 0 +hello world 1 +hello world 2 +hello world 3 +hello world 4 +hello world 5 +hello world 6 +hello world 7 +hello world 8 +hello world 9 +I (10392) elf_loader: Success to exit from APP of ELF +``` diff --git a/Libraries/elf_loader/examples/elf_loader_example/main/CMakeLists.txt b/Libraries/elf_loader/examples/elf_loader_example/main/CMakeLists.txt new file mode 100644 index 00000000..28150054 --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "elf_loader_example_main.c" + INCLUDE_DIRS "" + EMBED_TXTFILES "test.elf") diff --git a/Libraries/elf_loader/examples/elf_loader_example/main/Kconfig.projbuild b/Libraries/elf_loader/examples/elf_loader_example/main/Kconfig.projbuild new file mode 100644 index 00000000..1d935c2f --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/main/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "ELF Loader Example Configuration" + + config ELF_LOADER_MAIN_ARGS + bool "Support arguments for ELF main" + default n + + config ELF_LOADER_MAIN_ARGS_STRING + string "Arguments of ELF main" + default "main" + depends on ELF_LOADER_MAIN_ARGS + +endmenu diff --git a/Libraries/elf_loader/examples/elf_loader_example/main/elf_loader_example_main.c b/Libraries/elf_loader/examples/elf_loader_example/main/elf_loader_example_main.c new file mode 100644 index 00000000..1ee816ad --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/main/elf_loader_example_main.c @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "esp_log.h" +#include "esp_elf.h" + +static const char *TAG = "elf_loader"; + +extern const uint8_t test_elf_start[] asm("_binary_test_elf_start"); +extern const uint8_t test_elf_end[] asm("_binary_test_elf_end"); + +#ifdef CONFIG_ELF_LOADER_MAIN_ARGS +static int elf_args_decode(const char *str, int *argc, char ***argv) +{ + int n; + char **argv_buf; + char *s; + char *pbuf; + int len; + + s = (char *)str; + n = 1; + len = 1; + while (*s) { + if (*s++ == ' ') { + n++; + } + + len++; + } + + pbuf = malloc(n * sizeof(char *) + len); + if (!pbuf) { + return -ENOMEM; + } + + argv_buf = (char **)pbuf; + s = pbuf + n * sizeof(char *); + memcpy(s, str, len); + for (int i = 0; i < n; i++) { + argv_buf[i] = s; + + while (*s != ' ' || *s == 0) { + s++; + } + + *s++ = 0; + } + + *argc = n; + *argv = argv_buf; + + return 0; +} +#endif + +int app_main(void) +{ + int ret; + int argc; + char **argv; + esp_elf_t elf; + const uint8_t *buffer = test_elf_start; + + ESP_LOGI(TAG, "Start to relocate ELF file"); + +#ifdef CONFIG_ELF_LOADER_MAIN_ARGS + if (strlen(CONFIG_ELF_LOADER_MAIN_ARGS_STRING) <= 0) { + ESP_LOGE(TAG, "Failed to check arguments %s", + CONFIG_ELF_LOADER_MAIN_ARGS_STRING); + return -1; + } + + ret = elf_args_decode(CONFIG_ELF_LOADER_MAIN_ARGS_STRING, &argc, &argv); + if (ret < 0) { + ESP_LOGE(TAG, "Failed to decode arguments %s errno=%d", + CONFIG_ELF_LOADER_MAIN_ARGS_STRING, ret); + return ret; + } +#else + argc = 0; + argv = NULL; +#endif + + ret = esp_elf_init(&elf); + if (ret < 0) { + ESP_LOGE(TAG, "Failed to initialize ELF file errno=%d", ret); + return ret; + } + + ret = esp_elf_relocate(&elf, buffer); + if (ret < 0) { + ESP_LOGE(TAG, "Failed to relocate ELF file errno=%d", ret); + return ret; + } + + ESP_LOGI(TAG, "Start to run ELF file"); + + esp_elf_request(&elf, 0, argc, argv); + + ESP_LOGI(TAG, "Success to exit from ELF file"); + + esp_elf_deinit(&elf); + +#ifdef CONFIG_ELF_LOADER_MAIN_ARGS + if (argv) { + free(argv); + } +#endif + + return 0; +} diff --git a/Libraries/elf_loader/examples/elf_loader_example/main/idf_component.yml b/Libraries/elf_loader/examples/elf_loader_example/main/idf_component.yml new file mode 100644 index 00000000..02899c5f --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + elf_loader: + version: "0.*" + override_path: "../../../../components/elf_loader" + diff --git a/Libraries/elf_loader/examples/elf_loader_example/main/test.elf b/Libraries/elf_loader/examples/elf_loader_example/main/test.elf new file mode 100644 index 00000000..9fdd1fb3 Binary files /dev/null and b/Libraries/elf_loader/examples/elf_loader_example/main/test.elf differ diff --git a/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.spiram b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.spiram new file mode 100644 index 00000000..23b2a834 --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.spiram @@ -0,0 +1,2 @@ +CONFIG_SPIRAM=y +CONFIG_ELF_LOADER_LOAD_PSRAM=y \ No newline at end of file diff --git a/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.sram b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.sram new file mode 100644 index 00000000..cde0e63c --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.ci.sram @@ -0,0 +1 @@ +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n \ No newline at end of file diff --git a/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.defaults b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.defaults new file mode 100644 index 00000000..d0ea27a6 --- /dev/null +++ b/Libraries/elf_loader/examples/elf_loader_example/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n diff --git a/Libraries/elf_loader/idf_component.yml b/Libraries/elf_loader/idf_component.yml new file mode 100644 index 00000000..6d6fbed3 --- /dev/null +++ b/Libraries/elf_loader/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + espressif/cmake_utilities: + version: 0.* + idf: + version: '>=4.4.3' +description: Espressif ELF(Executable and Linkable Format) Loader +url: https://github.com/espressif/esp-iot-solution/tree/master/components/elf_loader +version: 0.1.0 diff --git a/Libraries/elf_loader/include/elf_symbol.h b/Libraries/elf_loader/include/elf_symbol.h new file mode 100644 index 00000000..2a30c169 --- /dev/null +++ b/Libraries/elf_loader/include/elf_symbol.h @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "private/elf_types.h" +#include "elf_symbol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ELFSYM_EXPORT(_sym) { #_sym, (void*)&_sym } +#define ESP_ELFSYM_END { NULL, NULL } + +/** @brief Function symbol description */ + +struct esp_elfsym { + const char *name; /*!< Function name */ + const void *sym; /*!< Function pointer */ +}; + +/** + * @brief Find symbol address by name. + * + * @param sym_name - Symbol name + * + * @return Symbol address if success or 0 if failed. + */ +uintptr_t elf_find_sym(const char *sym_name); + +#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS +void elf_set_custom_symbols(const struct esp_elfsym* symbols); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/Libraries/elf_loader/include/esp_elf.h b/Libraries/elf_loader/include/esp_elf.h new file mode 100644 index 00000000..646c607b --- /dev/null +++ b/Libraries/elf_loader/include/esp_elf.h @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "private/elf_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Map symbol's address of ELF to physic space. + * + * @param elf - ELF object pointer + * @param sym - ELF symbol address + * + * @return Mapped physic address. + */ +uintptr_t esp_elf_map_sym(esp_elf_t *elf, uintptr_t sym); + +/** + * @brief Initialize ELF object. + * + * @param elf - ELF object pointer + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_init(esp_elf_t *elf); + +/** + * @brief Decode and relocate ELF data. + * + * @param elf - ELF object pointer + * @param pbuf - ELF data buffer + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_relocate(esp_elf_t *elf, const uint8_t *pbuf); + +/** + * @brief Request running relocated ELF function. + * + * @param elf - ELF object pointer + * @param opt - Request options + * @param argc - Arguments number + * @param argv - Arguments value array + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_request(esp_elf_t *elf, int opt, int argc, char *argv[]); + +/** + * @brief Deinitialize ELF object. + * + * @param elf - ELF object pointer + * + * @return None + */ +void esp_elf_deinit(esp_elf_t *elf); + +/** + * @brief Print header description information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_hdr(const uint8_t *pbuf); + +/** + * @brief Print section header description information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_shdr(const uint8_t *pbuf); + +/** + * @brief Print section information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_sec(esp_elf_t *elf); + +#ifdef __cplusplus +} +#endif diff --git a/Libraries/elf_loader/include/private/elf_platform.h b/Libraries/elf_loader/include/private/elf_platform.h new file mode 100644 index 00000000..8ca092f5 --- /dev/null +++ b/Libraries/elf_loader/include/private/elf_platform.h @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "private/elf_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ELF_ALIGN_SIZE +#define ELF_ALIGN_SIZE 4 +#endif + +#define ELF_ALIGN(_a) (((_a) + (ELF_ALIGN_SIZE - 1)) & \ + (~(ELF_ALIGN_SIZE - 1))) + +/** + * @brief Allocate block of memory. + * + * @param n - Memory size in byte + * @param exec - True: memory can run executable code; False: memory can R/W data + * + * @return Memory pointer if success or NULL if failed. + */ +void *esp_elf_malloc(uint32_t n, bool exec); + +/** + * @brief Free block of memory. + * + * @param ptr - memory block pointer allocated by "esp_elf_malloc" + * + * @return None + */ +void esp_elf_free(void *ptr); + +/** + * @brief Relocates target architecture symbol of ELF + * + * @param elf - ELF object pointer + * @param rela - Relocated symbol data + * @param sym - ELF symbol table + * @param addr - Jumping target address + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_arch_relocate(esp_elf_t *elf, const elf32_rela_t *rela, + const elf32_sym_t *sym, uint32_t addr); + +/** + * @brief Remap symbol from ".data" to ".text" section. + * + * @param elf - ELF object pointer + * @param sym - ELF symbol table + * + * @return Remapped symbol value + */ +#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET +uintptr_t elf_remap_text(esp_elf_t *elf, uintptr_t sym); +#endif + +/** + * @brief Flush data from cache to external RAM. + * + * @param None + * + * @return None + */ +#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM +void esp_elf_arch_flush(void); +#endif + +/** + * @brief Initialize MMU hardware remapping function. + * + * @param elf - ELF object pointer + * + * @return 0 if success or a negative value if failed. + */ +#ifdef CONFIG_ELF_LOADER_SET_MMU +int esp_elf_arch_init_mmu(esp_elf_t *elf); +#endif + +/** + * @brief De-initialize MMU hardware remapping function. + * + * @param elf - ELF object pointer + * + * @return None + */ +#ifdef CONFIG_ELF_LOADER_SET_MMU +void esp_elf_arch_deinit_mmu(esp_elf_t *elf); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/Libraries/elf_loader/include/private/elf_types.h b/Libraries/elf_loader/include/private/elf_types.h new file mode 100644 index 00000000..9b542242 --- /dev/null +++ b/Libraries/elf_loader/include/private/elf_types.h @@ -0,0 +1,205 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EI_NIDENT 16 /*!< Magic number and other information length */ + +/** @brief Section Type */ + +#define SHT_NULL 0 /*!< invalid section header */ +#define SHT_PROGBITS 1 /*!< some useful section like .text, .data .got, .plt .rodata .interp and so on */ +#define SHT_SYMTAB 2 /*!< symbol table */ +#define SHT_STRTAB 3 /*!< string table */ +#define SHT_RELA 4 /*!< relocation table */ +#define SHT_HASH 5 /*!< HASH table */ +#define SHT_SYNAMIC 6 /*!< dynamic symbol table */ +#define SHT_NOTE 7 /*!< note information */ +#define SHT_NOBITS 8 /*!< .bss */ +#define SHT_REL 9 /*!< relocation table */ +#define SHT_SHKIB 10 /*!< reserved but has unspecified semantics. */ +#define SHT_SYNSYM 11 /*!< dynamic symbol */ +#define SHT_LOPROC 0x70000000 /*!< reserved for processor-specific semantics */ +#define SHT_LOUSER 0x7fffffff /*!< lower bound of the range of indexes reserved for application programs */ +#define SHT_HIUSER 0xffffffff /*!< upper bound of the range of indexes reserved for application programs. */ + +/** @brief Section Attribute Flags */ + +#define SHF_WRITE 1 /*!< writable when task runs */ +#define SHF_ALLOC 2 /*!< allocated when task runs */ +#define SHF_EXECINSTR 4 /*!< machine code */ +#define SHF_MASKPROG 0xf0000000 /*!< reserved for processor-specific semantics */ + +/** @brief Symbol Types */ + +#define STT_NOTYPE 0 /*!< symbol type is unspecified */ +#define STT_OBJECT 1 /*!< data object */ +#define STT_FUNC 2 /*!< code object */ +#define STT_SECTION 3 /*!< symbol identifies an ELF section */ +#define STT_FILE 4 /*!< symbol's name is file name */ +#define STT_COMMON 5 /*!< common data object */ +#define STT_TLS 6 /*!< thread-local data object */ +#define STT_NUM 7 /*!< defined types in generic range */ +#define STT_LOOS 10 /*!< Low OS specific range */ +#define STT_HIOS 12 /*!< High OS specific range */ +#define STT_LOPROC 13 /*!< processor specific range */ +#define STT_HIPROC 15 /*!< processor specific link range */ + +/** @brief Section names */ + +#define ELF_BSS ".bss" /*!< uninitialized data */ +#define ELF_DATA ".data" /*!< initialized data */ +#define ELF_DEBUG ".debug" /*!< debug */ +#define ELF_DYNAMIC ".dynamic" /*!< dynamic linking information */ +#define ELF_DYNSTR ".dynstr" /*!< dynamic string table */ +#define ELF_DYNSYM ".dynsym" /*!< dynamic symbol table */ +#define ELF_FINI ".fini" /*!< termination code */ +#define ELF_GOT ".got" /*!< global offset table */ +#define ELF_HASH ".hash" /*!< symbol hash table */ +#define ELF_INIT ".init" /*!< initialization code */ +#define ELF_REL_DATA ".rel.data" /*!< relocation data */ +#define ELF_REL_FINI ".rel.fini" /*!< relocation termination code */ +#define ELF_REL_INIT ".rel.init" /*!< relocation initialization code */ +#define ELF_REL_DYN ".rel.dyn" /*!< relocaltion dynamic link info */ +#define ELF_REL_RODATA ".rel.rodata" /*!< relocation read-only data */ +#define ELF_REL_TEXT ".rel.text" /*!< relocation code */ +#define ELF_RODATA ".rodata" /*!< read-only data */ +#define ELF_SHSTRTAB ".shstrtab" /*!< section header string table */ +#define ELF_STRTAB ".strtab" /*!< string table */ +#define ELF_SYMTAB ".symtab" /*!< symbol table */ +#define ELF_TEXT ".text" /*!< code */ +#define ELF_DATA_REL_RO ".data.rel.ro" /*!< dynamic read-only data */ + +/** @brief ELF section and symbol operation */ + +#define ELF_SEC_TEXT 0 +#define ELF_SEC_BSS 1 +#define ELF_SEC_DATA 2 +#define ELF_SEC_RODATA 3 +#define ELF_SEC_DRLRO 4 +#define ELF_SECS 5 + +#define ELF_ST_BIND(_i) ((_i) >> 4) +#define ELF_ST_TYPE(_i) ((_i) & 0xf) +#define ELF_ST_INFO(_b, _t) (((_b)<<4) + ((_t) & 0xf)) + +#define ELF_R_SYM(_i) ((_i) >> 8) +#define ELF_R_TYPE(_i) ((unsigned char)(_i)) +#define ELF_R_INFO(_s, _t) (((_s) << 8) + (unsigned char)(_t)) + +#define ELF_SEC_MAP(_elf, _sec, _addr) \ + ((_elf)->sec[(_sec)].addr - \ + (_elf)->sec[(_sec)].v_addr + \ + (_addr)) + +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef unsigned int Elf32_Word; +typedef unsigned short Elf32_Half; +typedef int Elf32_Sword; + +/** @brief ELF Header */ + +typedef struct elf32_hdr { + unsigned char ident[EI_NIDENT]; /*!< ELF Identification */ + Elf32_Half type; /*!< object file type */ + Elf32_Half machine; /*!< machine */ + Elf32_Word version; /*!< object file version */ + Elf32_Addr entry; /*!< virtual entry point */ + Elf32_Off phoff; /*!< program header table offset */ + Elf32_Off shoff; /*!< section header table offset */ + Elf32_Word flags; /*!< processor-specific flags */ + Elf32_Half ehsize; /*!< ELF header size */ + Elf32_Half phentsize; /*!< program header entry size */ + Elf32_Half phnum; /*!< number of program header entries */ + Elf32_Half shentsize; /*!< section header entry size */ + Elf32_Half shnum; /*!< number of section header entries */ + Elf32_Half shstrndx; /*!< section header table's "section header string table" entry offset */ +} elf32_hdr_t; + +/** @brief Section Header */ + +typedef struct elf32_shdr { + Elf32_Word name; /*!< section index and it is offset in section .shstrtab */ + Elf32_Word type; /*!< type */ + Elf32_Word flags; /*!< flags */ + Elf32_Addr addr; /*!< start address when map to task space */ + Elf32_Off offset; /*!< offset address from file start address */ + Elf32_Word size; /*!< size */ + Elf32_Word link; /*!< link to another section */ + Elf32_Word info; /*!< additional section information */ + Elf32_Word addralign; /*!< address align */ + Elf32_Word entsize; /*!< index table size */ +} elf32_shdr_t; + +/** @brief Symbol Table Entry */ + +typedef struct elf32_sym { + Elf32_Word name; /*!< name - index into string table */ + Elf32_Addr value; /*!< symbol value */ + Elf32_Word size; /*!< symbol size */ + unsigned char info; /*!< type and binding */ + unsigned char other; /*!< 0 - no defined meaning */ + Elf32_Half shndx; /*!< section header index */ +} elf32_sym_t; + +/** @brief Relocation entry with implicit addend */ + +typedef struct elf32_rel { + Elf32_Addr offset; /*!< offset of relocation */ + Elf32_Word info; /*!< symbol table index and type */ +} elf32_rel_t; + +/** @brief Relocation entry with explicit addend */ + +typedef struct elf32_rela { + Elf32_Addr offset; /*!< offset of relocation */ + Elf32_Word info; /*!< symbol table index and type */ + Elf32_Sword addend; /*!< Added information */ +} elf32_rela_t; + +/** @brief ELF section object */ + +typedef struct esp_elf_sec { + uintptr_t v_addr; /*!< symbol virtual address */ + off_t offset; /*!< offset in ELF */ + + uintptr_t addr; /*!< section physic address in memory */ + size_t size; /*!< section size */ +} esp_elf_sec_t; + +/** @brief ELF object */ + +typedef struct esp_elf { + unsigned char *ptext; /*!< instruction buffer pointer */ + + unsigned char *pdata; /*!< data buffer pointer */ + + esp_elf_sec_t sec[ELF_SECS]; /*!< ".bss", "data", "rodata", ".text" */ + + int (*entry)(int argc, char *argv[]); /*!< Entry pointer of ELF */ + +#ifdef CONFIG_ELF_LOADER_SET_MMU + uint32_t text_off; /* .text symbol offset */ + + uint32_t mmu_off; /* MMU unit offset */ + uint32_t mmu_num; /* MMU unit total number */ +#endif +} esp_elf_t; + +#ifdef __cplusplus +} +#endif diff --git a/Libraries/elf_loader/license.txt b/Libraries/elf_loader/license.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/Libraries/elf_loader/license.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Libraries/elf_loader/project_include.cmake b/Libraries/elf_loader/project_include.cmake new file mode 100644 index 00000000..90b2cbd5 --- /dev/null +++ b/Libraries/elf_loader/project_include.cmake @@ -0,0 +1 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH}) diff --git a/Libraries/elf_loader/src/arch/esp_elf_xtensa.c b/Libraries/elf_loader/src/arch/esp_elf_xtensa.c new file mode 100644 index 00000000..6edfe2ef --- /dev/null +++ b/Libraries/elf_loader/src/arch/esp_elf_xtensa.c @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_elf.h" +#include "esp_log.h" +#include "private/elf_platform.h" + +/** @brief Xtensa relocations defined by the ABIs */ + +#define R_XTENSA_NONE 0 +#define R_XTENSA_32 1 +#define R_XTENSA_RTLD 2 +#define R_XTENSA_GLOB_DAT 3 +#define R_XTENSA_JMP_SLOT 4 +#define R_XTENSA_RELATIVE 5 +#define R_XTENSA_PLT 6 +#define R_XTENSA_OP0 8 +#define R_XTENSA_OP1 9 +#define R_XTENSA_OP2 10 +#define R_XTENSA_ASM_EXPAND 11 +#define R_XTENSA_ASM_SIMPLIFY 12 +#define R_XTENSA_GNU_VTINHERIT 15 +#define R_XTENSA_GNU_VTENTRY 16 +#define R_XTENSA_DIFF8 17 +#define R_XTENSA_DIFF16 18 +#define R_XTENSA_DIFF32 19 +#define R_XTENSA_SLOT0_OP 20 +#define R_XTENSA_SLOT1_OP 21 +#define R_XTENSA_SLOT2_OP 22 +#define R_XTENSA_SLOT3_OP 23 +#define R_XTENSA_SLOT4_OP 24 +#define R_XTENSA_SLOT5_OP 25 +#define R_XTENSA_SLOT6_OP 26 +#define R_XTENSA_SLOT7_OP 27 +#define R_XTENSA_SLOT8_OP 28 +#define R_XTENSA_SLOT9_OP 29 +#define R_XTENSA_SLOT10_OP 30 +#define R_XTENSA_SLOT11_OP 31 +#define R_XTENSA_SLOT12_OP 32 +#define R_XTENSA_SLOT13_OP 33 +#define R_XTENSA_SLOT14_OP 34 +#define R_XTENSA_SLOT0_ALT 35 +#define R_XTENSA_SLOT1_ALT 36 +#define R_XTENSA_SLOT2_ALT 37 +#define R_XTENSA_SLOT3_ALT 38 +#define R_XTENSA_SLOT4_ALT 39 +#define R_XTENSA_SLOT5_ALT 40 +#define R_XTENSA_SLOT6_ALT 41 +#define R_XTENSA_SLOT7_ALT 42 +#define R_XTENSA_SLOT8_ALT 43 +#define R_XTENSA_SLOT9_ALT 44 +#define R_XTENSA_SLOT10_ALT 45 +#define R_XTENSA_SLOT11_ALT 46 +#define R_XTENSA_SLOT12_ALT 47 +#define R_XTENSA_SLOT13_ALT 48 +#define R_XTENSA_SLOT14_ALT 49 + +static const char *TAG = "elf_arch"; + +/** + * @brief Relocates target architecture symbol of ELF + * + * @param elf - ELF object pointer + * @param rela - Relocated symbol data + * @param sym - ELF symbol table + * @param addr - Jumping target address + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_arch_relocate(esp_elf_t *elf, const elf32_rela_t *rela, + const elf32_sym_t *sym, uint32_t addr) +{ + uint32_t val; + uint32_t *where; + + assert(elf && rela); + + where = (uint32_t *)esp_elf_map_sym(elf, rela->offset); + + ESP_LOGD(TAG, "where=%p addr=%x offset=%x\n", where, (int)addr, (int)rela->offset); + + switch (ELF_R_TYPE(rela->info)) { + case R_XTENSA_RELATIVE: + val = esp_elf_map_sym(elf, *where); +#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET + *where = elf_remap_text(elf, val); +#else + *where = val; +#endif + break; + case R_XTENSA_RTLD: + break; + case R_XTENSA_GLOB_DAT: + case R_XTENSA_JMP_SLOT: +#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET + *where = elf_remap_text(elf, addr); +#else + *where = addr; +#endif + break; + default: + ESP_LOGE(TAG, "info=%d is not supported", ELF_R_TYPE(rela->info)); + return -EINVAL; + } + + return 0; +} diff --git a/Libraries/elf_loader/src/esp_elf.c b/Libraries/elf_loader/src/esp_elf.c new file mode 100644 index 00000000..6ced644b --- /dev/null +++ b/Libraries/elf_loader/src/esp_elf.c @@ -0,0 +1,459 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "esp_log.h" + +#include "elf_symbol.h" +#include "private/elf_platform.h" + +#define stype(_s, _t) ((_s)->type == (_t)) +#define sflags(_s, _f) (((_s)->flags & (_f)) == (_f)) + +static const char *TAG = "ELF"; + +/** + * @brief Map symbol's address of ELF to physic space. + * + * @param elf - ELF object pointer + * @param sym - ELF symbol address + * + * @return ESP_OK if sucess or other if failed. + */ +uintptr_t esp_elf_map_sym(esp_elf_t *elf, uintptr_t sym) +{ + for (int i = 0; i < ELF_SECS; i++) { + if ((sym >= elf->sec[i].v_addr) && + (sym < (elf->sec[i].v_addr + elf->sec[i].size))) { + return sym - elf->sec[i].v_addr + elf->sec[i].addr; + } + } + + return 0; +} + +/** + * @brief Initialize ELF object. + * + * @param elf - ELF object pointer + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_init(esp_elf_t *elf) +{ + ESP_LOGI(TAG, "ELF loader version: %d.%d.%d", ELF_LOADER_VER_MAJOR, ELF_LOADER_VER_MINOR, ELF_LOADER_VER_PATCH); + + if (!elf) { + return -EINVAL; + } + + memset(elf, 0, sizeof(esp_elf_t)); + + return 0; +} + +/** + * @brief Decode and relocate ELF data. + * + * @param elf - ELF object pointer + * @param pbuf - ELF data buffer + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_relocate(esp_elf_t *elf, const uint8_t *pbuf) +{ + uint32_t entry; + uint32_t size; + + const elf32_hdr_t *phdr; + const elf32_shdr_t *pshdr; + const char *shstrab; + + if (!elf || !pbuf) { + return -EINVAL; + } + + phdr = (const elf32_hdr_t *)pbuf; + pshdr = (const elf32_shdr_t *)(pbuf + phdr->shoff); + shstrab = (const char *)pbuf + pshdr[phdr->shstrndx].offset; + + /* Calculate ELF image size */ + + for (uint32_t i = 0; i < phdr->shnum; i++) { + const char *name = shstrab + pshdr[i].name; + + if (stype(&pshdr[i], SHT_PROGBITS) && sflags(&pshdr[i], SHF_ALLOC)) { + if (sflags(&pshdr[i], SHF_EXECINSTR) && !strcmp(ELF_TEXT, name)) { + ESP_LOGD(TAG, ".text sec addr=0x%08x size=0x%08x offset=0x%08x", + pshdr[i].addr, pshdr[i].size, pshdr[i].offset); + + elf->sec[ELF_SEC_TEXT].v_addr = pshdr[i].addr; + elf->sec[ELF_SEC_TEXT].size = ELF_ALIGN(pshdr[i].size); + elf->sec[ELF_SEC_TEXT].offset = pshdr[i].offset; + + ESP_LOGD(TAG, ".text offset is 0x%lx size is 0x%x", + elf->sec[ELF_SEC_TEXT].offset, + elf->sec[ELF_SEC_TEXT].size); + } else if (sflags(&pshdr[i], SHF_WRITE) && !strcmp(ELF_DATA, name)) { + ESP_LOGD(TAG, ".data sec addr=0x%08x size=0x%08x offset=0x%08x", + pshdr[i].addr, pshdr[i].size, pshdr[i].offset); + + elf->sec[ELF_SEC_DATA].v_addr = pshdr[i].addr; + elf->sec[ELF_SEC_DATA].size = pshdr[i].size; + elf->sec[ELF_SEC_DATA].offset = pshdr[i].offset; + + ESP_LOGD(TAG, ".data offset is 0x%lx size is 0x%x", + elf->sec[ELF_SEC_DATA].offset, + elf->sec[ELF_SEC_DATA].size); + } else if (!strcmp(ELF_RODATA, name)) { + ESP_LOGD(TAG, ".rodata sec addr=0x%08x size=0x%08x offset=0x%08x", + pshdr[i].addr, pshdr[i].size, pshdr[i].offset); + + elf->sec[ELF_SEC_RODATA].v_addr = pshdr[i].addr; + elf->sec[ELF_SEC_RODATA].size = pshdr[i].size; + elf->sec[ELF_SEC_RODATA].offset = pshdr[i].offset; + + ESP_LOGD(TAG, ".rodata offset is 0x%lx size is 0x%x", + elf->sec[ELF_SEC_RODATA].offset, + elf->sec[ELF_SEC_RODATA].size); + } else if (!strcmp(ELF_DATA_REL_RO, name)) { + ESP_LOGD(TAG, ".data.rel.ro sec addr=0x%08x size=0x%08x offset=0x%08x", + pshdr[i].addr, pshdr[i].size, pshdr[i].offset); + + elf->sec[ELF_SEC_DRLRO].v_addr = pshdr[i].addr; + elf->sec[ELF_SEC_DRLRO].size = pshdr[i].size; + elf->sec[ELF_SEC_DRLRO].offset = pshdr[i].offset; + + ESP_LOGD(TAG, ".data.rel.ro offset is 0x%lx size is 0x%x", + elf->sec[ELF_SEC_DRLRO].offset, + elf->sec[ELF_SEC_DRLRO].size); + } + } else if (stype(&pshdr[i], SHT_NOBITS) && + sflags(&pshdr[i], SHF_ALLOC | SHF_WRITE) && + !strcmp(ELF_BSS, name)) { + ESP_LOGD(TAG, ".bss sec addr=0x%08x size=0x%08x offset=0x%08x", + pshdr[i].addr, pshdr[i].size, pshdr[i].offset); + + elf->sec[ELF_SEC_BSS].v_addr = pshdr[i].addr; + elf->sec[ELF_SEC_BSS].size = pshdr[i].size; + elf->sec[ELF_SEC_BSS].offset = pshdr[i].offset; + + ESP_LOGD(TAG, ".bss offset is 0x%lx size is 0x%x", + elf->sec[ELF_SEC_BSS].offset, + elf->sec[ELF_SEC_BSS].size); + } + } + + /* No .text on image */ + + if (!elf->sec[ELF_SEC_TEXT].size) { + return -EINVAL; + } + + elf->ptext = esp_elf_malloc(elf->sec[ELF_SEC_TEXT].size, true); + if (!elf->ptext) { + return -ENOMEM; + } + + size = elf->sec[ELF_SEC_DATA].size + + elf->sec[ELF_SEC_RODATA].size + + elf->sec[ELF_SEC_BSS].size + + elf->sec[ELF_SEC_DRLRO].size; + if (size) { + elf->pdata = esp_elf_malloc(size, false); + if (!elf->pdata) { + esp_elf_free(elf->ptext); + return -ENOMEM; + } + } + + /* Dump ".text" from ELF to excutable space memory */ + + elf->sec[ELF_SEC_TEXT].addr = (Elf32_Addr)elf->ptext; + memcpy(elf->ptext, pbuf + elf->sec[ELF_SEC_TEXT].offset, + elf->sec[ELF_SEC_TEXT].size); + +#ifdef CONFIG_ELF_LOADER_SET_MMU + if (esp_elf_arch_init_mmu(elf)) { + esp_elf_free(elf->ptext); + return -EIO; + } +#endif + + /** + * Dump ".data", ".rodata" and ".bss" from ELF to R/W space memory. + * + * Todo: Dump ".rodata" to rodata section by MMU/MPU. + */ + + if (size) { + uint8_t *pdata = elf->pdata; + + if (elf->sec[ELF_SEC_DATA].size) { + elf->sec[ELF_SEC_DATA].addr = (uint32_t)pdata; + + memcpy(pdata, pbuf + elf->sec[ELF_SEC_DATA].offset, + elf->sec[ELF_SEC_DATA].size); + + pdata += elf->sec[ELF_SEC_DATA].size; + } + + if (elf->sec[ELF_SEC_RODATA].size) { + elf->sec[ELF_SEC_RODATA].addr = (uint32_t)pdata; + + memcpy(pdata, pbuf + elf->sec[ELF_SEC_RODATA].offset, + elf->sec[ELF_SEC_RODATA].size); + + pdata += elf->sec[ELF_SEC_RODATA].size; + } + + if (elf->sec[ELF_SEC_DRLRO].size) { + elf->sec[ELF_SEC_DRLRO].addr = (uint32_t)pdata; + + memcpy(pdata, pbuf + elf->sec[ELF_SEC_DRLRO].offset, + elf->sec[ELF_SEC_DRLRO].size); + + pdata += elf->sec[ELF_SEC_DRLRO].size; + } + + if (elf->sec[ELF_SEC_BSS].size) { + elf->sec[ELF_SEC_BSS].addr = (uint32_t)pdata; + memset(pdata, 0, elf->sec[ELF_SEC_BSS].size); + } + } + + /* Set ELF entry */ + + entry = phdr->entry + elf->sec[ELF_SEC_TEXT].addr - + elf->sec[ELF_SEC_TEXT].v_addr; + +#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET + elf->entry = (void *)elf_remap_text(elf, (uintptr_t)entry); +#else + elf->entry = (void *)entry; +#endif + + /* Relocation section data */ + + for (uint32_t i = 0; i < phdr->shnum; i++) { + if (stype(&pshdr[i], SHT_RELA)) { + uint32_t nr_reloc; + const elf32_rela_t *rela; + const elf32_sym_t *symtab; + const char *strtab; + + nr_reloc = pshdr[i].size / sizeof(elf32_rela_t); + rela = (const elf32_rela_t *)(pbuf + pshdr[i].offset); + symtab = (const elf32_sym_t *)(pbuf + pshdr[pshdr[i].link].offset); + strtab = (const char *)(pbuf + pshdr[pshdr[pshdr[i].link].link].offset); + + ESP_LOGD(TAG, "Section %s has %d symbol tables", shstrab + pshdr[i].name, (int)nr_reloc); + + for (int i = 0; i < nr_reloc; i++) { + int type; + uintptr_t addr = 0; + elf32_rela_t rela_buf; + + memcpy(&rela_buf, &rela[i], sizeof(elf32_rela_t)); + + const elf32_sym_t *sym = &symtab[ELF_R_SYM(rela_buf.info)]; + + type = ELF_R_TYPE(rela->info); + if (type == STT_COMMON) { + const char *comm_name = strtab + sym->name; + + if (comm_name[0]) { + addr = elf_find_sym(comm_name); + + if (!addr) { + ESP_LOGE(TAG, "Can't find common %s", strtab + sym->name); + esp_elf_free(elf->pdata); + esp_elf_free(elf->ptext); + return -ENOSYS; + } + + ESP_LOGD(TAG, "Find common %s addr=%x", comm_name, addr); + } + } else if (type == STT_FILE) { + const char *func_name = strtab + sym->name; + + if (sym->value) { + addr = esp_elf_map_sym(elf, sym->value); + } else { + addr = elf_find_sym(func_name); + } + + if (!addr) { + ESP_LOGE(TAG, "Can't find symbol %s", func_name); + esp_elf_free(elf->pdata); + esp_elf_free(elf->ptext); + return -ENOSYS; + } + + ESP_LOGD(TAG, "Find function %s addr=%x", func_name, addr); + } + + esp_elf_arch_relocate(elf, &rela_buf, sym, addr); + } + } + } + +#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM + esp_elf_arch_flush(); +#endif + + return 0; +} + +/** + * @brief Request running relocated ELF function. + * + * @param elf - ELF object pointer + * @param opt - Request options + * @param argc - Arguments number + * @param argv - Arguments value array + * + * @return ESP_OK if sucess or other if failed. + */ +int esp_elf_request(esp_elf_t *elf, int opt, int argc, char *argv[]) +{ + elf->entry(argc, argv); + + return 0; +} + +/** + * @brief Deinitialize ELF object. + * + * @param elf - ELF object pointer + * + * @return None + */ +void esp_elf_deinit(esp_elf_t *elf) +{ + if (elf->pdata) { + esp_elf_free(elf->pdata); + elf->pdata = NULL; + } + + if (elf->ptext) { + esp_elf_free(elf->ptext); + elf->ptext = NULL; + } + +#ifdef CONFIG_ELF_LOADER_SET_MMU + esp_elf_arch_deinit_mmu(elf); +#endif +} + +/** + * @brief Print header description information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_hdr(const uint8_t *pbuf) +{ + const char *s_bits, *s_endian; + const elf32_hdr_t *hdr = (const elf32_hdr_t *)pbuf; + + switch (hdr->ident[4]) { + case 1: + s_bits = "32-bit"; + break; + case 2: + s_bits = "64-bit"; + break; + default: + s_bits = "invalid bits"; + break; + } + + switch (hdr->ident[5]) { + case 1: + s_endian = "little-endian"; + break; + case 2: + s_endian = "big-endian"; + break; + default: + s_endian = "invalid endian"; + break; + } + + if (hdr->ident[0] == 0x7f) { + ESP_LOGI(TAG, "%-40s %c%c%c", "Class:", hdr->ident[1], hdr->ident[2], hdr->ident[3]); + } + + ESP_LOGI(TAG, "%-40s %s, %s", "Format:", s_bits, s_endian); + ESP_LOGI(TAG, "%-40s %x", "Version(current):", hdr->ident[6]); + + ESP_LOGI(TAG, "%-40s %d", "Type:", hdr->type); + ESP_LOGI(TAG, "%-40s %d", "Machine:", hdr->machine); + ESP_LOGI(TAG, "%-40s %x", "Version:", hdr->version); + ESP_LOGI(TAG, "%-40s %x", "Entry point address:", hdr->entry); + ESP_LOGI(TAG, "%-40s %x", "Start of program headers:", hdr->phoff); + ESP_LOGI(TAG, "%-40s %d", "Start of section headers:", hdr->shoff); + ESP_LOGI(TAG, "%-40s 0x%x", "Flags:", hdr->flags); + ESP_LOGI(TAG, "%-40s %d", "Size of this header(bytes):", hdr->ehsize); + ESP_LOGI(TAG, "%-40s %d", "Size of program headers(bytes):", hdr->phentsize); + ESP_LOGI(TAG, "%-40s %d", "Number of program headers:", hdr->phnum); + ESP_LOGI(TAG, "%-40s %d", "Size of section headers(bytes):", hdr->shentsize); + ESP_LOGI(TAG, "%-40s %d", "Number of section headers:", hdr->shnum); + ESP_LOGI(TAG, "%-40s %d", "Section header string table i:", hdr->shstrndx); +} + +/** + * @brief Print section header description information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_shdr(const uint8_t *pbuf) +{ + const elf32_hdr_t *phdr = (const elf32_hdr_t *)pbuf; + const elf32_shdr_t *pshdr = (const elf32_shdr_t *)((size_t)pbuf + phdr->shoff); + + for (int i = 0; i < phdr->shnum; i++) { + ESP_LOGI(TAG, "%-40s %d", "name:", pshdr->name); + ESP_LOGI(TAG, "%-40s %d", "type:", pshdr->type); + ESP_LOGI(TAG, "%-40s 0x%x", "flags:", pshdr->flags); + ESP_LOGI(TAG, "%-40s %x", "addr", pshdr->addr); + ESP_LOGI(TAG, "%-40s %x", "offset:", pshdr->offset); + ESP_LOGI(TAG, "%-40s %d", "size", pshdr->size); + ESP_LOGI(TAG, "%-40s 0x%x", "link", pshdr->link); + ESP_LOGI(TAG, "%-40s %d", "addralign", pshdr->addralign); + ESP_LOGI(TAG, "%-40s %d", "entsize", pshdr->entsize); + + pshdr = (const elf32_shdr_t *)((size_t)pshdr + sizeof(elf32_shdr_t)); + } +} + +/** + * @brief Print section information of ELF. + * + * @param pbuf - ELF data buffer + * + * @return None + */ +void esp_elf_print_sec(esp_elf_t *elf) +{ + const char *sec_names[ELF_SECS] = { + "text", "bss", "data", "rodata" + }; + + for (int i = 0; i < ELF_SECS; i++) { + ESP_LOGI(TAG, "%s: 0x%08x size 0x%08x", sec_names[i], elf->sec[i].addr, elf->sec[i].size); + } + + ESP_LOGI(TAG, "entry: %p", elf->entry); +} diff --git a/Libraries/elf_loader/src/esp_elf_adapter.c b/Libraries/elf_loader/src/esp_elf_adapter.c new file mode 100644 index 00000000..93abf54a --- /dev/null +++ b/Libraries/elf_loader/src/esp_elf_adapter.c @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_idf_version.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "soc/soc.h" +#include "private/elf_platform.h" + +#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM +#ifdef CONFIG_IDF_TARGET_ESP32S3 +#define OFFSET_TEXT_VALUE (SOC_IROM_LOW - SOC_DROM_LOW) +#endif +#endif + +/** + * @brief Allocate block of memory. + * + * @param n - Memory size in byte + * @param exec - True: memory can run executable code; False: memory can R/W data + * + * @return Memory pointer if success or NULL if failed. + */ +void *esp_elf_malloc(uint32_t n, bool exec) +{ +#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM + uint32_t caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; +#else + uint32_t caps = exec ? MALLOC_CAP_EXEC : MALLOC_CAP_8BIT; +#endif + + return heap_caps_malloc(n, caps); +} + +/** + * @brief Free block of memory. + * + * @param ptr - memory block pointer allocated by "esp_elf_malloc" + * + * @return None + */ +void esp_elf_free(void *ptr) +{ + heap_caps_free(ptr); +} + +/** + * @brief Remap symbol from ".data" to ".text" section. + * + * @param elf - ELF object pointer + * @param sym - ELF symbol table + * + * @return Remapped symbol value + */ +#ifdef CONFIG_ELF_LOADER_CACHE_OFFSET +uintptr_t elf_remap_text(esp_elf_t *elf, uintptr_t sym) +{ + uintptr_t mapped_sym; + esp_elf_sec_t *sec = &elf->sec[ELF_SEC_TEXT]; + + if ((sym >= sec->addr) && + (sym < (sec->addr + sec->size))) { +#ifdef CONFIG_ELF_LOADER_SET_MMU + mapped_sym = sym + elf->text_off; +#else + mapped_sym = sym + OFFSET_TEXT_VALUE; +#endif + } else { + mapped_sym = sym; + } + + return mapped_sym; +} +#endif + +/** + * @brief Flush data from cache to external RAM. + * + * @param None + * + * @return None + */ +#ifdef CONFIG_ELF_LOADER_LOAD_PSRAM +void IRAM_ATTR esp_elf_arch_flush(void) +{ + extern void spi_flash_disable_interrupts_caches_and_other_cpu(void); + extern void spi_flash_enable_interrupts_caches_and_other_cpu(void); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + extern void Cache_WriteBack_All(void); + + Cache_WriteBack_All(); +#else + void esp_spiram_writeback_cache(void); + + esp_spiram_writeback_cache(); +#endif + + spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_enable_interrupts_caches_and_other_cpu(); +} +#endif + diff --git a/Libraries/elf_loader/src/esp_elf_symbol.c b/Libraries/elf_loader/src/esp_elf_symbol.c new file mode 100644 index 00000000..0a3cf158 --- /dev/null +++ b/Libraries/elf_loader/src/esp_elf_symbol.c @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rom/ets_sys.h" + +#include "elf_symbol.h" + + +extern int __ltdf2 (double a, double b); +extern unsigned int __fixunsdfsi (double a); +extern int __gtdf2 (double a, double b); +extern double __floatunsidf (unsigned int i); +extern double __divdf3 (double a, double b); + +/** @brief Libc public functions symbols look-up table */ + +static const struct esp_elfsym g_esp_libc_elfsyms[] = { + + /* string.h */ + + ESP_ELFSYM_EXPORT(strerror), + ESP_ELFSYM_EXPORT(memset), + ESP_ELFSYM_EXPORT(memcpy), + ESP_ELFSYM_EXPORT(strlen), + ESP_ELFSYM_EXPORT(strtod), + ESP_ELFSYM_EXPORT(strrchr), + ESP_ELFSYM_EXPORT(strchr), + ESP_ELFSYM_EXPORT(strcmp), + ESP_ELFSYM_EXPORT(strtol), + ESP_ELFSYM_EXPORT(strcspn), + ESP_ELFSYM_EXPORT(strncat), + + /* stdio.h */ + + ESP_ELFSYM_EXPORT(puts), + ESP_ELFSYM_EXPORT(putchar), + ESP_ELFSYM_EXPORT(fputc), + ESP_ELFSYM_EXPORT(fputs), + ESP_ELFSYM_EXPORT(printf), + ESP_ELFSYM_EXPORT(vfprintf), + ESP_ELFSYM_EXPORT(fprintf), + ESP_ELFSYM_EXPORT(fwrite), + + /* unistd.h */ + + ESP_ELFSYM_EXPORT(usleep), + ESP_ELFSYM_EXPORT(sleep), + ESP_ELFSYM_EXPORT(exit), + ESP_ELFSYM_EXPORT(close), + + /* stdlib.h */ + + ESP_ELFSYM_EXPORT(malloc), + ESP_ELFSYM_EXPORT(calloc), + ESP_ELFSYM_EXPORT(realloc), + ESP_ELFSYM_EXPORT(free), + + /* time.h */ + + ESP_ELFSYM_EXPORT(clock_gettime), + ESP_ELFSYM_EXPORT(strftime), + + /* pthread.h */ + + ESP_ELFSYM_EXPORT(pthread_create), + ESP_ELFSYM_EXPORT(pthread_attr_init), + ESP_ELFSYM_EXPORT(pthread_attr_setstacksize), + ESP_ELFSYM_EXPORT(pthread_detach), + ESP_ELFSYM_EXPORT(pthread_join), + ESP_ELFSYM_EXPORT(pthread_exit), + + /* newlib */ + + ESP_ELFSYM_EXPORT(__errno), + ESP_ELFSYM_EXPORT(__getreent), +#ifdef __HAVE_LOCALE_INFO__ + ESP_ELFSYM_EXPORT(__locale_ctype_ptr), +#else + ESP_ELFSYM_EXPORT(_ctype_), +#endif + + /* math */ + + ESP_ELFSYM_EXPORT(__ltdf2), + ESP_ELFSYM_EXPORT(__fixunsdfsi), + ESP_ELFSYM_EXPORT(__gtdf2), + ESP_ELFSYM_EXPORT(__floatunsidf), + ESP_ELFSYM_EXPORT(__divdf3), + + /* getopt.h */ + + ESP_ELFSYM_EXPORT(getopt_long), + ESP_ELFSYM_EXPORT(optind), + ESP_ELFSYM_EXPORT(opterr), + ESP_ELFSYM_EXPORT(optarg), + ESP_ELFSYM_EXPORT(optopt), + + /* setjmp.h */ + + ESP_ELFSYM_EXPORT(longjmp), + ESP_ELFSYM_EXPORT(setjmp), + + ESP_ELFSYM_END +}; + +/** @brief ESP-IDF public functions symbols look-up table */ + +static const struct esp_elfsym g_esp_espidf_elfsyms[] = { + + /* sys/socket.h */ + + ESP_ELFSYM_EXPORT(lwip_bind), + ESP_ELFSYM_EXPORT(lwip_setsockopt), + ESP_ELFSYM_EXPORT(lwip_socket), + ESP_ELFSYM_EXPORT(lwip_listen), + ESP_ELFSYM_EXPORT(lwip_accept), + ESP_ELFSYM_EXPORT(lwip_recv), + ESP_ELFSYM_EXPORT(lwip_recvfrom), + ESP_ELFSYM_EXPORT(lwip_send), + ESP_ELFSYM_EXPORT(lwip_sendto), + ESP_ELFSYM_EXPORT(lwip_connect), + + /* arpa/inet.h */ + + ESP_ELFSYM_EXPORT(ipaddr_addr), + ESP_ELFSYM_EXPORT(lwip_htons), + ESP_ELFSYM_EXPORT(lwip_htonl), + ESP_ELFSYM_EXPORT(ip4addr_ntoa), + + /* ROM functions */ + + ESP_ELFSYM_EXPORT(ets_printf), + + ESP_ELFSYM_END +}; + + +#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS +static const struct esp_elfsym* custom_symbols = NULL; +void elf_set_custom_symbols(const struct esp_elfsym* symbols) { + custom_symbols = symbols; +} +#endif + +/** + * @brief Find symbol address by name. + * + * @param sym_name - Symbol name + * + * @return Symbol address if success or 0 if failed. + */ +uintptr_t elf_find_sym(const char *sym_name) +{ + const struct esp_elfsym *syms; + +#ifdef CONFIG_ELF_LOADER_LIBC_SYMBOLS + syms = g_esp_libc_elfsyms; + while (syms->name) { + if (!strcmp(syms->name, sym_name)) { + return (uintptr_t)syms->sym; + } + + syms++; + } +#else + syms = g_esp_libc_elfsyms; + (void)syms; +#endif + +#ifdef CONFIG_ELF_LOADER_ESPIDF_SYMBOLS + syms = g_esp_espidf_elfsyms; + while (syms->name) { + if (!strcmp(syms->name, sym_name)) { + return (uintptr_t)syms->sym; + } + + syms++; + } +#else + syms = g_esp_espidf_elfsyms; + (void)syms; +#endif + +#ifdef CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS + syms = custom_symbols; + if (syms != NULL) { + while (syms->name) { + if (!strcmp(syms->name, sym_name)) { + return (uintptr_t)syms->sym; + } + + syms++; + } + } + +#endif + + return 0; +} + diff --git a/Libraries/elf_loader/src/soc/esp_elf_esp32s2.c b/Libraries/elf_loader/src/soc/esp_elf_esp32s2.c new file mode 100644 index 00000000..b3e18eb2 --- /dev/null +++ b/Libraries/elf_loader/src/soc/esp_elf_esp32s2.c @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_idf_version.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "esp32s2/rom/cache.h" +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +#include "esp32s2/spiram.h" +#endif +#include "soc/mmu.h" +#include "private/elf_platform.h" + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#define PSRAM_VADDR_START 0x3f800000 +#else +#define PSRAM_VADDR_START (DRAM0_CACHE_ADDRESS_HIGH - esp_spiram_get_size()) +#endif + +#define MMU_INVALID BIT(14) +#define MMU_UNIT_SIZE 0x10000 +#define MMU_REG ((volatile uint32_t *)DR_REG_MMU_TABLE) + +#define ESP_MMU_IBUS_BASE IRAM0_ADDRESS_LOW +#define ESP_MMU_IBUS_MAX ((IRAM0_ADDRESS_HIGH - IRAM0_ADDRESS_LOW) / \ + MMU_UNIT_SIZE) +#define ESP_MMU_IBUS_OFF (8) + +#define DADDR_2_SECS(v) ((v) / MMU_UNIT_SIZE) +#define IADDR_2_SECS(v) ((v) / MMU_UNIT_SIZE) + +#define PSRAM_OFF(v) ((v) - PSRAM_VADDR_START) +#define PSRAM_SECS(v) DADDR_2_SECS(PSRAM_OFF((uintptr_t)(v))) +#define PSRAM_ALIGN(v) ((uintptr_t)(v) & (~(MMU_UNIT_SIZE - 1))) + +#define ICACHE_ADDR(s) (ESP_MMU_IBUS_BASE + (s) * MMU_UNIT_SIZE) + +extern void spi_flash_disable_interrupts_caches_and_other_cpu(void); +extern void spi_flash_enable_interrupts_caches_and_other_cpu(void); + +/** + * @brief Initialize MMU hardware remapping function. + * + * @param elf - ELF object pointer + * + * @return 0 if success or a negative value if failed. + */ +int IRAM_ATTR esp_elf_arch_init_mmu(esp_elf_t *elf) +{ + int ret; + int off; + uint32_t ibus_secs; + volatile uint32_t dbus_secs; + esp_elf_sec_t *sec = &elf->sec[ELF_SEC_TEXT]; + volatile uint32_t *p = MMU_REG; + + if (sec->size % MMU_UNIT_SIZE) { + ibus_secs = IADDR_2_SECS(sec->size) + 1; + } else { + ibus_secs = IADDR_2_SECS(sec->size); + } + + dbus_secs = PSRAM_SECS(elf->ptext); + + spi_flash_disable_interrupts_caches_and_other_cpu(); + + for (off = ESP_MMU_IBUS_OFF; off < ESP_MMU_IBUS_MAX; off++) { + if (p[off] == MMU_INVALID) { + int j; + + for (j = 1; j < ibus_secs; j++) { + if (p[off + j] != MMU_INVALID) { + break; + } + } + + if (j >= ibus_secs) { + for (int k = 0; k < ibus_secs; k++) { + p [off + k] = MMU_ACCESS_SPIRAM | (dbus_secs + k); + } + + break; + } + } + } + + spi_flash_enable_interrupts_caches_and_other_cpu(); + + if (off >= ESP_MMU_IBUS_MAX) { + ret = -EIO; + } else { + elf->mmu_off = off; + elf->mmu_num = ibus_secs; + elf->text_off = ICACHE_ADDR(off) - PSRAM_ALIGN(elf->ptext); + + ret = 0; + } + + return ret; +} + +/** + * @brief De-initialize MMU hardware remapping function. + * + * @param elf - ELF object pointer + * + * @return None + */ +void IRAM_ATTR esp_elf_arch_deinit_mmu(esp_elf_t *elf) +{ + volatile int num = elf->mmu_num; + volatile int off = elf->mmu_off; + volatile uint32_t *p = MMU_REG; + + spi_flash_disable_interrupts_caches_and_other_cpu(); + + for (int i = 0; i < num; i++) { + p [off + i] = MMU_INVALID; + } + + spi_flash_enable_interrupts_caches_and_other_cpu(); +} diff --git a/Libraries/lv_screenshot/CMakeLists.txt b/Libraries/lv_screenshot/CMakeLists.txt index 3daaa702..f574d226 100644 --- a/Libraries/lv_screenshot/CMakeLists.txt +++ b/Libraries/lv_screenshot/CMakeLists.txt @@ -4,26 +4,32 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -file(GLOB SOURCES "src/*.c*") -file(GLOB HEADERS "src/*.h*") - -add_library(lv_screenshot STATIC) - -target_sources(lv_screenshot - PRIVATE ${SOURCES} - PUBLIC ${HEADERS} -) - -target_include_directories(lv_screenshot - PRIVATE private - PUBLIC src -) - if (DEFINED ENV{ESP_IDF_VERSION}) - target_link_libraries(lv_screenshot - PUBLIC idf::lvgl + file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + + idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "src/" + REQUIRES lvgl ) + + add_definitions(-DESP_PLATFORM) else() + file(GLOB SOURCES "src/*.c*") + file(GLOB HEADERS "src/*.h*") + + add_library(lv_screenshot STATIC) + + target_sources(lv_screenshot + PRIVATE ${SOURCES} + PUBLIC ${HEADERS} + ) + + target_include_directories(lv_screenshot + PRIVATE private + PUBLIC src + ) + target_link_libraries(lv_screenshot PUBLIC lvgl ) diff --git a/README.md b/README.md index 45dcc04a..edfea1a5 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,10 @@ There are also built-in apps: Play with the built-in apps or build your own! Use one of the supported devices or set up the drivers for your own hardware platform. Noteworthy features: -- Touch UI capabilities (via LVGL) with support for input devices such as on-device trackball or keyboard. -- An application platform that can run apps and services. -- Basic applications to boost productivity, such as a Wi-Fi connectivity and I2C apps. +- Touch UI capabilities (via LVGL) with support for input devices such as keyboard, trackable and more. +- Run apps and services. +- Run external apps from SD card. (limited APIs available, for now) +- Basic applications to boost productivity, such as a Wi-Fi connectivity and I2C. - Includes a PC simulator build target to speed up development. Requirements: @@ -122,6 +123,7 @@ Directories explained: - `App`: The application/firmware example project - `Boards`: Contains board configuration projects with drivers (and simulator) - `Tactility`: The main application platform code +- `TactilityC`: C wrappers for making side-loaded apps (C++ is not supported yet) - `TactilityHeadless`: Service framework and default services. - `TactilityCore`: Core functionality regarding threads, stdlib, etc. - `Libraries`: Contains a mix of regular libraries and ESP modules diff --git a/Tactility/CMakeLists.txt b/Tactility/CMakeLists.txt index 2d467257..433aa9dd 100644 --- a/Tactility/CMakeLists.txt +++ b/Tactility/CMakeLists.txt @@ -10,11 +10,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source/" PRIV_INCLUDE_DIRS "Private/" - REQUIRES TactilityHeadless lvgl driver - ) - - target_link_libraries(${COMPONENT_LIB} - PUBLIC lv_screenshot + REQUIRES TactilityHeadless lvgl driver elf_loader lv_screenshot ) add_definitions(-DESP_PLATFORM) diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index b3fd019f..d1f69763 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -53,8 +53,11 @@ namespace app { namespace wifiapsettings { extern const AppManifest manifest; } namespace wificonnect { extern const AppManifest manifest; } namespace wifimanage { extern const AppManifest manifest; } + + extern const AppManifest elfWrapperManifest; } + #ifndef ESP_PLATFORM extern const app::AppManifest screenshot_app; #endif @@ -77,6 +80,8 @@ static const std::vector system_apps = { &app::wifimanage::manifest, #ifndef ESP_PLATFORM &app::screenshot::manifest, // Screenshots don't work yet on ESP32 +#else + &app::elfWrapperManifest, // For hot-loading ELF apps #endif }; diff --git a/Tactility/Source/app/AppManifest.h b/Tactility/Source/app/AppManifest.h index d0095437..87ba2046 100644 --- a/Tactility/Source/app/AppManifest.h +++ b/Tactility/Source/app/AppManifest.h @@ -57,32 +57,32 @@ struct AppManifest { /** * App type affects launch behaviour. */ - const Type type = TypeUser; + Type type = TypeUser; /** * Non-blocking method to call when app is started. */ - const AppOnStart onStart = nullptr; + AppOnStart onStart = nullptr; /** * Non-blocking method to call when app is stopped. */ - const AppOnStop _Nullable onStop = nullptr; + AppOnStop _Nullable onStop = nullptr; /** * Non-blocking method to create the GUI */ - const AppOnShow _Nullable onShow = nullptr; + AppOnShow _Nullable onShow = nullptr; /** * Non-blocking method, called before gui is destroyed */ - const AppOnHide _Nullable onHide = nullptr; + AppOnHide _Nullable onHide = nullptr; /** * Handle the result for apps that are launched */ - const AppOnResult _Nullable onResult = nullptr; + AppOnResult _Nullable onResult = nullptr; }; struct { diff --git a/Tactility/Source/app/ElfApp.cpp b/Tactility/Source/app/ElfApp.cpp new file mode 100644 index 00000000..0bf63a90 --- /dev/null +++ b/Tactility/Source/app/ElfApp.cpp @@ -0,0 +1,126 @@ +#ifdef ESP_PLATFORM + +#include "file/File.h" +#include "ElfApp.h" +#include "TactilityCore.h" +#include "esp_elf.h" + +#include "service/loader/Loader.h" + +namespace tt::app { + +#define TAG "elf_app" +#define ELF_WRAPPER_APP_ID "ElfWrapper" + +static size_t elfManifestSetCount = 0; +std::unique_ptr elfFileData; +esp_elf_t elf; + +bool startElfApp(const char* filePath) { + TT_LOG_I(TAG, "Starting ELF %s", filePath); + + assert(elfFileData == nullptr); + + size_t size = 0; + elfFileData = file::readBinary(filePath, size); + if (elfFileData == nullptr) { + return false; + } + + if (esp_elf_init(&elf) < 0) { + TT_LOG_E(TAG, "Failed to initialize"); + return false; + } + + if (esp_elf_relocate(&elf, elfFileData.get()) < 0) { + TT_LOG_E(TAG, "Failed to load executable"); + return false; + } + + int argc = 0; + char* argv[] = {}; + + size_t manifest_set_count = elfManifestSetCount; + if (esp_elf_request(&elf, 0, argc, argv) < 0) { + TT_LOG_W(TAG, "Executable returned error code"); + return false; + } + + if (elfManifestSetCount > manifest_set_count) { + service::loader::startApp(ELF_WRAPPER_APP_ID); + } else { + TT_LOG_W(TAG, "App did not set manifest to run - cleaning up ELF"); + esp_elf_deinit(&elf); + elfFileData = nullptr; + } + + return true; +} + +static void onStart(AppContext& app) {} +static void onStop(AppContext& app) {} +static void onShow(AppContext& app, lv_obj_t* parent) {} +static void onHide(AppContext& app) {} +static void onResult(AppContext& app, Result result, const Bundle& resultBundle) {} + +AppManifest elfManifest = { + .id = "", + .name = "", + .type = TypeHidden, + .onStart = onStart, + .onStop = onStop, + .onShow = onShow, + .onHide = onHide, + .onResult = onResult +}; + +static void onStartWrapper(AppContext& app) { + elfManifest.onStart(app); +} + +static void onStopWrapper(AppContext& app) { + elfManifest.onStop(app); + TT_LOG_I(TAG, "Cleaning up ELF"); + esp_elf_deinit(&elf); + elfFileData = nullptr; +} + +static void onShowWrapper(AppContext& app, lv_obj_t* parent) { + elfManifest.onShow(app, parent); +} + +static void onHideWrapper(AppContext& app) { + elfManifest.onHide(app); +} + +static void onResultWrapper(AppContext& app, Result result, const Bundle& bundle) { + elfManifest.onResult(app, result, bundle); +} + +AppManifest elfWrapperManifest = { + .id = ELF_WRAPPER_APP_ID, + .name = "ELF Wrapper", + .type = TypeHidden, + .onStart = onStartWrapper, + .onStop = onStopWrapper, + .onShow = onShowWrapper, + .onHide = onHideWrapper, + .onResult = onResultWrapper +}; + +void setElfAppManifest(const AppManifest& manifest) { + elfManifest.id = manifest.id; + elfManifest.name = manifest.name; + elfWrapperManifest.name = manifest.name; + elfManifest.onStart = manifest.onStart; + elfManifest.onStop = manifest.onStop; + elfManifest.onShow = manifest.onShow; + elfManifest.onHide = manifest.onHide; + elfManifest.onResult = manifest.onResult; + + elfManifestSetCount++; +} + +} // namespace + +#endif // ESP_PLATFORM \ No newline at end of file diff --git a/Tactility/Source/app/ElfApp.h b/Tactility/Source/app/ElfApp.h new file mode 100644 index 00000000..3a3a3a42 --- /dev/null +++ b/Tactility/Source/app/ElfApp.h @@ -0,0 +1,16 @@ +#pragma once + +#include "AppManifest.h" + +#ifdef ESP_PLATFORM + +namespace tt::app { + +bool startElfApp(const char* filePath); + +void setElfAppManifest(const AppManifest& manifest); + +} + + +#endif // ESP_PLATFORM diff --git a/Tactility/Source/app/files/Files.cpp b/Tactility/Source/app/files/Files.cpp index 4412f1bf..6e0e3447 100644 --- a/Tactility/Source/app/files/Files.cpp +++ b/Tactility/Source/app/files/Files.cpp @@ -1,15 +1,17 @@ #include "FilesData.h" -#include "Tactility.h" #include "app/AppContext.h" +#include "Tactility.h" #include "Assets.h" #include "Check.h" #include "FileUtils.h" #include "StringUtils.h" +#include "app/ElfApp.h" #include "app/imageviewer/ImageViewer.h" #include "app/textviewer/TextViewer.h" #include "lvgl.h" #include "service/loader/Loader.h" #include "lvgl/Toolbar.h" + #include #include #include @@ -52,6 +54,15 @@ static bool hasFileExtension(const char* path, const char* extension) { return true; } +static bool isSupportedExecutableFile(const char* filename) { +#ifdef ESP_PLATFORM + // Currently only the PNG library is built into Tactility + return hasFileExtension(filename, ".elf"); +#else + return false; +#endif +} + static bool isSupportedImageFile(const char* filename) { // Currently only the PNG library is built into Tactility return hasFileExtension(filename, ".png"); @@ -120,7 +131,11 @@ static void viewFile(const char* path, const char* filename) { TT_LOG_I(TAG, "Clicked %s", filepath); - if (isSupportedImageFile(filename)) { + if (isSupportedExecutableFile(filename)) { +#ifdef ESP_PLATFORM + app::startElfApp(processed_filepath); +#endif + } else if (isSupportedImageFile(filename)) { auto bundle = std::make_shared(); bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); service::loader::startApp("ImageViewer", false, bundle); diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index 94ee4e6b..e06851df 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -109,3 +109,15 @@ extern const AppManifest manifest = { }; } +extern "C" { + +extern void tt_app_selectiondialog_start2(const char* title, int argc, const char* argv[]) { + std::vector list; + for (int i = 0; i < argc; i++) { + const char* item = argv[i]; + list.push_back(item); + } + tt::app::selectiondialog::start(title, list); +} + +} diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.h b/Tactility/Source/app/selectiondialog/SelectionDialog.h index 0bc384be..d81ddc36 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.h +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.h @@ -19,4 +19,4 @@ namespace tt::app::selectiondialog { /** App result data */ int32_t getResultIndex(const Bundle& bundle); -} \ No newline at end of file +} diff --git a/Tactility/Source/lvgl/LabelUtils.cpp b/Tactility/Source/lvgl/LabelUtils.cpp index 850f2f91..f56247c0 100644 --- a/Tactility/Source/lvgl/LabelUtils.cpp +++ b/Tactility/Source/lvgl/LabelUtils.cpp @@ -1,65 +1,13 @@ #include "LabelUtils.h" -#include "TactilityCore.h" +#include "file/File.h" namespace tt::lvgl { #define TAG "tt_lv_label" -static long file_get_size(FILE* file) { - long original_offset = ftell(file); - - if (fseek(file, 0, SEEK_END) != 0) { - TT_LOG_E(TAG, "fseek failed"); - return -1; - } - - long file_size = ftell(file); - if (file_size == -1) { - TT_LOG_E(TAG, "Could not get file length"); - return -1; - } - - if (fseek(file, original_offset, SEEK_SET) != 0) { - TT_LOG_E(TAG, "fseek Failed"); - return -1; - } - - return file_size; -} - -static char* str_alloc_from_file(const char* filepath) { - FILE* file = fopen(filepath, "rb"); - - if (file == nullptr) { - TT_LOG_E(TAG, "Failed to open %s", filepath); - return nullptr; - } - - long content_length = file_get_size(file); - - auto* text_buffer = static_cast(malloc(content_length + 1)); - if (text_buffer == nullptr) { - TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length); - return nullptr; - } - - int buffer; - uint32_t buffer_offset = 0; - text_buffer[0] = 0; - while ((buffer = fgetc(file)) != EOF && buffer_offset < content_length) { - text_buffer[buffer_offset] = (char)buffer; - buffer_offset++; - } - text_buffer[buffer_offset] = 0; - - fclose(file); - return text_buffer; -} - void label_set_text_file(lv_obj_t* label, const char* filepath) { - char* text = str_alloc_from_file(filepath); - lv_label_set_text(label, text); - free(text); + auto text = file::readString(filepath); + lv_label_set_text(label, (const char*)text.get()); } } // namespace diff --git a/TactilityC/CMakeLists.txt b/TactilityC/CMakeLists.txt new file mode 100644 index 00000000..47d555e8 --- /dev/null +++ b/TactilityC/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (DEFINED ENV{ESP_IDF_VERSION}) + file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + + idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "Source/" + REQUIRES Tactility TactilityCore TactilityHeadless lvgl elf_loader + ) + + add_definitions(-DESP_PLATFORM) +else() + file(GLOB_RECURSE SOURCES "Source/*.c**") + file(GLOB_RECURSE HEADERS "Source/*.h**") + + add_library(TactilityC OBJECT) + + target_sources(TactilityC + PRIVATE ${SOURCES} + PUBLIC ${HEADERS} + ) + + target_include_directories(TactilityC + PUBLIC Source/ + ) + + add_definitions(-D_Nullable=) + add_definitions(-D_Nonnull=) + target_link_libraries(TactilityC + PRIVATE Tactility + PRIVATE TactilityCore + PRIVATE TactilityHeadless + PRIVATE lvgl + ) +endif() + diff --git a/TactilityC/Source/TactilityC/TactilityC.cpp b/TactilityC/Source/TactilityC/TactilityC.cpp new file mode 100644 index 00000000..ccd3751a --- /dev/null +++ b/TactilityC/Source/TactilityC/TactilityC.cpp @@ -0,0 +1,50 @@ +#ifdef ESP_PLATFORM + +#include "elf_symbol.h" + +#include "app/App.h" +#include "app/SelectionDialog.h" +#include "lvgl/Toolbar.h" + +#include "lvgl.h" + +extern "C" { + +const struct esp_elfsym elf_symbols[] { + // Tactility + ESP_ELFSYM_EXPORT(tt_app_selectiondialog_start), + ESP_ELFSYM_EXPORT(tt_set_app_manifest), + ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create), + ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create_simple), + // lv_obj + ESP_ELFSYM_EXPORT(lv_obj_add_event_cb), + ESP_ELFSYM_EXPORT(lv_obj_align), + ESP_ELFSYM_EXPORT(lv_obj_align_to), + ESP_ELFSYM_EXPORT(lv_obj_get_user_data), + ESP_ELFSYM_EXPORT(lv_obj_set_user_data), + ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_all), + ESP_ELFSYM_EXPORT(lv_obj_set_style_pad_all), + ESP_ELFSYM_EXPORT(lv_obj_set_style_border_width), + ESP_ELFSYM_EXPORT(lv_obj_set_style_border_color), + // lv_button + ESP_ELFSYM_EXPORT(lv_button_create), + // lv_label + ESP_ELFSYM_EXPORT(lv_label_create), + ESP_ELFSYM_EXPORT(lv_label_set_text), + ESP_ELFSYM_EXPORT(lv_label_set_text_fmt), + ESP_ELFSYM_END +}; + +void tt_init_tactility_c() { + elf_set_custom_symbols(elf_symbols); +} + +#else // PC + +void tt_init_tactility_c() { +} + +#endif // ESP_PLATFORM + +} + diff --git a/TactilityC/Source/TactilityC/TactilityC.h b/TactilityC/Source/TactilityC/TactilityC.h new file mode 100644 index 00000000..319f9ecf --- /dev/null +++ b/TactilityC/Source/TactilityC/TactilityC.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void tt_init_tactility_c(); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityC/Source/TactilityC/app/App.cpp b/TactilityC/Source/TactilityC/app/App.cpp new file mode 100644 index 00000000..141077d9 --- /dev/null +++ b/TactilityC/Source/TactilityC/app/App.cpp @@ -0,0 +1,102 @@ +#include "App.h" +#include "Log.h" +#include "app/ElfApp.h" + +#define TAG "tactilityc_app" + +AppOnStart elfOnStart = nullptr; +AppOnStop elfOnStop = nullptr; +AppOnShow elfOnShow = nullptr; +AppOnHide elfOnHide = nullptr; +AppOnResult elfOnResult = nullptr; + +static void onStartWrapper(tt::app::AppContext& context) { + if (elfOnStart != nullptr) { + TT_LOG_I(TAG, "onStartWrapper"); + elfOnStart(&context); + } else { + TT_LOG_W(TAG, "onStartWrapper not set"); + } +} + +static void onStopWrapper(tt::app::AppContext& context) { + if (elfOnStop != nullptr) { + TT_LOG_I(TAG, "onStopWrapper"); + elfOnStop(&context); + } else { + TT_LOG_W(TAG, "onStopWrapper not set"); + } +} + +static void onShowWrapper(tt::app::AppContext& context, lv_obj_t* parent) { + if (elfOnShow != nullptr) { + TT_LOG_I(TAG, "onShowWrapper"); + elfOnShow(&context, parent); + } else { + TT_LOG_W(TAG, "onShowWrapper not set"); + } +} + +static void onHideWrapper(tt::app::AppContext& context) { + if (elfOnHide != nullptr) { + TT_LOG_I(TAG, "onHideWrapper"); + elfOnHide(&context); + } else { + TT_LOG_W(TAG, "onHideWrapper not set"); + } +} + +static void onResultWrapper(tt::app::AppContext& context, tt::app::Result result, const tt::Bundle& resultData) { + if (elfOnResult != nullptr) { + TT_LOG_I(TAG, "onResultWrapper"); +// Result convertedResult = AppResultError; +// switch (result) { +// case tt::app::ResultOk: +// convertedResult = AppResultOk; +// break; +// case tt::app::ResultCancelled: +// convertedResult = AppResultCancelled; +// break; +// case tt::app::ResultError: +// convertedResult = AppResultError; +// break; +// } +// elfOnResult(&context, convertedResult, (BundleHandle)&resultData); + } else { + TT_LOG_W(TAG, "onResultWrapper not set"); + } +} + +tt::app::AppManifest manifest = { + .id = "ElfWrapperInTactilityC", + .name = "", + .icon = "", + .onStart = onStartWrapper, + .onStop = onStopWrapper, + .onShow = onShowWrapper, + .onHide = onHideWrapper, + .onResult = onResultWrapper +}; + +extern "C" { + +void tt_set_app_manifest( + const char* name, + const char* _Nullable icon, + AppOnStart onStart, + AppOnStop _Nullable onStop, + AppOnShow _Nullable onShow, + AppOnHide _Nullable onHide, + AppOnResult _Nullable onResult +) { + manifest.name = name; + manifest.icon = icon ? icon : ""; + elfOnStart = onStart; + elfOnStop = onStop; + elfOnShow = onShow; + elfOnHide = onHide; + elfOnResult = onResult; + tt::app::setElfAppManifest(manifest); +} + +} \ No newline at end of file diff --git a/TactilityC/Source/TactilityC/app/App.h b/TactilityC/Source/TactilityC/app/App.h new file mode 100644 index 00000000..a3488365 --- /dev/null +++ b/TactilityC/Source/TactilityC/app/App.h @@ -0,0 +1,36 @@ +#pragma once + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* AppContextHandle; +typedef void* BundleHandle; + +typedef enum { + AppResultOk, + AppResultCancelled, + AppResultError +} Result; + +typedef void (*AppOnStart)(AppContextHandle app); +typedef void (*AppOnStop)(AppContextHandle app); +typedef void (*AppOnShow)(AppContextHandle app, lv_obj_t* parent); +typedef void (*AppOnHide)(AppContextHandle app); +typedef void (*AppOnResult)(AppContextHandle app, Result result, BundleHandle resultData); + +void tt_set_app_manifest( + const char* name, + const char* _Nullable icon, + AppOnStart onStart, + AppOnStop _Nullable onStop, + AppOnShow _Nullable onShow, + AppOnHide _Nullable onHide, + AppOnResult _Nullable onResult +); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityC/Source/TactilityC/app/SelectionDialog.cpp b/TactilityC/Source/TactilityC/app/SelectionDialog.cpp new file mode 100644 index 00000000..64471080 --- /dev/null +++ b/TactilityC/Source/TactilityC/app/SelectionDialog.cpp @@ -0,0 +1,14 @@ +#include "app/selectiondialog/SelectionDialog.h" + +extern "C" { + +void tt_app_selectiondialog_start(const char* title, int argc, const char* argv[]) { + std::vector list; + for (int i = 0; i < argc; i++) { + const char* item = argv[i]; + list.push_back(item); + } + tt::app::selectiondialog::start(title, list); +} + +} diff --git a/TactilityC/Source/TactilityC/app/SelectionDialog.h b/TactilityC/Source/TactilityC/app/SelectionDialog.h new file mode 100644 index 00000000..6c8a9b53 --- /dev/null +++ b/TactilityC/Source/TactilityC/app/SelectionDialog.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void tt_app_selectiondialog_start(const char* title, int argc, const char* argv[]); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityC/Source/TactilityC/lvgl/Toolbar.cpp b/TactilityC/Source/TactilityC/lvgl/Toolbar.cpp new file mode 100644 index 00000000..bc9ccc86 --- /dev/null +++ b/TactilityC/Source/TactilityC/lvgl/Toolbar.cpp @@ -0,0 +1,14 @@ +#include "Toolbar.h" +#include "lvgl/Toolbar.h" + +extern "C" { + +lv_obj_t* tt_lvgl_toolbar_create(lv_obj_t* parent, AppContextHandle context) { + return tt::lvgl::toolbar_create(parent, *(tt::app::AppContext*)context); +} + +lv_obj_t* tt_lvgl_toolbar_create_simple(lv_obj_t* parent, const char* title) { + return tt::lvgl::toolbar_create(parent, title); +} + +} diff --git a/TactilityC/Source/TactilityC/lvgl/Toolbar.h b/TactilityC/Source/TactilityC/lvgl/Toolbar.h new file mode 100644 index 00000000..f472a50f --- /dev/null +++ b/TactilityC/Source/TactilityC/lvgl/Toolbar.h @@ -0,0 +1,15 @@ +#pragma once + +#include "lvgl.h" +#include "TactilityC/app/App.h" + +#ifdef __cplusplus +extern "C" { +#endif + +lv_obj_t* tt_lvgl_toolbar_create(lv_obj_t* parent, AppContextHandle context); +lv_obj_t* tt_lvgl_toolbar_create_simple(lv_obj_t* parent, const char* title); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityCore/CMakeLists.txt b/TactilityCore/CMakeLists.txt index ad4c68dc..d7fa6459 100644 --- a/TactilityCore/CMakeLists.txt +++ b/TactilityCore/CMakeLists.txt @@ -3,31 +3,35 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -file(GLOB_RECURSE SOURCES "Source/*.c*") -file(GLOB_RECURSE HEADERS "Source/*.h*") - -add_library(TactilityCore OBJECT) - -target_sources(TactilityCore - PRIVATE ${SOURCES} - PUBLIC ${HEADERS} -) - -target_include_directories(TactilityCore SYSTEM - PUBLIC Source/ -) - if (DEFINED ENV{ESP_IDF_VERSION}) - add_definitions(-DESP_PLATFORM) - target_link_libraries(TactilityCore - PUBLIC idf::mbedtls - PRIVATE idf::nvs_flash # ESP-IDF // for secure.c + file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + + idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "Source/" + REQUIRES mbedtls nvs_flash ) + + add_definitions(-DESP_PLATFORM) else() + file(GLOB_RECURSE SOURCES "Source/*.c*") + file(GLOB_RECURSE HEADERS "Source/*.h*") + + add_library(TactilityCore OBJECT) + + target_sources(TactilityCore + PRIVATE ${SOURCES} + PUBLIC ${HEADERS} + ) + + target_include_directories(TactilityCore SYSTEM + PUBLIC Source/ + ) + add_definitions(-D_Nullable=) add_definitions(-D_Nonnull=) target_link_libraries(TactilityCore - PUBLIC mbedtls - PUBLIC freertos_kernel + PUBLIC mbedtls + PUBLIC freertos_kernel ) -endif() +endif() \ No newline at end of file diff --git a/TactilityCore/Source/Mutex.cpp b/TactilityCore/Source/Mutex.cpp index e2ba4cd6..49f80a9c 100644 --- a/TactilityCore/Source/Mutex.cpp +++ b/TactilityCore/Source/Mutex.cpp @@ -112,7 +112,7 @@ ThreadId Mutex::getOwner() const { std::unique_ptr Mutex::scoped() const { - return std::move(std::make_unique(*this)); + return std::make_unique(*this); } Mutex* tt_mutex_alloc(Mutex::Type type) { diff --git a/TactilityCore/Source/file/File.cpp b/TactilityCore/Source/file/File.cpp new file mode 100644 index 00000000..4f843fed --- /dev/null +++ b/TactilityCore/Source/file/File.cpp @@ -0,0 +1,82 @@ +#include "File.h" + +namespace tt::file { + +#define TAG "file" + +long getSize(FILE* file) { + long original_offset = ftell(file); + + if (fseek(file, 0, SEEK_END) != 0) { + TT_LOG_E(TAG, "fseek failed"); + return -1; + } + + long file_size = ftell(file); + if (file_size == -1) { + TT_LOG_E(TAG, "Could not get file length"); + return -1; + } + + if (fseek(file, original_offset, SEEK_SET) != 0) { + TT_LOG_E(TAG, "fseek Failed"); + return -1; + } + + return file_size; +} + +static std::unique_ptr readBinaryInternal(const char* filepath, size_t& outSize, size_t sizePadding = 0) { + FILE* file = fopen(filepath, "rb"); + + if (file == nullptr) { + TT_LOG_E(TAG, "Failed to open %s", filepath); + return nullptr; + } + + long content_length = getSize(file); + if (content_length == -1) { + TT_LOG_E(TAG, "Failed to determine content length for %s", filepath); + return nullptr; + } + + auto data = std::make_unique(content_length + sizePadding); + if (data == nullptr) { + TT_LOG_E(TAG, "Insufficient memory. Failed to allocate %ldl bytes.", content_length); + return nullptr; + } + + size_t buffer_offset = 0; + while (buffer_offset < content_length) { + size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file); + TT_LOG_I(TAG, "Read %d bytes", bytes_read); + if (bytes_read > 0) { + buffer_offset += bytes_read; + } else { // Something went wrong? + data = nullptr; + break; + } + } + + outSize = buffer_offset; + + fclose(file); + return data; +} + +std::unique_ptr readBinary(const char* filepath, size_t& outSize) { + return readBinaryInternal(filepath, outSize); +} + +std::unique_ptr readString(const char* filepath) { + size_t size = 0; + auto data = readBinaryInternal(filepath, size, 1); + if (size > 0) { + data.get()[size] = 0; // Append null terminator + return data; + } else { + return nullptr; + } +} + +} diff --git a/TactilityCore/Source/file/File.h b/TactilityCore/Source/file/File.h new file mode 100644 index 00000000..47dd82f7 --- /dev/null +++ b/TactilityCore/Source/file/File.h @@ -0,0 +1,15 @@ +#pragma once + +#include "TactilityCore.h" +#include + +namespace tt::file { + +#define TAG "file" + +long getSize(FILE* file); + +std::unique_ptr readBinary(const char* filepath, size_t& outSize); +std::unique_ptr readString(const char* filepath); + +} diff --git a/TactilityHeadless/CMakeLists.txt b/TactilityHeadless/CMakeLists.txt index cf9415a4..81405982 100644 --- a/TactilityHeadless/CMakeLists.txt +++ b/TactilityHeadless/CMakeLists.txt @@ -10,18 +10,15 @@ if (DEFINED ENV{ESP_IDF_VERSION}) SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source/" PRIV_INCLUDE_DIRS "Private/" - REQUIRES esp_wifi nvs_flash spiffs driver + REQUIRES TactilityCore esp_wifi nvs_flash spiffs driver ) - set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets") - spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT) - - set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config") - spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT) - - target_link_libraries(${COMPONENT_LIB} - PUBLIC TactilityCore - ) + if (NOT DEFINED TACTILITY_SKIP_SPIFFS) + set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets") + spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT) + set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config") + spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT) + endif() add_definitions(-DESP_PLATFORM) else() diff --git a/sdkconfig.board.lilygo_tdeck b/sdkconfig.board.lilygo_tdeck index 883211b0..2e07d39e 100644 --- a/sdkconfig.board.lilygo_tdeck +++ b/sdkconfig.board.lilygo_tdeck @@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y # Hardware: Main CONFIG_TT_BOARD_LILYGO_TDECK=y diff --git a/sdkconfig.board.m5stack_core2 b/sdkconfig.board.m5stack_core2 index 9bf63768..f28c2c09 100644 --- a/sdkconfig.board.m5stack_core2 +++ b/sdkconfig.board.m5stack_core2 @@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y # Hardware: Main CONFIG_TT_BOARD_M5STACK_CORE2=y diff --git a/sdkconfig.board.m5stack_cores3 b/sdkconfig.board.m5stack_cores3 index e9e28f25..ffa7a204 100644 --- a/sdkconfig.board.m5stack_cores3 +++ b/sdkconfig.board.m5stack_cores3 @@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y # Hardware: Main CONFIG_TT_BOARD_M5STACK_CORES3=y diff --git a/sdkconfig.board.yellow_board b/sdkconfig.board.yellow_board index cc610426..ad464d9e 100644 --- a/sdkconfig.board.yellow_board +++ b/sdkconfig.board.yellow_board @@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y # Hardware: Main CONFIG_TT_BOARD_YELLOW_BOARD_24_CAP=y diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 131bb3ad..8dc75001 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -18,6 +18,7 @@ CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y # Hardware defaults CONFIG_TT_BOARD_CUSTOM=y