Custom embedded Linux distribution for CM5 MI450 based on Yocto/Poky.
- Docker Desktop with WSL2
- VS Code with Dev Containers extension
- At least 250GB free disk space
- 16GB+ RAM recommended
- Clone the repository:
git clone --recurse-submodules https://github.com/yourname/linnie.git
cd linnie- Open in VS Code:
code .Then select "Reopen in Container" when prompted.
Inside the devcontainer, use KAS to build:
# Build the Default image
kas build kas/linnie.yml
# Different Builds
kas build kas/linnie.yml -- -c build core-image-minimal
kas build kas/linnie.yml -- -c build core-image-base
#Build with Specific Recipe to Isolate Issues
kas shell kas/linnie.yml -c "bitbake thermal-overlay"
kas shell kas/linnie.yml -c "bitbake linnie"
# Or build with shell access for debugging
kas shell /workspaces/linnie/kas/linnie.yml
bitbake core-image-base
bitbake core-image-minimalThe build output will be in build/tmp/deploy/images/cm5-mi450-linnie/.
- Show All Current Layers:
bitbake-layers show-layers- Explore to Learn What KAS did:
cat conf/local.conf
cat conf/bblayers.conf
bitbake -e | grep "^MACHINE=" # what machine is set
bitbake-layers show-recipes "*-image-*" # what avail images to build- Building The Image:
bitbake core-image-base # suggested image by KAS
bitbake core-image-minimal # minimal image (smaller)- Clean Specific Recipes or Everything if Build Fails:
bitbake -c cleanall <recipe-name>
bitbake <recipe-name>
bitbake -c cleanall core-image-base
bitbake core-image-base- Check Disk Space:
df -h /yoctolinnie/
├── .devcontainer/ # Dev container configuration
├── conf/
│ └── site.conf # Site-specific build settings
├── kas/
│ └── linnie.yml # KAS build configuration
├── scripts/ # Utility scripts
└── sources/
├── meta-linnie/ # Custom layer (machine, distro, recipes)
├── meta-openembedded/
├── meta-raspberrypi/
└── poky/
- Machine config:
sources/meta-linnie/conf/machine/cm5-mi450-linnie.conf - Distro config:
sources/meta-linnie/conf/distro/linnie.conf - Build settings:
conf/site.conf - KAS config:
kas/linnie.yml
Create recipes in sources/meta-linnie/recipes-*/:
mkdir -p sources/meta-linnie/recipes-apps/myapp
# Add myapp_1.0.bb recipe fileOnce the build completes successfully, your bootable image and related files are located at:
/yocto/build/tmp/deploy/images/raspberrypi5/# View all output files
ls /yocto/build/tmp/deploy/images/raspberrypi5/
# Key files you'll see:
core-image-minimal-raspberrypi5.rootfs.wic.bz2 ← Bootable SD card image (compressed)
core-image-minimal-raspberrypi5.rootfs.wic.bmap ← Block map for fast flashing
core-image-minimal-raspberrypi5.rootfs.manifest ← List of installed packages
Image-raspberrypi5.bin ← Linux kernel
bcm2712-rpi-5-b.dtb ← Device tree blob
bootfiles/ ← Bootloader files (config.txt, etc.)
modules-*.tgz ← Kernel modules archive# Find the bootable image
ls /yocto/build/tmp/deploy/images/raspberrypi5/*.wic.bz2
# Example output:
# /yocto/build/tmp/deploy/images/raspberrypi5/core-image-minimal-raspberrypi5.rootfs-20260131184059.wic.bz2
# /yocto/build/tmp/deploy/images/raspberrypi5/core-image-minimal-raspberrypi5.rootfs.wic.bz2 -> (symlink to timestamped file)
# Check image size
ls -lh /yocto/build/tmp/deploy/images/raspberrypi5/*.wic.bz2
# Expected size: ~40-60MB compressed (minimal), ~100-150MB (base), larger (custom)# See what's included in your image
cat /yocto/build/tmp/deploy/images/raspberrypi5/core-image-minimal-raspberrypi5.rootfs.manifest
# You'll see packages like:
# - linux-raspberrypi (kernel)
# - busybox (core utilities)
# - systemd (init system)
# - dropbear (SSH server)
# - base-files, util-linux, etc.You have several options for flashing your image to a microSD card:
Using dd (traditional method):
# 1. Decompress the image
cd /yocto/build/tmp/deploy/images/raspberrypi5/
bunzip2 -k core-image-minimal-raspberrypi5.rootfs.wic.bz2
# Creates: core-image-minimal-raspberrypi5.rootfs.wic
# 2. Insert SD card and find device name
lsblk
# Look for your SD card (e.g., /dev/sdb or /dev/mmcblk0)
# 3. Flash to SD card
# WARNING: This ERASES the SD card! Double-check the device name!
sudo dd if=core-image-minimal-raspberrypi5.rootfs.wic \
of=/dev/sdX \
bs=4M \
status=progress \
&& sync
# Replace /dev/sdX with your actual SD card deviceUsing bmaptool (faster, recommended):
# Flash compressed image directly (no need to decompress)
sudo bmaptool copy \
core-image-minimal-raspberrypi5.rootfs.wic.bz2 \
/dev/sdX
# bmaptool only writes used blocks, making it much faster than ddStep 1: Copy image to Windows
# From DevContainer terminal
cp /yocto/build/tmp/deploy/images/raspberrypi5/core-image-minimal-raspberrypi5.rootfs.wic.bz2 \
/workspaces/linnie/
# The file is now accessible at:
# C:\Users\YourName\path\to\linnie\core-image-minimal-raspberrypi5.rootfs.wic.bz2Step 2: Decompress
- Right-click the
.wic.bz2file - Use 7-Zip or similar to extract
- You'll get a
.wicfile
Step 3: Flash using Raspberry Pi Imager
- Download Raspberry Pi Imager
- Open Raspberry Pi Imager
- Choose "Use custom" image
- Select your
.wicfile - Choose your SD card
- Click "Write"
Alternative: Win32DiskImager
- Download Win32DiskImager
- Select your
.wicfile - Select SD card drive
- Click "Write"
# Copy image to remote machine via SCP
scp /yocto/build/tmp/deploy/images/raspberrypi5/core-image-minimal-raspberrypi5.rootfs.wic.bz2 \
user@remotemachine:/tmp/
# SSH to remote machine
ssh user@remotemachine
# Flash from remote machine
cd /tmp
bunzip2 core-image-minimal-raspberrypi5.rootfs.wic.bz2
sudo dd if=core-image-minimal-raspberrypi5.rootfs.wic of=/dev/sdX bs=4M status=progress && sync- Insert SD card into CM5 carrier board
- Connect peripherals (if testing):
- HDMI cable to monitor (optional)
- USB keyboard (optional)
- Ethernet cable (optional, for network access)
- Serial console cable (optional, for debugging)
- Apply power (5V, 3A recommended)
The system should boot in 15-30 seconds. You'll see:
- Bootloader messages
- Kernel boot messages
- systemd starting services
- Login prompt
Login: root
Password: (just press Enter - no password)
Note: Empty root password and SSH access are enabled because the image was built with debug-tweaks feature. Do not use this in production!
# Check kernel version
uname -a
# Check system info
cat /etc/os-release
# Check available disk space
df -h
# Check running services
systemctl status
# Check network interfaces
ip addr show
# Test SSH (if Ethernet connected)
# From your PC: ssh root@<cm5-ip-address>Now that you've successfully built core-image-minimal, you can build the fuller core-image-base which includes:
- Package management tools
- More networking utilities
- Development tools
- Additional system utilities
# Exit current shell (if in one)
exit
# Enter KAS shell
kas shell /workspaces/linnie/kas/linnie.yml
# Build core-image-base
bitbake core-image-base
# Expected build time: 30-60 minutes
# Most packages are already built and cached, so this is much faster!Or change the target in kas/linnie.yml:
# Edit kas/linnie.yml
target: core-image-base # Changed from core-image-minimalThen rebuild:
kas build /workspaces/linnie/kas/linnie.ymlEdit your image recipe or create a custom one:
# Create a custom image recipe
mkdir -p /workspaces/linnie/sources/meta-linnie/recipes-core/images
nano /workspaces/linnie/sources/meta-linnie/recipes-core/images/linnie-image.bbExample custom image:
SUMMARY = "Custom Linnie Image for CM5"
LICENSE = "MIT"
inherit core-image
# Include everything from core-image-base
require recipes-core/images/core-image-base.bb
# Add custom packages
IMAGE_INSTALL:append = " \
htop \
vim \
tmux \
git \
python3 \
python3-pip \
i2c-tools \
spi-tools \
"
# Add extra features
IMAGE_FEATURES:append = " \
package-management \
ssh-server-dropbear \
"
# Set root password (for production)
# EXTRA_USERS_PARAMS = "usermod -P 'yourpassword' root;"Then build your custom image:
bitbake linnie-image# Create kernel modification
mkdir -p /workspaces/linnie/sources/meta-linnie/recipes-kernel/linux/files
# Add device tree overlay
nano /workspaces/linnie/sources/meta-linnie/recipes-kernel/linux/files/my-overlay.dts
# Add kernel bbappend
nano /workspaces/linnie/sources/meta-linnie/recipes-kernel/linux/linux-raspberrypi_%.bbappendExample bbappend:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " \
file://my-overlay.dts \
"
do_compile:append() {
# Compile device tree overlay
dtc -@ -I dts -O dtb -o ${B}/my-overlay.dtbo ${WORKDIR}/my-overlay.dts
}
do_deploy:append() {
install -d ${DEPLOYDIR}/overlays
install -m 0644 ${B}/my-overlay.dtbo ${DEPLOYDIR}/overlays/
}# Clean a specific package
bitbake -c cleanall linux-raspberrypi
# Rebuild
bitbake linux-raspberrypi# Remove build artifacts (keeps downloads and sstate)
rm -rf /yocto/build/tmp
# Rebuild
bitbake core-image-minimal# WARNING: This deletes all Docker volumes!
exit # Exit container first
# In VS Code terminal (outside container)
docker volume rm linnie-yocto-build
docker volume rm linnie-yocto-workspace
# Keep downloads and sstate to speed up rebuild:
# docker volume rm linnie-yocto-downloads
# docker volume rm linnie-yocto-sstate
# Rebuild container
# F1 → "Dev Containers: Rebuild Container"# Build time per recipe
cat /yocto/build/tmp/buildstats/*/build_stats
# Disk usage breakdown
du -sh /yocto/downloads/ # Source tarballs
du -sh /yocto/sstate-cache/ # Compiled cache
du -sh /yocto/build/tmp/ # Build artifactsOut of disk space:
- Increase Docker disk allocation (Docker Desktop → Settings → Resources)
- Clean old builds:
rm -rf /yocto/build/tmp
Build fails after host update:
- Rebuild container:
F1→ "Dev Containers: Rebuild Container"
Package fetch fails:
- Check network:
ping github.com - Check DNS:
cat /etc/resolv.conf - Retry:
bitbake <recipe-name>
Permission errors:
sudo chown -R vscode:vscode /yocto- instead of manually creating toolbox via source oe-init-build-env, KAS can run overall bitbake commands, pull correct repositories, and create my dev env
- KAS will no longer need my submodules for poky, openembedded, and BSP layers as submodules (submodules checkout specific commits (detached HEAD), KAS expects to manage branches and update repos, but KAS can't find branch "scarthgap" with the detached HEAD)
- Opening a project from Windows into a DevContainer mounts it as an NTFS (9p) filesystem, which breaks Yocto due to lack of case sensitivity, poor symlink/hard-link support, and slow I/O. The fix was to keep editable source files on the Windows-mounted workspace but move Yocto-generated files (build, downloads, sstate, cloned layers) onto Linux-backed Docker volumes (or fully into WSL2’s ext4 filesystem). I chose to do this instead of moving all to WSL since im broke (no build machine) AND this makes it more production like so anyone can just clone and use and not have to clone into their wsl (this may just be a persional bias as I rarely do this).
- PR is commit-based lol
For more information: