Skip to content

Commit 9d96571

Browse files
authored
Merge pull request #515 from tock/process-info
MobiSys Tutorial 2025: Add Dynamic Apps and Policies Apps
2 parents 050f47b + 8dffb28 commit 9d96571

33 files changed

+1799
-1
lines changed

Configuration.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ TOCK_ARCHS := $(sort $(foreach target, $(TOCK_TARGETS), $(firstword $(subst |, ,
107107

108108
# Check if elf2tab exists, if not, install it using cargo.
109109
ELF2TAB ?= elf2tab
110-
ELF2TAB_REQUIRED_VERSION := 0.12.0
110+
ELF2TAB_REQUIRED_VERSION := 0.13.0
111111
ELF2TAB_EXISTS := $(shell $(SHELL) -c "command -v $(ELF2TAB)")
112112
ELF2TAB_VERSION := $(shell $(SHELL) -c "$(ELF2TAB) --version | cut -d ' ' -f 2")
113113

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Dynamic Apps and Policies Tutorial
2+
==================================
3+
4+
This folder contains example apps designed to be used with the
5+
[Dynamic Apps and Policies Tutorial](https://book.tockos.org/course/dynamic-apps-and-policies/overview).
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
loadable_binaries.h
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Makefile for user application
2+
3+
.DEFAULT_GOAL = all
4+
5+
# Restrict to Cortex M4 for the moment. Add new chip families as needed.
6+
TOCK_TARGETS ?= cortex-m4
7+
8+
# Specify this directory relative to the current application.
9+
TOCK_USERLAND_BASE_DIR = ../../../..
10+
11+
PACKAGE_NAME = app_loader
12+
13+
# Which files to compile.
14+
C_SRCS := $(wildcard *.c)
15+
16+
# Include an ECDSA signature in the compiled TBF.
17+
ELF2TAB_ARGS += --ecdsa-nist-p256-private ../keys/ec-secp256r1-tutorial-key.private.p8
18+
19+
# List of apps to generate embed rules for:
20+
APPS_TO_EMBED := \
21+
# $(TOCK_USERLAND_BASE_DIR)/examples/blink \
22+
23+
BINARY_COUNT = $(words $(APPS_TO_EMBED))
24+
AUTOGEN_HEADER = loadable_binaries.h
25+
main.c: $(AUTOGEN_HEADER)
26+
27+
28+
build:
29+
$(TRACE_DIR)
30+
@mkdir -p $@
31+
32+
# Ensure we rebuild if `APPS_TO_EMBED` changes.
33+
.PHONY: FORCE
34+
define DEPENDABLE_VAR
35+
build/$(1): build
36+
@printf '%s' "$($(1))" > build/$(1)
37+
ifneq ("$(shell cat build/$(1) 2>/dev/null)","$($(1))")
38+
build/$(1): FORCE
39+
endif
40+
endef
41+
$(eval $(call DEPENDABLE_VAR,APPS_TO_EMBED))
42+
43+
$(AUTOGEN_HEADER): build/APPS_TO_EMBED
44+
@echo "// \`app_loader\`'s makefile autogenerates this file. Do not edit manually." > $(AUTOGEN_HEADER)
45+
@echo "#pragma once" >> $(AUTOGEN_HEADER)
46+
@echo "" >> $(AUTOGEN_HEADER)
47+
@echo "#define BINARY_COUNT ${BINARY_COUNT}" >> $(AUTOGEN_HEADER)
48+
@echo "" >> $(AUTOGEN_HEADER)
49+
@for app in $(notdir $(APPS_TO_EMBED)); do \
50+
echo "#include \"$$app.xxd\"" >> $(AUTOGEN_HEADER); \
51+
upper=$$(echo $$app | tr a-z A-Z); \
52+
echo "#define APP_$$upper $${app}_embed" >> $(AUTOGEN_HEADER); \
53+
done
54+
@echo "" >> $(AUTOGEN_HEADER)
55+
@echo "const uint8_t* binaries[] = {" >> $(AUTOGEN_HEADER)
56+
@for app in $(notdir $(APPS_TO_EMBED)); do \
57+
upper=$$(echo $$app | tr a-z A-Z); \
58+
echo " APP_$$upper," >> $(AUTOGEN_HEADER); \
59+
done
60+
@echo "};" >> $(AUTOGEN_HEADER)
61+
@echo "" >> $(AUTOGEN_HEADER)
62+
@echo "size_t binary_sizes[] = {" >> $(AUTOGEN_HEADER)
63+
@for app in $(notdir $(APPS_TO_EMBED)); do \
64+
echo " $${app}_embed_size," >> $(AUTOGEN_HEADER); \
65+
done
66+
@echo "};" >> $(AUTOGEN_HEADER)
67+
@echo "" >> $(AUTOGEN_HEADER)
68+
@echo "const char* binary_names[] = {" >> $(AUTOGEN_HEADER)
69+
@for app in $(notdir $(APPS_TO_EMBED)); do \
70+
echo " \"$$app\"," >> $(AUTOGEN_HEADER); \
71+
done
72+
@echo "};" >> $(AUTOGEN_HEADER)
73+
@echo "" >> $(AUTOGEN_HEADER)
74+
@echo "size_t actual_sizes[] = {" >> $(AUTOGEN_HEADER)
75+
@for app in $(notdir $(APPS_TO_EMBED)); do \
76+
echo " $${app}_embed_actual_size," >> $(AUTOGEN_HEADER); \
77+
done
78+
@echo "};" >> $(AUTOGEN_HEADER)
79+
80+
# Include userland master makefile. Contains rules and flags for actually
81+
# building the application.
82+
include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk
83+
84+
# Include app loading support rules. Rules to generate app binary images.
85+
include ../support/AppLoaderSupport.mk
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
U8G2 Menu Example
2+
=================
3+
4+
This creates a simple example menu with two pages. One is a dummy selector that
5+
scrolls through a list of fruit. The other supports turning on and off four
6+
LEDs.
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
#include <math.h>
5+
#include <string.h>
6+
7+
#include <libtock-sync/services/alarm.h>
8+
#include <libtock/kernel/app_loader.h>
9+
#include <libtock/kernel/ipc.h>
10+
11+
#include "loadable_binaries.h"
12+
13+
#define FLASH_BUFFER_SIZE 512
14+
#define RETURNCODE_SUCCESS 0
15+
16+
static bool setup_done = false; // to check if setup is done
17+
static bool write_done = false; // to check if writing to flash is done
18+
static bool finalize_done = false; // to check if the kernel is done finalizing the process binary
19+
static bool load_done = false; // to check if the process was loaded successfully
20+
21+
uint8_t app_id = 0;
22+
23+
24+
/********************************
25+
* Function prototypes
26+
*********************************/
27+
int install_binary(uint8_t id);
28+
29+
30+
/******************************************************************************************************
31+
* Callback functions
32+
*
33+
* 1. Callback to let us know when the capsule is done writing data to flash
34+
* 2. Set button callback to initiate the dynamic app load process on pressing button 1 (on nrf52840dk)
35+
*
36+
******************************************************************************************************/
37+
38+
static void app_setup_done_callback(__attribute__((unused)) int arg0,
39+
__attribute__((unused)) int arg1,
40+
__attribute__((unused)) int arg2,
41+
__attribute__((unused)) void* ud) {
42+
setup_done = true;
43+
}
44+
45+
static void app_write_done_callback(__attribute__((unused)) int arg0,
46+
__attribute__((unused)) int arg1,
47+
__attribute__((unused)) int arg2,
48+
__attribute__((unused)) void* ud) {
49+
write_done = true;
50+
}
51+
52+
static void app_finalize_done_callback(__attribute__((unused)) int arg0,
53+
__attribute__((unused)) int arg1,
54+
__attribute__((unused)) int arg2,
55+
__attribute__((unused)) void* ud) {
56+
finalize_done = true;
57+
}
58+
59+
static void app_load_done_callback(int arg0,
60+
__attribute__((unused)) int arg1,
61+
__attribute__((unused)) int arg2,
62+
__attribute__((unused)) void* ud) {
63+
64+
if (arg0 != RETURNCODE_SUCCESS) {
65+
printf("[Error] Process creation failed: %d.\n", arg0);
66+
} else {
67+
printf("[Success] Process created successfully.\n");
68+
}
69+
load_done = true;
70+
}
71+
72+
int install_binary(uint8_t id) {
73+
if (BINARY_COUNT == 0) {
74+
printf("[App Loader] No included apps. Unable to install!\n");
75+
return -1;
76+
}
77+
78+
const char* app_name = NULL;
79+
unsigned char* app_data = NULL;
80+
size_t app_size = 0;
81+
size_t binary_size = 0;
82+
83+
app_name = binary_names[id];
84+
app_data = (uint8_t*)(uintptr_t)binaries[id];
85+
app_size = binary_sizes[id];
86+
binary_size = actual_sizes[id];
87+
88+
printf("[AppLoader] Requested to load %s!\n", app_name);
89+
90+
int ret = libtock_app_loader_setup(app_size);
91+
if (ret != RETURNCODE_SUCCESS) {
92+
printf("[Error] Setup Failed: %d.\n", ret);
93+
return -1;
94+
}
95+
96+
yield_for(&setup_done);
97+
setup_done = false;
98+
99+
printf("[Success] Setup successful. Writing app to flash.\n");
100+
int ret1 = write_app(binary_size, app_data);
101+
if (ret1 != RETURNCODE_SUCCESS) {
102+
printf("[Error] App flash write unsuccessful: %d.\n", ret1);
103+
return -1;
104+
}
105+
106+
printf("[Success] App flashed successfully. Creating process now.\n");
107+
int ret2 = libtock_app_loader_load();
108+
if (ret2 != RETURNCODE_SUCCESS) {
109+
printf("[Error] Process creation failed: %d.\n", ret2);
110+
return -1;
111+
}
112+
113+
// wait on load done callback
114+
yield_for(&load_done);
115+
load_done = false;
116+
117+
return 0;
118+
}
119+
120+
/******************************************************************************************************
121+
*
122+
* Function to write the app into the flash
123+
*
124+
* Takes app size and the app binary as arguments
125+
******************************************************************************************************/
126+
127+
int write_app(double size, uint8_t binary[]) {
128+
129+
uint32_t write_count = 0;
130+
uint8_t write_buffer[FLASH_BUFFER_SIZE];
131+
uint32_t flash_offset = 0;
132+
133+
// This value can be changed to different sizes
134+
// to mimic different bus widths.
135+
uint32_t write_buffer_size = FLASH_BUFFER_SIZE;
136+
137+
// set the write buffer
138+
int ret = libtock_app_loader_set_buffer(write_buffer, FLASH_BUFFER_SIZE);
139+
if (ret != RETURNCODE_SUCCESS) {
140+
printf("[Error] Failed to set the write buffer: %d.\n", ret);
141+
return -1;
142+
}
143+
144+
write_count = (size + write_buffer_size - 1) / write_buffer_size;
145+
146+
for (uint32_t offset = 0; offset < write_count; offset++) {
147+
148+
memset(write_buffer, 0, write_buffer_size);
149+
// copy binary to write buffer
150+
flash_offset = (offset * write_buffer_size);
151+
size_t bytes_left = size - flash_offset;
152+
size_t chunk = bytes_left < write_buffer_size ? bytes_left : write_buffer_size;
153+
memcpy(write_buffer, &binary[write_buffer_size * offset], chunk);
154+
int ret1 = libtock_app_loader_write(flash_offset, write_buffer_size);
155+
if (ret1 != 0) {
156+
printf("[Error] Failed writing data to flash at address: 0x%lx\n", flash_offset);
157+
printf("[Error] Error nature: %d\n", ret1);
158+
return -1;
159+
}
160+
// wait on write done callback
161+
yield_for(&write_done);
162+
write_done = false;
163+
}
164+
165+
// Now that we are done writing the binary, we ask the kernel to finalize it.
166+
printf("Done writing app, finalizing.\n");
167+
int ret2 = libtock_app_loader_finalize();
168+
if (ret2 != 0) {
169+
printf("[Error] Failed to finalize new process binary.\n");
170+
return -1;
171+
}
172+
yield_for(&finalize_done);
173+
finalize_done = false;
174+
175+
return 0;
176+
}
177+
178+
static void ipc_callback(int pid, int len, int buf, __attribute__ ((unused)) void* ud) {
179+
uint8_t* buffer = (uint8_t*) (uintptr_t) buf;
180+
const char* name_buffer = (const char*) (uintptr_t) buf;
181+
182+
int offset = 0;
183+
int num_binaries = sizeof(binary_sizes) / sizeof(binary_sizes[0]);
184+
185+
if (len < 1) {
186+
// Need at least one byte for the command.
187+
return;
188+
}
189+
190+
uint8_t command = buffer[0];
191+
192+
switch (command) {
193+
case 0:
194+
// Return the number of binaries available
195+
buffer[0] = num_binaries;
196+
ipc_notify_client(pid);
197+
break;
198+
199+
case 1:
200+
// Return the list of binaries to display on the menu
201+
if (len < num_binaries + 1) {
202+
printf("[AppLoader] Returning on Command 0x01\n");
203+
return;
204+
}
205+
206+
for (int i = 0; i < num_binaries; i++) {
207+
size_t name_len = strlen(binary_names[i]);
208+
209+
if ((size_t)(offset + name_len + 1) > (size_t)len) {
210+
printf("[AppLoader] Buffer overflow risk.\n");
211+
return;
212+
}
213+
214+
// Copy the binary name to the buffer
215+
memcpy((void*) &name_buffer[offset], binary_names[i], name_len + 1);
216+
offset += name_len + 1;
217+
}
218+
219+
ipc_notify_client(pid);
220+
break;
221+
222+
case 2:
223+
// install certain app
224+
if (len < 2) {
225+
// app id missing
226+
printf("[AppLoader] Returning on Command 0x02\n");
227+
return;
228+
}
229+
230+
app_id = buffer[1];
231+
int ret = install_binary(app_id);
232+
buffer[0] = ret;
233+
ipc_notify_client(pid);
234+
break;
235+
}
236+
}
237+
238+
239+
int main(void) {
240+
241+
if (!libtock_app_loader_exists()) {
242+
printf("[Error] Failed to detect App Loader Driver!\n");
243+
return -1;
244+
}
245+
246+
// set up the setup done callback
247+
int err1 = libtock_app_loader_subscribe_setup(app_setup_done_callback, NULL);
248+
if (err1 != 0) {
249+
printf("[Error] Failed to set setup done callback: %d\n", err1);
250+
return err1;
251+
}
252+
253+
// set up the write done callback
254+
int err2 = libtock_app_loader_subscribe_write(app_write_done_callback, NULL);
255+
if (err2 != 0) {
256+
printf("[Error] Failed to set flash write done callback: %d\n", err2);
257+
return err2;
258+
}
259+
260+
// set up the finalize done callback
261+
int err3 = libtock_app_loader_subscribe_finalize(app_finalize_done_callback, NULL);
262+
if (err3 != 0) {
263+
printf("[Error] Failed to set finalize done callback: %d\n", err3);
264+
return err3;
265+
}
266+
267+
// set up the load done callback
268+
int err4 = libtock_app_loader_subscribe_load(app_load_done_callback, NULL);
269+
if (err4 != 0) {
270+
printf("[Error] Failed to set load done callback: %d\n", err4);
271+
return err4;
272+
}
273+
274+
ipc_register_service_callback("app_loader", ipc_callback,
275+
NULL);
276+
277+
while (1) {
278+
yield();
279+
}
280+
}

0 commit comments

Comments
 (0)