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.