Skip to main content

Developer Package

The Developer Package adds the SDK — a cross-toolchain and sysroot — on top of a flashed Starter Package. It is for building your own applications and rebuilding the kernel, without the full-source Distribution Package Yocto build.

Paths below assume STM32MP1-Ecosystem-v5.0.0 on the STM32MP135F-DK.

Install the SDK

Host dependencies, then the self-extracting SDK installer:

sudo apt-get install -y gawk wget git diffstat unzip texinfo gcc-multilib \
chrpath socat cpio python3 python3-pip python3-pexpect \
libssl-dev libgmp-dev libmpc-dev lz4 zstd \
build-essential libncurses-dev libyaml-dev repo \
coreutils bsdmainutils sed curl bc lrzsz corkscrew cvs subversion mercurial \
nfs-common nfs-kernel-server libarchive-zip-perl dos2unix texi2html libxml2-utils
sudo apt install -y python-is-python3

# Allow enough eMMC/SD block-device minor numbers for the partitions
echo 'options mmc_block perdev_minors=16' | sudo tee /etc/modprobe.d/mmc_block.conf

# Unpack and run the SDK installer
cd "$HOME/STM32MPU_workspace/tmp"
tar xvf en.SDK-x86_64-stm32mp1-openstlinux-6.1-yocto-mickledore-mp1-v23.06.21.tar.gz

SDK_DIR="$HOME/STM32MPU_workspace/STM32MP1-Ecosystem-v5.0.0/Developer-Package/SDK"
mkdir -p "$SDK_DIR"
chmod +x stm32mp1-openstlinux-*/sdk/st-image-weston-*-toolchain-*.sh
./stm32mp1-openstlinux-*/sdk/st-image-weston-*-toolchain-*.sh -d "$SDK_DIR"

Enter the SDK environment

The environment script must be sourced once per terminal before cross-compiling. It sets CC, ARCH, CROSS_COMPILE, STRIP and the sysroot:

cd "$HOME/STM32MPU_workspace/STM32MP1-Ecosystem-v5.0.0/Developer-Package"
source SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

echo $ARCH # arm
echo $CROSS_COMPILE # arm-ostl-linux-gnueabi-
$CC --version
echo $OECORE_SDK_VERSION

Sourcing it rewrites PATH and the compiler variables for that shell only. A new terminal, or a build system that spawns a fresh shell, must source it again — forgetting to is why a build suddenly picks up the host's gcc and produces x86 binaries.

Cross-compile an application

A GTK "hello world", built against the SDK sysroot.

gtk_hello_world.c:

#include <gtk/gtk.h>

static void print_hello(GtkWidget *widget, gpointer data)
{
g_print("Hello World\n");
}

static void activate(GtkApplication *app, gpointer user_data)
{
GtkWidget *window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window");
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

GtkWidget *button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_container_add(GTK_CONTAINER(window), button_box);

GtkWidget *button = gtk_button_new_with_label("Hello World");
g_signal_connect(button, "clicked", G_CALLBACK(print_hello), NULL);
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), window);
gtk_container_add(GTK_CONTAINER(button_box), button);

gtk_widget_show_all(window);
}

int main(int argc, char **argv)
{
GtkApplication *app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}

Makefile — note it uses $(CC) and pkg-config from the sourced environment, so it never names the cross-compiler explicitly:

PROG = gtk_hello_world
SRCS = gtk_hello_world.c

CFLAGS += -Wall $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)

all: $(PROG)

$(PROG): $(SRCS)
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)

clean:
rm -f $(PROG) $(patsubst %.c,%.o,$(SRCS))

Build, copy to the board, and run it under the Weston compositor:

make
scp gtk_hello_world root@BOARD_IP_ADDRESS:/usr/local

# On the board
su -l weston -c "/usr/local/gtk_hello_world"

Clicking Hello World closes the window and prints Hello World on the console. The app must run as the weston user; started as root it has no access to the compositor's Wayland socket and fails to open a window.

Rebuild the kernel

Unpack the kernel sources shipped with the Developer Package, apply the ST patches, and configure with the STM32MP fragments:

cd "$HOME/STM32MPU_workspace/STM32MP1-Ecosystem-v5.0.0/Developer-Package"
tar xvf en.SOURCES-stm32mp1-openstlinux-6.1-yocto-mickledore-mp1-v23.06.21.tar.gz

cd stm32mp1-openstlinux-*/sources/arm-ostl-linux-gnueabi/linux-stm32mp-6.1.28-stm32mp-r1-r0
tar xvf linux-6.1.28.tar.xz
cd linux-6.1.28

# Apply the ST patch series
for p in ../*.patch; do patch -p1 < "$p"; done

# merge_config's lexer needs these
sudo apt-get install -y bison flex

make ARCH=arm multi_v7_defconfig "fragment*.config"
for f in ../fragment*.config; do scripts/kconfig/merge_config.sh -m -r .config "$f"; done
yes '' | make ARCH=arm oldconfig

Build the kernel, device trees and modules:

make -j4 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040
make -j4 ARCH=arm modules

mkdir -p "$PWD/install_artifact"
make ARCH=arm INSTALL_MOD_PATH="$PWD/install_artifact" modules_install

LOADADDR=0xC2000040 is the uImage load address for this platform. A uImage target without it fails, because the mkimage wrapper needs to stamp the address into the header.

Deploy to the board

scp arch/arm/boot/uImage root@BOARD_IP_ADDRESS:/boot
scp arch/arm/boot/dts/stm32mp135*.dtb root@BOARD_IP:/boot

# Drop the build/source symlinks before copying modules; they point at host paths
rm install_artifact/lib/modules/6.1.28/build install_artifact/lib/modules/6.1.28/source

# Optional: strip debug info to shrink the modules
find install_artifact/ -name "*.ko" \
| xargs $STRIP --strip-debug --remove-section=.comment --remove-section=.note --preserve-dates

scp -r install_artifact/lib/modules/* root@BOARD_IP:/lib/modules

On the board, rebuild the module dependency database and reboot:

/sbin/depmod -a
sync
reboot

The build and source symlinks under lib/modules/<version> point at the host's build tree. Copied to the board they dangle, and some tools follow them into nothing — remove them before the copy, as above.

Example: patch a built-in driver

Confirm the display driver's probe is silent today:

dmesg | grep -i stm_drm_platform_probe # nothing

Add a log line to ./drivers/gpu/drm/stm/drv.c in stm_drm_platform_probe:

static int stm_drm_platform_probe(struct platform_device *pdev)
{
/* ... */
DRM_INFO("Simple example - %s\n", __func__);
return 0;
/* ... */
}

Rebuild just the uImage, redeploy, reboot:

make -j4 uImage LOADADDR=0xC2000040
scp arch/arm/boot/uImage root@BOARD_IP:/boot
dmesg | grep -i stm_drm_platform_probe
# [ 4.050373] [drm] Simple example - stm_drm_platform_probe

Making a configuration change permanent is a separate step — see Kernel configuration.