diff --git a/Helpers.mk b/Helpers.mk index a1f13bd16..ce985247f 100644 --- a/Helpers.mk +++ b/Helpers.mk @@ -32,6 +32,13 @@ check_no_spaces = \ $(if $(findstring $(strip $($(strip $1))),$($(strip $1))),,$(error Error: Space in variable $(strip $1))) \ $(if $(word 2,$($(strip $1))),$(error Error: Multiple entries in variable $(strip $1)),) +# Reject directory paths with trailing slashes. +# +# Params: +# 1. Variable name holding path to test. +reject_trailing_slash = \ + $(if $(filter $(dir $1),$1),$(error Error: Trailing slash in path "$1")) + # Check for a ~/ at the beginning of a path variable (TOCK_USERLAND_BASE_DIR). # Make will not properly expand this. ifdef TOCK_USERLAND_BASE_DIR diff --git a/examples/tests/app_loader/abort-test/.gitignore b/examples/tests/app_loader/abort-test/.gitignore new file mode 100644 index 000000000..ba1debd3c --- /dev/null +++ b/examples/tests/app_loader/abort-test/.gitignore @@ -0,0 +1 @@ +loadable_binaries.h diff --git a/examples/tests/app_loader/abort-test/Makefile b/examples/tests/app_loader/abort-test/Makefile new file mode 100644 index 000000000..a991b5333 --- /dev/null +++ b/examples/tests/app_loader/abort-test/Makefile @@ -0,0 +1,14 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# List of apps to generate embed rules for +APPS_TO_EMBED := \ + $(TOCK_USERLAND_BASE_DIR)/examples/tests/adc/adc + +# Include app loading support rules. Rules to generate app binary images. +include ../support/CommonAppMake.mk diff --git a/examples/tests/app_loader/abort-test/README.md b/examples/tests/app_loader/abort-test/README.md new file mode 100644 index 000000000..3f626e58b --- /dev/null +++ b/examples/tests/app_loader/abort-test/README.md @@ -0,0 +1,9 @@ +# Dynamic App Loader Abort Test (Helper App) +============================================ +This app showcases the ability of the `abort` functionality of +the Dynamic Process Loader. + +When the app boots, it tries to load the `adc` app, aborts midway +and logs the result. Ideally, we should see: +`[Success] Abort Successful.`, and the `abort-test` app should +terminate. \ No newline at end of file diff --git a/examples/tests/app_loader/abort-test/main.c b/examples/tests/app_loader/abort-test/main.c new file mode 100644 index 000000000..de8a5c1d6 --- /dev/null +++ b/examples/tests/app_loader/abort-test/main.c @@ -0,0 +1,125 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "loadable_binaries.h" + +/****************************************************************************************************** +* Callback Tracking Flags +******************************************************************************************************/ +static bool setup_done = false; // to check if setup is done +static bool abort_done = false; // to check if write abort was successful +volatile bool abort_flag = false; // track if abort request was sent +uint32_t write_buffer_size = 4096; + +/****************************************************************************************************** +* Function Prototypes +******************************************************************************************************/ +void abort_test(void); + +/****************************************************************************************************** + * Callback functions + * + * 1. Set callback to initiate the dynamic app load process + * 2. Set callback to call `abort()` with a timer + *******************************************************************************************************/ + +static void app_setup_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + setup_done = true; +} + +static void app_abort_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + abort_done = true; +} + +static void abort_alarm_cb(__attribute__ ((unused)) uint32_t now, + __attribute__ ((unused)) uint32_t scheduled, + __attribute__ ((unused)) void* opaque) { + printf("[Timer] Triggering abort now.\n"); + abort_flag = true; + int ret = libtock_app_loader_abort(app_abort_done_callback); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Abort Failed: %d.\n", ret); + } + + // wait on abort done callback + yield_for(&abort_done); + abort_done = false; + printf("[Success] Abort Successful.\n"); +} + +/****************************************************************************************************** +* Function to test the abort functionality of Tock's dynamic app loading feature set +* +* Takes callback function as argument +******************************************************************************************************/ + +void abort_test(void) { + + libtock_alarm_t abort_alarm; + + unsigned char* app_data = (uint8_t*)(uintptr_t)binaries[0]; + size_t app_size = binary_sizes[0]; + size_t binary_size = actual_sizes[0]; + + int ret = libtock_app_loader_setup(app_size, app_setup_done_callback); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Setup Failed: %d.\n", ret); + tock_exit(ret); + } + + // wait on setup done callback + yield_for(&setup_done); + setup_done = false; + + libtock_alarm_in_ms(150, abort_alarm_cb, NULL, &abort_alarm); + + size_t offset = 0; + while (offset < binary_size) { + if (abort_flag) break; + size_t chunk_len = (binary_size - offset > write_buffer_size) + ? write_buffer_size + : binary_size - offset; + + int ret1 = libtock_app_loader_write(offset, &app_data[offset], chunk_len); + if (ret1 != RETURNCODE_SUCCESS) { + printf("[Error] Chunk write failed at offset %zu\n", offset); + break; + } + offset += chunk_len; + } +} + +/****************************************************************************************************** +* Main +******************************************************************************************************/ + +int main(void) { + printf("[Log] Simple test app to test abort functionality of dynamic process loading.\n"); + + // check if app loader driver exists + if (!libtock_app_loader_exists()) { + printf("No App Loader driver!\n"); + return -1; + } + + libtocksync_alarm_delay_ms(5000); + + printf("[Log] Initiating Abort Test.\n"); + + abort_test(); + + printf("[Log] Exiting Abort Test.\n"); + return 0; +} diff --git a/examples/tests/app_loader/button-press-loading/.gitignore b/examples/tests/app_loader/button-press-loading/.gitignore new file mode 100644 index 000000000..ba1debd3c --- /dev/null +++ b/examples/tests/app_loader/button-press-loading/.gitignore @@ -0,0 +1 @@ +loadable_binaries.h diff --git a/examples/tests/app_loader/button-press-loading/Makefile b/examples/tests/app_loader/button-press-loading/Makefile new file mode 100644 index 000000000..bfcfc69f9 --- /dev/null +++ b/examples/tests/app_loader/button-press-loading/Makefile @@ -0,0 +1,15 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# List of apps to generate embed rules for +APPS_TO_EMBED = \ + $(TOCK_USERLAND_BASE_DIR)/examples/blink \ + $(TOCK_USERLAND_BASE_DIR)/examples/tests/adc/adc + +# Include app loading support rules. Rules to generate app binary images. +include ../support/CommonAppMake.mk diff --git a/examples/tests/app_loader/button-press-loading/README.md b/examples/tests/app_loader/button-press-loading/README.md new file mode 100644 index 000000000..786cd9cba --- /dev/null +++ b/examples/tests/app_loader/button-press-loading/README.md @@ -0,0 +1,47 @@ +# Dynamic App Loader (Helper App) +================================= + +This app waits upon the user to press one of two buttons on the device (if supported). + +When Button 1 (Default user button on boards) is pressed, the app tried to load in the +`blink` app. + +When Button 2 is pressed, the app tries to load in the `adc` app. + +There are three stages to this: + +1. Setup Phase +2. Flash Phase +3. Load Phase + +#### Setup Phase +During the setup phase, the application passes the size of the new app `(blink)/(adc)`'s +binary to the app_loader capsule. + +The capsule returns with either a success or failure. + +On success, the app requests the app_loader capsule to flash the `blink/adc` app. + +On Failure, the app exits logs the reason for failure and exits. + +#### Flash Phase +The app sends the binary of the `blink/adc` app 512 bytes (this is the size of the shared +buffer between the app and the capsule) at a time along with the offset. + +The capsule checks that these values do not violate the bounds dictated by the kernel +and then requests the kernel to flash the app. + +The kernel performs more stringent checking on the request to ensure that memory access +violations do not take place, and flashes the app. + +The capsule then returns with either a success or failure. + +On success, the app requests the app_loader capsule to load the `blink/adc` app. + +On Failure, the app exits logs the reason for failure and exits. + +#### Load Phase +The app requests the capsule to load the new app. There are only two outcomes: + +1. The `blink/adc` app is loaded successfully and functions without requiring a restart. +2. The load process failed somewhere, and the app is erased. \ No newline at end of file diff --git a/examples/tests/app_loader/button-press-loading/main.c b/examples/tests/app_loader/button-press-loading/main.c new file mode 100644 index 000000000..326a4e985 --- /dev/null +++ b/examples/tests/app_loader/button-press-loading/main.c @@ -0,0 +1,170 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "loadable_binaries.h" + +/****************************************************************************************************** +* Callback Tracking Flags +******************************************************************************************************/ +static bool setup_done = false; // to check if setup is done +static bool finalize_done = false; // to check if the process was finalized successfully +static bool load_done = false; // to check if the process was loaded successfully +static bool app_load = false; // to check if there is a request to load a new app + +/****************************************************************************************************** +* Variables to hold loadable app information +******************************************************************************************************/ +const char* new_app_name = NULL; +unsigned char* new_app_data = NULL; +size_t new_app_size = 0; +size_t new_binary_size = 0; +uint32_t write_buffer_size = 4096; + +/****************************************************************************************************** +* Callback functions +* +* Set button callback to initiate the dynamic app load process on pressing buttons +******************************************************************************************************/ + +static void app_setup_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + setup_done = true; +} + +static void app_finalize_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + finalize_done = true; +} + +static void app_load_done_callback(int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + + if (arg0 != RETURNCODE_SUCCESS) { + printf("[Error] Process creation failed: %d.\n", arg0); + } else { + printf("[Success] Process created successfully.\n"); + } + load_done = true; +} + +// Callback for button presses. +static void button_callback(__attribute__ ((unused)) returncode_t retval, int btn_num, __attribute__ ( + (unused)) bool pressed) { + // Callback for button presses. + if (btn_num < BINARY_COUNT) { + app_load = true; + new_app_name = binary_names[btn_num]; + new_app_data = (uint8_t*)(uintptr_t)binaries[btn_num]; + new_app_size = binary_sizes[btn_num]; + new_binary_size = actual_sizes[btn_num]; + } else { + printf("[App Loader] Invalid button selected. Unable to install!\n"); + } +} + + +/****************************************************************************************************** +* Helper Function for the apploader machine +* +* Takes app size and the app binary as arguments +******************************************************************************************************/ + +static void appload(size_t binary_size, size_t app_size, uint8_t binary[]) { + int ret = libtock_app_loader_setup(app_size, app_setup_done_callback); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Setup Failed: %d.\n", ret); + tock_exit(ret); + } + + // wait on setup done callback + yield_for(&setup_done); + setup_done = false; + + printf("[Success] Setup successful. Writing app to flash.\n"); + + size_t offset = 0; + while (offset < binary_size) { + size_t chunk_len = (binary_size - offset > write_buffer_size) + ? write_buffer_size + : binary_size - offset; + + int ret1 = libtock_app_loader_write(offset, &binary[offset], chunk_len); + if (ret1 != RETURNCODE_SUCCESS) { + printf("[Error] Chunk write failed at offset %zu\n", offset); + break; + } + offset += chunk_len; + } + + int ret2 = libtock_app_loader_finalize(app_finalize_done_callback); + if (ret2 != RETURNCODE_SUCCESS) { + printf("[Error] Finalizing app failed: %d.\n", ret2); + tock_exit(ret2); + } + + // wait on finalize done callback + yield_for(&finalize_done); + finalize_done = false; + + printf("[Success] App flashed successfully. Creating process now.\n"); + int ret3 = libtock_app_loader_load(app_load_done_callback); + if (ret3 != RETURNCODE_SUCCESS) { + printf("[Error] Process creation failed: %d.\n", ret3); + tock_exit(ret3); + } + + // wait on load done callback + yield_for(&load_done); + load_done = false; + + printf("[Log] Waiting for a button press.\n"); +} + +/****************************************************************************************************** +* Main +******************************************************************************************************/ + +int main(void) { + printf("[Log] Simple test app to load an app dynamically.\n"); + + // check if app loader driver exists + if (!libtock_app_loader_exists()) { + printf("No App Loader driver!\n"); + return -1; + } + + int count; + int err = libtock_button_count(&count); + // Ensure there is a button to use. + if (err < 0) return err; + printf("[Log] There are %d buttons on this board.\n", count); + + // Enable interrupts on each button. + for (int i = 0; i < count; i++) { + libtock_button_notify_on_press(i, button_callback); + } + + printf("[Log] Waiting for a button press.\n"); + + while (1) { + if (app_load) { + printf("[Event] Button for %s pressed!\n", new_app_name); + printf("size: %d bytes\n", new_app_size); + appload(new_binary_size, new_app_size, new_app_data); + app_load = false; + } + yield(); + } +} diff --git a/examples/tests/app_loader/button-press-uninstall/Makefile b/examples/tests/app_loader/button-press-uninstall/Makefile new file mode 100644 index 000000000..b3fe26a2e --- /dev/null +++ b/examples/tests/app_loader/button-press-uninstall/Makefile @@ -0,0 +1,10 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include app loading support rules. Rules to generate app binary images. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/app_loader/button-press-uninstall/README.md b/examples/tests/app_loader/button-press-uninstall/README.md new file mode 100644 index 000000000..786cd9cba --- /dev/null +++ b/examples/tests/app_loader/button-press-uninstall/README.md @@ -0,0 +1,47 @@ +# Dynamic App Loader (Helper App) +================================= + +This app waits upon the user to press one of two buttons on the device (if supported). + +When Button 1 (Default user button on boards) is pressed, the app tried to load in the +`blink` app. + +When Button 2 is pressed, the app tries to load in the `adc` app. + +There are three stages to this: + +1. Setup Phase +2. Flash Phase +3. Load Phase + +#### Setup Phase +During the setup phase, the application passes the size of the new app `(blink)/(adc)`'s +binary to the app_loader capsule. + +The capsule returns with either a success or failure. + +On success, the app requests the app_loader capsule to flash the `blink/adc` app. + +On Failure, the app exits logs the reason for failure and exits. + +#### Flash Phase +The app sends the binary of the `blink/adc` app 512 bytes (this is the size of the shared +buffer between the app and the capsule) at a time along with the offset. + +The capsule checks that these values do not violate the bounds dictated by the kernel +and then requests the kernel to flash the app. + +The kernel performs more stringent checking on the request to ensure that memory access +violations do not take place, and flashes the app. + +The capsule then returns with either a success or failure. + +On success, the app requests the app_loader capsule to load the `blink/adc` app. + +On Failure, the app exits logs the reason for failure and exits. + +#### Load Phase +The app requests the capsule to load the new app. There are only two outcomes: + +1. The `blink/adc` app is loaded successfully and functions without requiring a restart. +2. The load process failed somewhere, and the app is erased. \ No newline at end of file diff --git a/examples/tests/app_loader/button-press-uninstall/main.c b/examples/tests/app_loader/button-press-uninstall/main.c new file mode 100644 index 000000000..c4e8ef345 --- /dev/null +++ b/examples/tests/app_loader/button-press-uninstall/main.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +/****************************************************************************************************** +* Short ID and version of app that has to be uninstalled (Blink in this case) +******************************************************************************************************/ +const uint32_t short_id = 4246331976; +const uint32_t version = 0; + +/****************************************************************************************************** +* Callback Tracking Flags +******************************************************************************************************/ +static bool uninstall_done = false; // to check if setup is done +static bool app_uninstall = false; // to track if app has to be uninstalled + +/****************************************************************************************************** +* Callback functions +* +* Set button callback to initiate the uninstall on pressing buttons +******************************************************************************************************/ + +static void app_uninstall_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + + if (arg0 != RETURNCODE_SUCCESS) { + printf("[Error] Uninstall failed: %d.\n", arg0); + } else { + printf("[Success] Uninstalled app successfully.\n"); + } + uninstall_done = true; +} + +// Callback for button presses. +static void button_callback(__attribute__ ((unused)) returncode_t retval, int btn_num, __attribute__ ( + (unused)) bool pressed) { + // Callback for button presses. + if (btn_num == 0) { + app_uninstall = true; + } else { + printf("[App Uninstaller] Invalid button selected. Unable to uninstall!\n"); + } +} + + +/****************************************************************************************************** +* Main +******************************************************************************************************/ + +int main(void) { + printf("[Log] Simple test app to uninstall an app during runtime.\n"); + + // check if app loader driver exists + if (!libtock_app_loader_exists()) { + printf("No App Loader driver!\n"); + return -1; + } + + int count; + int err = libtock_button_count(&count); + // Ensure there is a button to use. + if (err < 0) return err; + printf("[Log] There are %d buttons on this board.\n", count); + + // Enable interrupts on each button. + for (int i = 0; i < count; i++) { + libtock_button_notify_on_press(i, button_callback); + } + + printf("[Log] Waiting for a button press.\n"); + + while (1) { + if (app_uninstall) { + printf("Uninstalling app with Short ID: %" PRIu32 " and version %" PRIu32 "\n", short_id, version); + int ret = libtock_app_loader_uninstall(short_id, version, app_uninstall_done_callback); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Uninstall Failed: %d.\n", ret); + tock_exit(ret); + } + yield_for(&uninstall_done); + uninstall_done = false; + app_uninstall = false; + } + yield(); + } +} diff --git a/examples/tests/app_loader/support/AppLoaderSupport.mk b/examples/tests/app_loader/support/AppLoaderSupport.mk new file mode 100644 index 000000000..fa0de01e9 --- /dev/null +++ b/examples/tests/app_loader/support/AppLoaderSupport.mk @@ -0,0 +1,67 @@ +# Rules that need to be generated exactly once per arch, i.e. +# foreach (arch1, ...) { +# $(1)=archX + +HELPER_DIR := $(TOCK_USERLAND_BASE_DIR)/examples/tutorials/dynamic-apps-and-policies/support + +# Change this to link from the dyn app loader example when that is merged. + +define EMBED_RULES_PER_ARCH + +####### LEGACY COMPILER SUPPORT +####### TODO: Delete when #embed is safer to assume as ubiquitous +override CFLAGS_$(1) += '-Ibuild/$(1)/' +####### END LEGACY BLOCK + +####### EMERGING COMPILER SUPPORT +####### TODO: Uncomment below when the above is deleted +####### (older compilers fail on the unknown flag) +####### override CFLAGS_$(1) += '--embed-dir=build/$(1)/' +endef +$(foreach platform, $(TOCK_ARCHS), $(eval $(call EMBED_RULES_PER_ARCH,$(platform)))) + + +# App-specific and arch-specific rules, i.e. +# foreach (app1|app1_path, app2|app2_path) { +# foreach (arch1, arch2) { +# $(1)=appX +# $(2)=appX_path +# $$(1)=archX +define EMBED_RULES_PER_APP + +## Rule to build the app to embed if-needed +.PHONY: $(2) +$(2): + @$(MAKE) -C $(2) -q build/$(1).tab || $(MAKE) -C $(2) + +define EMBED_RULES_PER_ARCH_FOR_$(1) +$$(call reject_trailing_slash, $(2)) +$$(OBJS_$$(1)): $$(BUILDDIR)/$$(1)/$(1).embed + +$$(BUILDDIR)/$$(1)/$(1).embed: $(2)/build/$$(1)/$$(1).tbf + cp $$$$< $$$$@ + +## Use order-only rule for PHONY target to get run-once semantics +$(2)/build/$$(1)/$$(1).tbf: | $(2) + +####### LEGACY COMPILER SUPPORT +####### TODO: Delete when #embed is safer to assume as ubiquitous + +$$(OBJS_$$(1)): $$(BUILDDIR)/$$(1)/$(1).xxd + +# $$(BUILDDIR)/$$(1)/$(1).xxd: $$(BUILDDIR)/$$(1)/$(1).embed +# cd $$(BUILDDIR)/$$(1) && xxd -i $(1).embed | sed 's/unsigned char/const unsigned char/' > $(1).xxd +####### END LEGACY BLOCK +endef +#$$(info $$(foreach platform, $$(TOCK_ARCHS),$$(call EMBED_RULES_PER_ARCH_FOR_$(1),$$(platform)))) +$$(foreach platform, $$(TOCK_ARCHS),$$(eval $$(call EMBED_RULES_PER_ARCH_FOR_$(1),$$(platform)))) +endef +#$(info $(foreach app, $(APPS_TO_EMBED),$(call EMBED_RULES_PER_APP,$(notdir $(app)),$(app)))) +$(foreach app, $(APPS_TO_EMBED),$(eval $(call EMBED_RULES_PER_APP,$(notdir $(app)),$(app)))) + +$(BUILDDIR)/%.xxd: $(BUILDDIR)/%.embed | $(HELPER_DIR)/trim_format.py + @mkdir -p $(@D) + @python3 $(HELPER_DIR)/trim_format.py \ + "$<" "$@" \ + "$(basename $(notdir $@))_embed" + diff --git a/examples/tests/app_loader/support/CommonAppMake.mk b/examples/tests/app_loader/support/CommonAppMake.mk new file mode 100644 index 000000000..6823ea4f3 --- /dev/null +++ b/examples/tests/app_loader/support/CommonAppMake.mk @@ -0,0 +1,80 @@ +# Makefile for user application + +.DEFAULT_GOAL = all + +# Restrict to Cortex M4 for the moment. Add new chip families as needed. +TOCK_TARGETS ?= cortex-m0 cortex-m3 cortex-m4 cortex-m7 + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# # Which files to compile. +# C_SRCS = $(wildcard *.c) + +# List of apps to generate embed rules for: +APPS_TO_EMBED ?= \ + # $(TOCK_USERLAND_BASE_DIR)/examples/blink \ + +BINARY_COUNT = $(words $(APPS_TO_EMBED)) +AUTOGEN_HEADER = loadable_binaries.h +main.c: $(AUTOGEN_HEADER) + + +build: + $(TRACE_DIR) + @mkdir -p $@ + +# Ensure we rebuild if `APPS_TO_EMBED` changes. +.PHONY: FORCE +define DEPENDABLE_VAR +build/$(1): build + @printf '%s' "$($(1))" > build/$(1) +ifneq ("$(shell cat build/$(1) 2>/dev/null)","$($(1))") +build/$(1): FORCE +endif +endef +$(eval $(call DEPENDABLE_VAR,APPS_TO_EMBED)) + +$(AUTOGEN_HEADER): build/APPS_TO_EMBED + @echo "// \`app_loader\`'s makefile autogenerates this file. Do not edit manually." > $(AUTOGEN_HEADER) + @echo "#pragma once" >> $(AUTOGEN_HEADER) + @echo "" >> $(AUTOGEN_HEADER) + @echo "#define BINARY_COUNT ${BINARY_COUNT}" >> $(AUTOGEN_HEADER) + @echo "" >> $(AUTOGEN_HEADER) + @for app in $(notdir $(APPS_TO_EMBED)); do \ + echo "#include \"$$app.xxd\"" >> $(AUTOGEN_HEADER); \ + upper=$$(echo $$app | tr a-z A-Z); \ + echo "#define APP_$$upper $${app}_embed" >> $(AUTOGEN_HEADER); \ + done + @echo "" >> $(AUTOGEN_HEADER) + @echo "const uint8_t* binaries[] = {" >> $(AUTOGEN_HEADER) + @for app in $(notdir $(APPS_TO_EMBED)); do \ + upper=$$(echo $$app | tr a-z A-Z); \ + echo " APP_$$upper," >> $(AUTOGEN_HEADER); \ + done + @echo "};" >> $(AUTOGEN_HEADER) + @echo "" >> $(AUTOGEN_HEADER) + @echo "size_t binary_sizes[] = {" >> $(AUTOGEN_HEADER) + @for app in $(notdir $(APPS_TO_EMBED)); do \ + echo " $${app}_embed_size," >> $(AUTOGEN_HEADER); \ + done + @echo "};" >> $(AUTOGEN_HEADER) + @echo "" >> $(AUTOGEN_HEADER) + @echo "const char* binary_names[] = {" >> $(AUTOGEN_HEADER) + @for app in $(notdir $(APPS_TO_EMBED)); do \ + echo " \"$$app\"," >> $(AUTOGEN_HEADER); \ + done + @echo "};" >> $(AUTOGEN_HEADER) + @echo "" >> $(AUTOGEN_HEADER) + @echo "size_t actual_sizes[] = {" >> $(AUTOGEN_HEADER) + @for app in $(notdir $(APPS_TO_EMBED)); do \ + echo " $${app}_embed_actual_size," >> $(AUTOGEN_HEADER); \ + done + @echo "};" >> $(AUTOGEN_HEADER) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk + +# Include app loading support rules. Rules to generate app binary images. +include ../support/AppLoaderSupport.mk \ No newline at end of file diff --git a/examples/tests/app_loader/support/README.md b/examples/tests/app_loader/support/README.md new file mode 100644 index 000000000..4df08f498 --- /dev/null +++ b/examples/tests/app_loader/support/README.md @@ -0,0 +1,4 @@ +Rules to extract .tbf from a .tab +================================= + +These are helpers to extract arch-specific builds from a `.tab`. diff --git a/examples/tests/app_loader/support/trim_format.py b/examples/tests/app_loader/support/trim_format.py new file mode 100644 index 000000000..74319c403 --- /dev/null +++ b/examples/tests/app_loader/support/trim_format.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +import sys + +def trim_trailing_zeroes(b): + for i in range(len(b) - 1, 0, -1): + if b[i] != 0x0: + return b[:i+1] + return b + +def format_data_strings(b): + def chunked(source, size): + for i in range(0, len(source), size): + yield source[i : i+size] + + rows = [] + for chunk in chunked(b, 10): + row = ["0x{:02x}".format(c) for c in chunk] + rows.append(", ".join(row)) + return "{{\n {}\n}}".format(",\n ".join(rows)) + +def main(input_path, output_path, array_name): + data = open(input_path, "rb").read() + trimmed = trim_trailing_zeroes(data) + c_data = format_data_strings(trimmed) + with open(output_path, "w") as f: + f.write(f"const unsigned char {array_name}[] = {c_data};\n") + f.write(f"const size_t {array_name}_size = {len(data)};\n") + f.write(f"const size_t {array_name}_actual_size = {len(trimmed)};\n") + +if __name__ == "__main__": + if len(sys.argv) != 4: + sys.exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3]) \ No newline at end of file diff --git a/examples/tutorials/dynamic-apps-and-policies/app_loader/main.c b/examples/tutorials/dynamic-apps-and-policies/app_loader/main.c index 03d0ac6ef..a0479edf2 100644 --- a/examples/tutorials/dynamic-apps-and-policies/app_loader/main.c +++ b/examples/tutorials/dynamic-apps-and-policies/app_loader/main.c @@ -9,11 +9,7 @@ #include "loadable_binaries.h" -#define FLASH_BUFFER_SIZE 512 -#define RETURNCODE_SUCCESS 0 - static bool setup_done = false; // to check if setup is done -static bool write_done = false; // to check if writing to flash is done static bool finalize_done = false; // to check if the kernel is done finalizing the process binary static bool load_done = false; // to check if the process was loaded successfully @@ -41,13 +37,6 @@ static void app_setup_done_callback(__attribute__((unused)) int arg0, setup_done = true; } -static void app_write_done_callback(__attribute__((unused)) int arg0, - __attribute__((unused)) int arg1, - __attribute__((unused)) int arg2, - __attribute__((unused)) void* ud) { - write_done = true; -} - static void app_finalize_done_callback(__attribute__((unused)) int arg0, __attribute__((unused)) int arg1, __attribute__((unused)) int arg2, @@ -74,10 +63,11 @@ int install_binary(uint8_t id) { return -1; } - const char* app_name = NULL; - unsigned char* app_data = NULL; - size_t app_size = 0; - size_t binary_size = 0; + const char* app_name = NULL; + unsigned char* app_data = NULL; + size_t app_size = 0; + size_t binary_size = 0; + uint32_t write_buffer_size = 4096; app_name = binary_names[id]; app_data = (uint8_t*)(uintptr_t)binaries[id]; @@ -86,27 +76,47 @@ int install_binary(uint8_t id) { printf("[AppLoader] Requested to load %s!\n", app_name); - int ret = libtock_app_loader_setup(app_size); + int ret = libtock_app_loader_setup(app_size, app_setup_done_callback); if (ret != RETURNCODE_SUCCESS) { printf("[Error] Setup Failed: %d.\n", ret); - return -1; + tock_exit(ret); } + // wait on setup done callback yield_for(&setup_done); setup_done = false; printf("[Success] Setup successful. Writing app to flash.\n"); - int ret1 = write_app(binary_size, app_data); - if (ret1 != RETURNCODE_SUCCESS) { - printf("[Error] App flash write unsuccessful: %d.\n", ret1); - return -1; + + size_t offset = 0; + while (offset < binary_size) { + size_t chunk_len = (binary_size - offset > write_buffer_size) + ? write_buffer_size + : binary_size - offset; + + int ret1 = libtock_app_loader_write(offset, &app_data[offset], chunk_len); + if (ret1 != RETURNCODE_SUCCESS) { + printf("[Error] Chunk write failed at offset %zu\n", offset); + break; + } + offset += chunk_len; } - printf("[Success] App flashed successfully. Creating process now.\n"); - int ret2 = libtock_app_loader_load(); + int ret2 = libtock_app_loader_finalize(app_finalize_done_callback); if (ret2 != RETURNCODE_SUCCESS) { - printf("[Error] Process creation failed: %d.\n", ret2); - return -1; + printf("[Error] Finalizing app failed: %d.\n", ret2); + tock_exit(ret2); + } + + // wait on finalize done callback + yield_for(&finalize_done); + finalize_done = false; + + printf("[Success] App flashed successfully. Creating process now.\n"); + int ret3 = libtock_app_loader_load(app_load_done_callback); + if (ret3 != RETURNCODE_SUCCESS) { + printf("[Error] Process creation failed: %d.\n", ret3); + tock_exit(ret3); } // wait on load done callback @@ -116,63 +126,6 @@ int install_binary(uint8_t id) { return 0; } -/****************************************************************************************************** -* -* Function to write the app into the flash -* -* Takes app size and the app binary as arguments -******************************************************************************************************/ - -int write_app(double size, uint8_t binary[]) { - - uint32_t write_count = 0; - uint8_t write_buffer[FLASH_BUFFER_SIZE]; - uint32_t flash_offset = 0; - - // This value can be changed to different sizes - // to mimic different bus widths. - uint32_t write_buffer_size = FLASH_BUFFER_SIZE; - - // set the write buffer - int ret = libtock_app_loader_set_buffer(write_buffer, FLASH_BUFFER_SIZE); - if (ret != RETURNCODE_SUCCESS) { - printf("[Error] Failed to set the write buffer: %d.\n", ret); - return -1; - } - - write_count = (size + write_buffer_size - 1) / write_buffer_size; - - for (uint32_t offset = 0; offset < write_count; offset++) { - - memset(write_buffer, 0, write_buffer_size); - // copy binary to write buffer - flash_offset = (offset * write_buffer_size); - size_t bytes_left = size - flash_offset; - size_t chunk = bytes_left < write_buffer_size ? bytes_left : write_buffer_size; - memcpy(write_buffer, &binary[write_buffer_size * offset], chunk); - int ret1 = libtock_app_loader_write(flash_offset, write_buffer_size); - if (ret1 != 0) { - printf("[Error] Failed writing data to flash at address: 0x%lx\n", flash_offset); - printf("[Error] Error nature: %d\n", ret1); - return -1; - } - // wait on write done callback - yield_for(&write_done); - write_done = false; - } - - // Now that we are done writing the binary, we ask the kernel to finalize it. - printf("Done writing app, finalizing.\n"); - int ret2 = libtock_app_loader_finalize(); - if (ret2 != 0) { - printf("[Error] Failed to finalize new process binary.\n"); - return -1; - } - yield_for(&finalize_done); - finalize_done = false; - - return 0; -} static void ipc_callback(int pid, int len, int buf, __attribute__ ((unused)) void* ud) { uint8_t* buffer = (uint8_t*) (uintptr_t) buf; @@ -242,34 +195,6 @@ int main(void) { return -1; } - // set up the setup done callback - int err1 = libtock_app_loader_subscribe_setup(app_setup_done_callback, NULL); - if (err1 != 0) { - printf("[Error] Failed to set setup done callback: %d\n", err1); - return err1; - } - - // set up the write done callback - int err2 = libtock_app_loader_subscribe_write(app_write_done_callback, NULL); - if (err2 != 0) { - printf("[Error] Failed to set flash write done callback: %d\n", err2); - return err2; - } - - // set up the finalize done callback - int err3 = libtock_app_loader_subscribe_finalize(app_finalize_done_callback, NULL); - if (err3 != 0) { - printf("[Error] Failed to set finalize done callback: %d\n", err3); - return err3; - } - - // set up the load done callback - int err4 = libtock_app_loader_subscribe_load(app_load_done_callback, NULL); - if (err4 != 0) { - printf("[Error] Failed to set load done callback: %d\n", err4); - return err4; - } - ipc_register_service_callback("app_loader", ipc_callback, NULL); diff --git a/libtock/kernel/app_loader.c b/libtock/kernel/app_loader.c index 6e4d2b76e..e0a185535 100644 --- a/libtock/kernel/app_loader.c +++ b/libtock/kernel/app_loader.c @@ -1,96 +1,156 @@ +#include +#include +#include + +#include + #include "app_loader.h" -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_setup(subscribe_upcall cb, void* userdata) { - return libtock_app_loader_set_setup_upcall(cb, userdata); -} +#define FLASH_BUFFER_SIZE 4096 -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_write(subscribe_upcall cb, void* userdata) { - return libtock_app_loader_set_write_upcall(cb, userdata); -} +static bool write_done = false; +static bool write_upcall_registered = false; -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_finalize(subscribe_upcall cb, void* userdata) { - return libtock_app_loader_set_finalize_upcall(cb, userdata); -} +/****************************************************************************************************** + * Callback functions + * + * Set callback to track write done signal from the kernel + *******************************************************************************************************/ -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_load(subscribe_upcall cb, void* userdata) { - return libtock_app_loader_set_load_upcall(cb, userdata); +static void write_done_callback(__attribute__((unused)) int arg0, + __attribute__((unused)) int arg1, + __attribute__((unused)) int arg2, + __attribute__((unused)) void* ud) { + write_done = true; } -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_abort(subscribe_upcall cb, void* userdata) { - return libtock_app_loader_set_abort_upcall(cb, userdata); -} +/****************************************************************************************************** +* Function to setup for the new app install +* +* Takes app size and the callback function as arguments +******************************************************************************************************/ -/* - * Command to request the kernel to set up for a new app. - * This functions takes the size of the new app as the argument. - */ -returncode_t libtock_app_loader_setup(uint32_t app_length) { - return libtock_app_loader_command_setup(app_length); +returncode_t libtock_app_loader_setup(uint32_t app_size, subscribe_upcall cb) { + // set up the setup done callback + int err = libtock_app_loader_set_setup_upcall(cb, NULL); + if (err != 0) { + printf("[Error] Failed to set setup done callback: %d\n", err); + return err; + } + + return libtock_app_loader_command_setup(app_size); } -/* - * Function to set up a shared buffer with the capsule. - * This buffer is used to write data to the flash. - */ -returncode_t libtock_app_loader_set_buffer(uint8_t* buffer, uint32_t len) { - return libtock_app_loader_write_buffer(buffer, len); -} +/****************************************************************************************************** +* Function to write app binary chunks into the flash +* +* Takes offset, binary chunk and the chunk length as arguments +* The user has to keep track of the offset, and has control +* over the sequence of bytes being written +******************************************************************************************************/ -/* - * Command to write new app binary to the capsule. - * Specify the offset at which the data segment should be written. - * Also specify how long the data segment will be in bytes. - * - * After writing data, the userspace app should yield for - * the write done callback from the capsule. - */ -returncode_t libtock_app_loader_write(uint32_t flash_offset, uint32_t write_length) { - return libtock_app_loader_command_write(flash_offset, write_length); +returncode_t libtock_app_loader_write(uint32_t offset, uint8_t* chunk_data, size_t chunk_len) { + static uint8_t write_buffer[FLASH_BUFFER_SIZE]; + + if (chunk_len > FLASH_BUFFER_SIZE) { + return RETURNCODE_FAIL; + } + + if (!write_upcall_registered) { + // set up the write done callback + libtock_app_loader_set_write_upcall(write_done_callback, NULL); + write_upcall_registered = true; + } + + int ret = libtock_app_loader_write_buffer(write_buffer, FLASH_BUFFER_SIZE); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Failed to set the write buffer: %d.\n", ret); + return ret; + } + + memcpy(write_buffer, chunk_data, chunk_len); + if (chunk_len < FLASH_BUFFER_SIZE) { + memset(write_buffer + chunk_len, 0, FLASH_BUFFER_SIZE - chunk_len); + } + + ret = libtock_app_loader_command_write(offset, FLASH_BUFFER_SIZE); + if (ret != RETURNCODE_SUCCESS) { + printf("[Error] Flash write failed at offset 0x%lx: err %d\n", offset, ret); + return ret; + } + + yield_for(&write_done); + write_done = false; + + return RETURNCODE_SUCCESS; } -/* - * Command to request the kernel to load the newly flashed app. - */ -returncode_t libtock_app_loader_finalize(void) { +/****************************************************************************************************** +* Function to finalize the binary for the new app +* +* Takes callback function as argument +******************************************************************************************************/ + +returncode_t libtock_app_loader_finalize(subscribe_upcall cb) { + write_upcall_registered = false; + // set up the finalize done callbacks + int err = libtock_app_loader_set_finalize_upcall(cb, NULL); + if (err != 0) { + printf("[Error] Failed to set finalize done callback: %d\n", err); + return err; + } + return libtock_app_loader_command_finalize(); } -/* - * Command to request the kernel to load the newly flashed app. - */ -returncode_t libtock_app_loader_load(void) { +/****************************************************************************************************** +* Function to create a process for the newly installed app +* +* Takes callback function as argument +******************************************************************************************************/ + +returncode_t libtock_app_loader_load(subscribe_upcall cb) { + // set up the load done callback + int err = libtock_app_loader_set_load_upcall(cb, NULL); + if (err != 0) { + printf("[Error] Failed to set load done callback: %d\n", err); + return err; + } + return libtock_app_loader_command_load(); } -/* - * Command to request the kernel to abort setup/write process. - */ -returncode_t libtock_app_loader_abort(void) { +/****************************************************************************************************** +* Function to abort the setup/writing of new app binary +* +* Takes callback function as argument +******************************************************************************************************/ + +returncode_t libtock_app_loader_abort(subscribe_upcall cb) { + // set up the abort done callback + int err = libtock_app_loader_set_abort_upcall(cb, NULL); + if (err != 0) { + printf("[Error] Failed to set abort done callback: %d\n", err); + return err; + } + return libtock_app_loader_command_abort(); } + +/****************************************************************************************************** +* Function to uninstall an existing application +* +* Takes the short id and version of the app and the callback function as arguments +******************************************************************************************************/ + +returncode_t libtock_app_loader_uninstall(uint32_t app_short_id, uint32_t app_version, subscribe_upcall cb) { + // set up the uninstall done callback + int err = libtock_app_loader_set_uninstall_upcall(cb, NULL); + if (err != 0) { + printf("[Error] Failed to set setup done callback: %d\n", err); + return err; + } + + return libtock_app_loader_command_uninstall(app_short_id, app_version); +} diff --git a/libtock/kernel/app_loader.h b/libtock/kernel/app_loader.h index 7cc56c3ad..24efcb81c 100644 --- a/libtock/kernel/app_loader.h +++ b/libtock/kernel/app_loader.h @@ -8,89 +8,17 @@ extern "C" #include "libtock/kernel/syscalls/app_loader_syscalls.h" #include "libtock/tock.h" -#define BUTTON1 0 -#define BUTTON2 1 -#define BUTTON3 2 +returncode_t libtock_app_loader_setup(uint32_t app_length, subscribe_upcall cb); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_setup(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_write(uint32_t offset, uint8_t* chunk_data, size_t chunk_len); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_write(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_finalize(subscribe_upcall cb); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_finalize(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_load(subscribe_upcall cb); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_load(subscribe_upcall cb, void* userdata); - -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_subscribe_abort(subscribe_upcall cb, void* userdata); - -/* - * Command to request the kernel to set up for a new app. - * This functions takes the size of the new app as the argument. - */ -returncode_t libtock_app_loader_setup(uint32_t app_length); - - -/* - * Function to set up a shared buffer with the capsule. - * This buffer is used to write data to the flash. - */ -returncode_t libtock_app_loader_set_buffer(uint8_t* buffer, uint32_t len); - -/* - * Command to write new app binary to the capsule. - * Specify the offset at which the data segment should be written. - * Also specify how long the data segment will be in bytes. - * - * After writing data, the userspace app should yield for - * the write done callback from the capsule. - */ -returncode_t libtock_app_loader_write(uint32_t flash_offset, uint32_t write_length); - -/* - * Signal to the app we are done writing a process binary. - */ -returncode_t libtock_app_loader_finalize(void); - -/* - * Command to request the kernel to load the newly flashed app. - */ -returncode_t libtock_app_loader_load(void); - -/* - * Command to request the kernel to abort the setup/write phase. - */ -returncode_t libtock_app_loader_abort(void); - - -/* - * Internal function to write new app to flash. Takes app size as argument. - */ -int write_app(double size, uint8_t binary[]); +returncode_t libtock_app_loader_abort(subscribe_upcall cb); +returncode_t libtock_app_loader_uninstall(uint32_t app_short_id, uint32_t app_version, subscribe_upcall cb); #ifdef __cplusplus diff --git a/libtock/kernel/syscalls/app_loader_syscalls.c b/libtock/kernel/syscalls/app_loader_syscalls.c index 1e2eaec06..70ace41d3 100644 --- a/libtock/kernel/syscalls/app_loader_syscalls.c +++ b/libtock/kernel/syscalls/app_loader_syscalls.c @@ -29,6 +29,11 @@ returncode_t libtock_app_loader_set_abort_upcall(subscribe_upcall cb, void* user return tock_subscribe_return_to_returncode(sval); } +returncode_t libtock_app_loader_set_uninstall_upcall(subscribe_upcall cb, void* userdata) { + subscribe_return_t sval = subscribe(DRIVER_NUM_APP_LOADER, 5, cb, userdata); + return tock_subscribe_return_to_returncode(sval); +} + returncode_t libtock_app_loader_write_buffer(uint8_t* buffer, uint32_t len) { allow_ro_return_t aval = allow_readonly(DRIVER_NUM_APP_LOADER, 0, (void*) buffer, len); return tock_allow_ro_return_to_returncode(aval); @@ -58,3 +63,8 @@ returncode_t libtock_app_loader_command_abort(void) { syscall_return_t res = command(DRIVER_NUM_APP_LOADER, 5, 0, 0); return tock_command_return_novalue_to_returncode(res); } + +returncode_t libtock_app_loader_command_uninstall(uint32_t app_short_id, uint32_t app_version) { + syscall_return_t res = command(DRIVER_NUM_APP_LOADER, 6, app_short_id, app_version); + return tock_command_return_novalue_to_returncode(res); +} diff --git a/libtock/kernel/syscalls/app_loader_syscalls.h b/libtock/kernel/syscalls/app_loader_syscalls.h index abc5f5c5c..3f1e22791 100644 --- a/libtock/kernel/syscalls/app_loader_syscalls.h +++ b/libtock/kernel/syscalls/app_loader_syscalls.h @@ -15,26 +15,12 @@ extern "C" */ bool libtock_app_loader_exists(void); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_set_setup_upcall(subscribe_upcall cb, void* userdata); - /* * Command to request the kernel to set up for a new app. * This functions takes the size of the new app as the argument. */ returncode_t libtock_app_loader_command_setup(uint32_t app_length); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_set_write_upcall(subscribe_upcall cb, void* userdata); - /* * Function to set up a shared buffer with the capsule. * This buffer is used to write data to the flash. @@ -51,13 +37,6 @@ returncode_t libtock_app_loader_write_buffer(uint8_t* buffer, uint32_t len); */ returncode_t libtock_app_loader_command_write(uint32_t flash_offset, uint32_t write_length); -/* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. - */ -returncode_t libtock_app_loader_set_finalize_upcall(subscribe_upcall cb, void* userdata); - /* * Signal to kernel we are done writing a new process binary. */ @@ -69,23 +48,26 @@ returncode_t libtock_app_loader_command_finalize(void); returncode_t libtock_app_loader_command_load(void); /* - * Function to setup the callback from capsule. - * This function takes in the function that will be executed - * when the callback is triggered. + * Command to request the kernel to abort setup/writing process. */ -returncode_t libtock_app_loader_set_load_upcall(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_command_abort(void); /* - * Command to request the kernel to abort setup/writing process. + * Command to request the kernel to uninstall an application. */ -returncode_t libtock_app_loader_command_abort(void); +returncode_t libtock_app_loader_command_uninstall(uint32_t app_short_id, uint32_t app_version); /* - * Function to setup the callback from capsule. + * Functions to setup the callback from capsule. * This function takes in the function that will be executed * when the callback is triggered. */ +returncode_t libtock_app_loader_set_setup_upcall(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_set_write_upcall(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_set_finalize_upcall(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_set_load_upcall(subscribe_upcall cb, void* userdata); returncode_t libtock_app_loader_set_abort_upcall(subscribe_upcall cb, void* userdata); +returncode_t libtock_app_loader_set_uninstall_upcall(subscribe_upcall cb, void* userdata);