Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Overview

This is a collection of scripts that are used to build a Ubuntu 20.04 preinstalled desktop/server image for the Raspberry Pi Zero 2W, 3, 4, and 400.
This is a collection of scripts that build Ubuntu preinstalled desktop/server images for the Raspberry Pi Zero 2W, 3, 4, 5, and 400. The Ubuntu release is set per profile (e.g. 20.04 for legacy, 24.04 or 22.04 for rpi).

![Raspberry Pi 4](https://www.electromaker.io/uploads/images/board-guide/single-board-computer/medium/Raspberry%20Pi%204B-540x386.png)

Expand All @@ -21,9 +21,34 @@ Please install the below packages on your host machine:
sudo apt-get install -y build-essential gcc-aarch64-linux-gnu bison \
qemu-user-static qemu-system-arm qemu-efi u-boot-tools binfmt-support \
debootstrap flex libssl-dev bc rsync kmod cpio xz-utils fakeroot parted \
udev dosfstools uuid-runtime grub-pc
udev dosfstools uuid-runtime grub-pc libgnutls28-dev
```

## Supported platforms

One image works across all of these boards; the correct U-Boot and device tree are chosen at boot via `config.txt`:

| Board | SoC | config.txt section |
|-------------|----------|----------------------|
| Pi Zero 2W | BCM2710A1| `[all]` or `[pi3]` |
| Pi 3 | BCM2837 | `[pi3]` |
| Pi 4 / 400 | BCM2711 | `[pi4]` |
| Pi 5 | BCM2712 | `[pi5]` |

Two build profiles (switch in `scripts/config`):

| Profile | File | Boards |
|------------|------------------------|---------------------------|
| **legacy** | `scripts/config.legacy` | Pi Zero 2W, 3, 4, 400 |
| **rpi** | `scripts/config.rpi` | Pi Zero 2W, 3, 4, 5, 400 |

- **Use legacy:** In `scripts/config`, set `BUILD_PROFILE=legacy` (default). Uses kernel/firmware tag `1.20220830` and U-Boot `v2022.01`.
- **Use RPi (incl. Pi 5):** In `scripts/config`, set `BUILD_PROFILE=rpi`. Uses kernel branch and firmware/U-Boot tags from `scripts/config.rpi`; **UBUNTU_RELEASE** defaults to **noble** (24.04); set to **jammy** in `config.rpi` for 22.04. Pi 5 uses `rpi_arm64_defconfig` (same binary as other 64-bit boards).

Or pass the profile as an argument (recommended; survives sudo): `sudo ./build.sh rpi`

After changing the profile or editing `scripts/config.legacy` / `scripts/config.rpi`, run `sudo ./build.sh` (or remove `build/` and rebuild).

## Building

To checkout the source and build:
Expand Down Expand Up @@ -58,7 +83,7 @@ Password: root
To flash the Ubuntu 20.04 preinstalled image to removable media:

```
xz -dc images/ubuntu-20.04-preinstalled-desktop-arm64-rpi.tar.xz | sudo dd of=/dev/sdX bs=4k
xz -dc images/ubuntu-24.04-preinstalled-desktop-arm64-rpi.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
```

> This assumes that the removable media is added as /dev/sdX and all it’s partitions are unmounted.
7 changes: 6 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi

# Profile (legacy or rpi): use argument so it works with sudo. E.g. sudo ./build.sh rpi
if [ -n "$1" ]; then
export BUILD_PROFILE="$1"
fi

# Build the U-Boot bootloader
./scripts/build-u-boot.sh

Expand All @@ -18,4 +23,4 @@ fi
./scripts/build-rootfs.sh

# Build the Ubuntu preinstalled images
./scripts/build-image.sh
./scripts/build-image.sh
11 changes: 10 additions & 1 deletion scripts/build-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ dtoverlay=dwc2,dr_mode=host

[all]
EOF
# Pi 5 uses rpi_arm64_defconfig (same binary as [all]); add [pi5] for correct DTB
cat >> ${mount_point}/boot/config.txt << EOF

[pi5]
kernel=u-boot-rpi-arm64.bin
device_tree=bcm2712-rpi-5-b.dtb
os_check=0
EOF

# Copy uboot binary
cp u-boot-rpi-3.bin ${mount_point}/boot
cp u-boot-rpi-4.bin ${mount_point}/boot
Expand All @@ -221,4 +230,4 @@ EOF
echo -e "\nCompressing $(basename "${img}.xz")\n"
xz -9 --extreme --force --keep --quiet --threads=0 "${img}"
rm -f "${img}"
done
done
14 changes: 12 additions & 2 deletions scripts/build-kernel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi

cd "$(dirname -- "$(readlink -f -- "$0")")" && cd ..
REPO_ROOT="$(cd "$(dirname -- "$(readlink -f -- "$0")")/.." && pwd)"
[ -f "$REPO_ROOT/scripts/config" ] && source "$REPO_ROOT/scripts/config"

# Kernel ref: use RPI_RELEASE (same as firmware) for compatibility across all platforms
KERNEL_REF="${RPI_RELEASE:-rpi-6.1.y}"

cd "$REPO_ROOT"
mkdir -p build && cd build

# Download the raspberry pi linux kernel source
if [ ! -d linux ]; then
git clone --depth=1 --progress -b 1.20220830 https://github.com/raspberrypi/linux
git clone --depth=1 --progress -b "$KERNEL_REF" https://github.com/raspberrypi/linux
else
git -C linux fetch origin
git -C linux checkout "$KERNEL_REF" 2>/dev/null || git -C linux checkout "origin/$KERNEL_REF" 2>/dev/null || true
git -C linux pull --ff-only origin "$KERNEL_REF" 2>/dev/null || true
fi
cd linux

Expand Down
34 changes: 28 additions & 6 deletions scripts/build-rootfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi

cd "$(dirname -- "$(readlink -f -- "$0")")" && cd ..
REPO_ROOT="$(cd "$(dirname -- "$(readlink -f -- "$0")")/.." && pwd)"
[ -f "$REPO_ROOT/scripts/config" ] && source "$REPO_ROOT/scripts/config"

# Firmware ref: use RPI_FIRMWARE_TAG if set (for Pi 5 use e.g. 1.20240306), else RPI_RELEASE.
# Config profiles (config.legacy / config.rpi) always set these.
FIRMWARE_REF="${RPI_FIRMWARE_TAG:-$RPI_RELEASE}"

cd "$REPO_ROOT"
mkdir -p build && cd build

if [ ! -d linux ]; then
Expand All @@ -18,20 +25,35 @@ fi

# Download the raspberry pi firmware
if [ ! -d firmware ]; then
git clone --depth 1 --progress -b 1.20220830 https://github.com/raspberrypi/firmware.git
git clone --depth 1 --progress -b "$FIRMWARE_REF" https://github.com/raspberrypi/firmware.git
else
if [ -n "$FIRMWARE_REF" ]; then
git -C firmware fetch origin tag "$FIRMWARE_REF" --no-tags 2>/dev/null || git -C firmware fetch origin 2>/dev/null || true
git -C firmware checkout "$FIRMWARE_REF" 2>/dev/null || true
else
git -C firmware pull --ff-only
fi
fi

# These env vars can cause issues with chroot
unset TMP
unset TEMP
unset TMPDIR

# Debootstrap options
# Debootstrap options (UBUNTU_RELEASE from config: focal=20.04, jammy=22.04, noble=24.04)
arch=arm64
release=focal
release="${UBUNTU_RELEASE:-focal}"
mirror=http://ports.ubuntu.com/ubuntu-ports
chroot_dir=rootfs

# Version for output tarball names (e.g. 20.04, 22.04, 24.04)
case "$release" in
focal) UBUNTU_VERSION=20.04 ;;
jammy) UBUNTU_VERSION=22.04 ;;
noble) UBUNTU_VERSION=24.04 ;;
*) UBUNTU_VERSION="${release}" ;;
esac

# Clean chroot dir and make sure folder is not mounted
umount -lf ${chroot_dir}/dev/pts 2> /dev/null || true
umount -lf ${chroot_dir}/* 2> /dev/null || true
Expand Down Expand Up @@ -346,7 +368,7 @@ umount -lf ${chroot_dir}/dev/pts 2> /dev/null || true
umount -lf ${chroot_dir}/* 2> /dev/null || true

# Tar the entire rootfs
cd ${chroot_dir} && XZ_OPT="-0 -T0" tar -cpJf ../ubuntu-20.04-preinstalled-server-arm64-rpi.rootfs.tar.xz . && cd ..
cd ${chroot_dir} && XZ_OPT="-0 -T0" tar -cpJf ../ubuntu-${UBUNTU_VERSION}-preinstalled-server-arm64-rpi.rootfs.tar.xz . && cd ..

# Mount the temporary API filesystems
mkdir -p ${chroot_dir}/{proc,sys,run,dev,dev/pts}
Expand Down Expand Up @@ -378,4 +400,4 @@ umount -lf ${chroot_dir}/dev/pts 2> /dev/null || true
umount -lf ${chroot_dir}/* 2> /dev/null || true

# Tar the entire rootfs
cd ${chroot_dir} && XZ_OPT="-0 -T0" tar -cpJf ../ubuntu-20.04-preinstalled-desktop-arm64-rpi.rootfs.tar.xz . && cd ..
cd ${chroot_dir} && XZ_OPT="-0 -T0" tar -cpJf ../ubuntu-${UBUNTU_VERSION}-preinstalled-desktop-arm64-rpi.rootfs.tar.xz . && cd ..
23 changes: 19 additions & 4 deletions scripts/build-u-boot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi

cd "$(dirname -- "$(readlink -f -- "$0")")" && cd ..
REPO_ROOT="$(cd "$(dirname -- "$(readlink -f -- "$0")")/.." && pwd)"
[ -f "$REPO_ROOT/scripts/config" ] && source "$REPO_ROOT/scripts/config"

cd "$REPO_ROOT"
mkdir -p build && cd build

# Download and build u-boot
# Download and build u-boot (use UBOOT_TAG from scripts/config for a compatible release)
if [ ! -d u-boot ]; then
git clone --progress --depth=1 -b v2022.01 http://git.denx.de/u-boot.git
if [ -n "$UBOOT_TAG" ]; then
git clone --progress --depth=1 -b "$UBOOT_TAG" https://github.com/u-boot/u-boot.git
else
git clone --progress --depth=1 https://github.com/u-boot/u-boot.git
fi
else
if [ -n "$UBOOT_TAG" ]; then
git -C u-boot fetch origin tag "$UBOOT_TAG" --no-tags 2>/dev/null || true
git -C u-boot checkout "$UBOOT_TAG" 2>/dev/null || true
else
git -C u-boot pull --ff-only
fi
fi
cd u-boot

Expand All @@ -39,4 +53,5 @@ make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- rpi_arm64_defconfig

# Compile u-boot binary
make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- -j "$(nproc)"
cp u-boot.bin ../u-boot-rpi-arm64.bin
cp u-boot.bin ../u-boot-rpi-arm64.bin
# Pi 5 uses the same binary (rpi_arm64_defconfig)
7 changes: 7 additions & 0 deletions scripts/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Build profile: "legacy" (Pi 0/3/4/400) or "rpi" (adds Pi 5).
# Switch by setting BUILD_PROFILE or editing the line below.
BUILD_PROFILE="${BUILD_PROFILE:-legacy}"

CONFIG_DIR="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]:-$0}")")"
# shellcheck source=config.legacy
[ -f "$CONFIG_DIR/config.$BUILD_PROFILE" ] && source "$CONFIG_DIR/config.$BUILD_PROFILE"
7 changes: 7 additions & 0 deletions scripts/config.legacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Original build: Pi Zero 2W, Pi 3, Pi 4, Pi 400 (no Pi 5).
# Same kernel/firmware refs as the original ubuntu-raspberry-pi project.

RPI_RELEASE="${RPI_RELEASE:-1.20220830}"
RPI_FIRMWARE_TAG="${RPI_FIRMWARE_TAG:-1.20220830}"
UBOOT_TAG="${UBOOT_TAG:-v2022.01}"
UBUNTU_RELEASE="${UBUNTU_RELEASE:-focal}"
9 changes: 9 additions & 0 deletions scripts/config.rpi
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# RPi build (all 64-bit variants including Pi 5).
# Kernel branch and firmware tag that include Pi 5 (BCM2712) support.
# Pi 5 uses the same U-Boot as other 64-bit boards: rpi_arm64_defconfig (u-boot-rpi-arm64.bin).
# UBUNTU_RELEASE: noble=24.04, jammy=22.04

RPI_RELEASE="${RPI_RELEASE:-rpi-6.12.y}"
RPI_FIRMWARE_TAG="${RPI_FIRMWARE_TAG:-1.20250915}"
UBOOT_TAG="${UBOOT_TAG:-v2026.01}"
UBUNTU_RELEASE="${UBUNTU_RELEASE:-noble}"