Skip to content

Dynamic App Loader Helper #387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7995a0e
validated ota app userland app support for current kernel
viswajith-g Nov 8, 2023
9dc07f2
added apploader test userland application
viswajith-g Feb 17, 2024
0590bc1
rebase and add examples/test/app_loader. New app binaries are located…
viswajith-g Mar 29, 2024
c1713d8
example to work with async loading
viswajith-g Jun 27, 2024
81ace16
added two new apps
viswajith-g Feb 1, 2025
6587756
Switched to newer libtock interface for app_loader apps.
viswajith-g Feb 2, 2025
df3f140
moved syscall wrappers to their own file and moved binaries.h and pyt…
viswajith-g Feb 12, 2025
e8170d6
clean up and revert lvgl version change
viswajith-g Feb 13, 2025
4f06f7b
added abort test + refactored code for other apps.
viswajith-g Feb 16, 2025
c986984
revert lvgl update
viswajith-g Feb 19, 2025
1ed69d8
cleanup
viswajith-g Feb 19, 2025
445b2d8
check if process loading succeeded based on result in upcall + addres…
viswajith-g Feb 27, 2025
7ed99af
modified to match kernel at impl.
viswajith-g Mar 5, 2025
dba9207
updated abort_test.c to include finalize()
viswajith-g Mar 5, 2025
71b0886
removed padding bytes from blink and adc binaries
viswajith-g Mar 6, 2025
1cd6554
examples: tests: app_loader: clean up
viswajith-g May 1, 2025
aa5400d
changed app_binaries directory structure
viswajith-g May 3, 2025
f76d5eb
new directory structure for app_binaries and fix libtock function cal…
viswajith-g May 3, 2025
3e5db1b
split apps into separate files + changed app input to console
viswajith-g May 4, 2025
ecce7d2
tests: app_loader: abort_test: fix ci-format error
viswajith-g May 5, 2025
edc1ab5
tests: abort-test: remove extra debug print statement
viswajith-g May 5, 2025
8e80a57
make abort test standalone
viswajith-g May 5, 2025
654442d
WIP: Automate app image creation; hard-coded arch, buttons only
ppannuto May 28, 2025
0aecac4
WIP: convert abort-test to auto-build
ppannuto May 28, 2025
38a1df5
remove python utility
ppannuto May 28, 2025
1e48aa2
make app embeds arch-agnostic
ppannuto May 29, 2025
065068b
add check for trailing slash in paths
ppannuto May 29, 2025
bc74e47
add fallback for xxd-based embed
ppannuto May 29, 2025
0474e3e
AppLoaderSupport.mk: comment explaining gcc version dependency + chan…
viswajith-g May 29, 2025
20f37e3
fix ci failure
viswajith-g May 29, 2025
408aacb
just disable #embed completely for now :(
ppannuto May 29, 2025
cb3364c
separate app load logic from button press + abort-test upkeep
viswajith-g May 29, 2025
3ba978e
apploader: build embedded app if-needed
ppannuto May 30, 2025
2f7e5c9
button_press_loading: make button ISR simpler
viswajith-g Jun 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Helpers.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions examples/tests/app_loader/abort-test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Makefile for user application

# Restrict to ARM targets for the moment, as we don't have the infrastructure in
# place to enforce the placement rules for RISC-V binaries with dynamic loading
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)

# Include userland master makefile. Contains rules and flags for actually
# building the application.
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk

# 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/AppLoaderSupport.mk
9 changes: 9 additions & 0 deletions examples/tests/app_loader/abort-test/README.md
Original file line number Diff line number Diff line change
@@ -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.
261 changes: 261 additions & 0 deletions examples/tests/app_loader/abort-test/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#include <math.h>
#include <stdio.h>
#include <string.h>

#include <libtock-sync/interface/console.h>
#include <libtock-sync/services/alarm.h>
#include <libtock/kernel/app_loader.h>
#include <libtock/tock.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
static bool abort_done = false; // to check if the process binary writing
// was aborted successfully

static bool abort_tracker = false; // track when an abort was successful to stop writing
// process binary data

/******************************************************************************************************
* Loadable Applications
******************************************************************************************************/

// LEGACY COMPILER SUPPORT
// Avoid #embed until it's more ubiqutious (req's gcc15+, clang19+)
//
// #ifdef __has_embed
// static const uint8_t APP_ADC[] = {
// #embed "adc.embed"
// };
// #else
#include "adc.xxd"
#define APP_ADC adc_embed
// #endif

/******************************************************************************************************
* Function Prototypes
******************************************************************************************************/
void abort_test(void);

/******************************************************************************************************
* Callback functions
*
* 1. Callback to let us know when the capsule is done writing data to flash
* 2. Set callback to initiate the dynamic app load process on receiving command from console
*
*******************************************************************************************************/

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_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,
__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;
}

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;
}

// Callback for console commands.
void abort_test(void) {

const uint8_t* app_data = APP_ADC;
uint32_t app_size = sizeof(APP_ADC);
abort_tracker = true;

int ret = libtock_app_loader_setup(app_size);
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");
int ret1 = write_app(app_size, app_data);
if (ret1 != RETURNCODE_SUCCESS) {
printf("[Error] App flash write unsuccessful: %d.\n", ret1);
tock_exit(ret1);
}

if (!abort_tracker) {
printf("[Success] App flashed successfully. Creating process now.\n");
int ret2 = libtock_app_loader_load();
if (ret2 != RETURNCODE_SUCCESS) {
printf("[Error] Process creation failed: %d.\n", ret2);
tock_exit(ret2);
}

// wait on load done callback
yield_for(&load_done);
load_done = false;
}

abort_tracker = false;
}


/******************************************************************************************************
*
* Function to write the app into the flash
*
* Takes app size and the app binary as arguments
******************************************************************************************************/

int write_app(double size, const uint8_t binary[]) {

int ret;
uint32_t write_count = 0;
uint8_t write_buffer[FLASH_BUFFER_SIZE];
uint32_t flash_offset = 0;

write_count = ceil(size / FLASH_BUFFER_SIZE);

// set the write buffer
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;
}

if (abort_tracker) {
write_count = write_count / 2;
}

for (uint32_t offset = 0; offset < write_count; offset++) {
// copy binary to write buffer
memcpy(write_buffer, &binary[FLASH_BUFFER_SIZE * offset], FLASH_BUFFER_SIZE);
flash_offset = (offset * FLASH_BUFFER_SIZE);
ret = libtock_app_loader_write(flash_offset, FLASH_BUFFER_SIZE);
if (ret != 0) {
printf("[Error] Failed writing data to flash at address: 0x%lx\n", flash_offset);
printf("[Error] Error nature: %d\n", ret);
return -1;
}
// wait on write done callback
yield_for(&write_done);
write_done = false;
}

if (!abort_tracker) {
// 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;
} else {
printf("[Log] Aborting Setup/Write.\n");
int ret0 = libtock_app_loader_abort();
if (ret0 != RETURNCODE_SUCCESS) {
printf("[Error] Abort Failed: %d.\n", ret0);
}

// wait on abort done callback
yield_for(&abort_done);
abort_done = false;
printf("[Success] Abort Successful.\n");
}
return 0;
}


/******************************************************************************************************
*
* 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;
}

// set up the setup done callback
int err1 = libtock_app_loader_setup_set_upcall(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_write_set_upcall(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_finalize_set_upcall(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_load_set_upcall(app_load_done_callback, NULL);
if (err4 != 0) {
printf("[Error] Failed to set load done callback: %d\n", err4);
return err4;
}

// set up the abort done callback
int err5 = libtock_app_loader_abort_set_upcall(app_abort_done_callback, NULL);
if (err5 != 0) {
printf("[Error] Failed to set abort done callback: %d\n", err5);
return err5;
}

libtocksync_alarm_delay_ms(5000);

printf("[Log] Initiating Abort Test.\n");

abort_test();

printf("[Log] Exiting Abort Test.\n");
return 0;
}
23 changes: 23 additions & 0 deletions examples/tests/app_loader/button-press-loading/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Makefile for user application

# Restrict to ARM targets for the moment, as we don't have the infrastructure in
# place to enforce the placement rules for RISC-V binaries with dynamic loading
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)

# Include userland master makefile. Contains rules and flags for actually
# building the application.
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk

# 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/AppLoaderSupport.mk
47 changes: 47 additions & 0 deletions examples/tests/app_loader/button-press-loading/README.md
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading