Skip to content

Commit a1dddd1

Browse files
committed
Add make-boot-image service
Signed-off-by: Richard Oliver <[email protected]>
1 parent 9b9962e commit a1dddd1

File tree

8 files changed

+321
-1
lines changed

8 files changed

+321
-1
lines changed

host-support/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ RPI_DEVICE_EEPROM_WP_SET=
88
DEVICE_SERIAL_STORE=/usr/local/etc/rpi-sb-provisioner/seen
99
DEMO_MODE_ONLY=
1010
RPI_SB_WORKDIR=
11+
BOOT_IMAGE_VENDOR=
12+
BOOT_IMAGE_MAINTAINER=

host-support/ramdisk_cmdline.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootwait console=tty0 console=serial0,115200 root=/dev/ram0
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[all]
2+
kernel=zImage
3+
arm_64bit=1
4+
initramfs rootfs.cpio.zst
5+
enable_uart=1
6+
uart_2ndstage=1
7+
disable_overscan=1
8+
cmdline=cmdline.txt
9+
10+
[cm4]
11+
dtoverlay=dwc2,dr_mode=host
12+
13+
[none]

host-support/terminal-functions.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,20 @@ get_fastboot_config_file() {
130130
else
131131
echo "/var/lib/rpi-sb-provisioner/boot_ramdisk_config.txt"
132132
fi
133-
}
133+
}
134+
135+
get_internal_config_file() {
136+
if [ -f /etc/rpi-sb-provisioner/ramdisk_internal_config.txt ]; then
137+
echo "/etc/rpi-sb-provisioner/ramdisk_internal_config.txt"
138+
else
139+
echo "/var/lib/rpi-sb-provisioner/ramdisk_internal_config.txt"
140+
fi
141+
}
142+
143+
get_ramdisk_cmdline_file() {
144+
if [ -f /etc/rpi-sb-provisioner/ramdisk_cmdline.txt ]; then
145+
echo "/etc/rpi-sb-provisioner/ramdisk_cmdline.txt"
146+
else
147+
echo "/var/lib/rpi-sb-provisioner/ramdisk_cmdline.txt"
148+
fi
149+
}

make-boot-image/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# make-boot-image
2+
A oneshot service to download the specified Raspberry Pi linux-image- and
3+
create a replacement boot-image- package. This replacement package contains a
4+
signed boot.img with a cryptroot-enabled initramfs. The kernel modules are
5+
retained in the replacement package. Necessary firmware file are inserted into
6+
the signed boot.img where appropriate (via raspi-firmware package).
7+
8+
> [!CAUTION]
9+
> Support only exists for v8 kernels at this time.
10+
11+
## Configuration
12+
- VENDOR
13+
- OPENSSL
14+
- CUSTOMER\_KEY\_FILE\_PEM
15+
16+
## Usage
17+
To create a replacement boot-image- package for linux-image-6.6.31+rpt-rpi-v8
18+
```
19+
systemctl start make-boot-image@$(systemd-escape 6.6.31+rpt-rpi-v8).service
20+
```
21+
22+
To determine the latest v8 linux image (in order to run the service as
23+
suggested above)
24+
```
25+
META_PKG=linux-image-rpi-v8
26+
SRV=rpi-package-download@$(systemd-escape $META_PKG).service
27+
systemctl start --wait $SRV \
28+
&& grep-dctrl -F Package -X $META_PKG -n -s Depends /var/cache/$SRV/latest/Packages \
29+
| grep -o '^[[:graph:]]*'
30+
```
31+
32+
The service makes use of systemd's CacheDirectory during execution. The boot-image- package created by the example given above would typically be found at:
33+
```
34+
/var/cache/[email protected]\x2brpt\x2drpi\x2dv8.service/boot-image-<vendor>-6.6.31+rpt-rpi-v8_6.6.31-1+rpt1_arm64.deb
35+
```
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
# deps:
6+
# - dpkg (dpkg-deb)
7+
# - openssl
8+
# - zstd
9+
# - cpio
10+
11+
. /usr/local/bin/terminal-functions.sh
12+
13+
read_config
14+
15+
TMPDIR="${TMPDIR:=/tmp}"
16+
17+
if [[ -z "${1}" ]]; then
18+
>&2 echo "No linux image specified"
19+
exit 1
20+
fi
21+
22+
if [[ -z "${RPI_DEVICE_FAMILY}" ]]; then
23+
>&2 echo "'RPI_DEVICE_FAMILY' not specified"
24+
exit 1
25+
fi
26+
27+
if [[ -z "${BOOT_IMAGE_VENDOR}" ]]; then
28+
>&2 echo "'BOOT_IMAGE_VENDOR' not specified"
29+
exit 1
30+
fi
31+
32+
if [[ -z "${BOOT_IMAGE_MAINTAINER}" ]]; then
33+
>&2 echo "'BOOT_IMAGE_MAINTAINER' not specified"
34+
exit 1
35+
fi
36+
37+
if [[ -z "${OPENSSL}" || ! -f "${OPENSSL}" ]]; then
38+
>&2 echo "'OPENSSL' not set or binary does not exist"
39+
exit 1
40+
fi
41+
42+
if [[ -z "${CUSTOMER_KEY_FILE_PEM}" || ! -f "${CUSTOMER_KEY_FILE_PEM}" ]]; then
43+
>&2 echo "'CUSTOMER_KEY_FILE_PEM' not set or file does not exist"
44+
exit 1
45+
fi
46+
47+
LINUX_IMAGE="${1}"
48+
49+
# Should be set by systemd
50+
SERVICE_NAME="make-boot-image@$(systemd-escape $LINUX_IMAGE).service"
51+
CACHE_DIRECTORY="${CACHE_DIRECTORY:=/var/cache/${SERVICE_NAME}}"
52+
RUNTUME_DIRECTORY="${RUNTIME_DIRECTORY:=/run/${SERVICE_NAME}}"
53+
54+
# TODO: Might be interesting to start rpi-package-download with --no-block to
55+
# allow multiple simultaneous downloads.
56+
function download_package() {
57+
systemctl start \
58+
--wait \
59+
rpi-package-download@$(systemd-escape ${1}).service
60+
}
61+
62+
KERNEL_2711="linux-image-${LINUX_IMAGE}"
63+
>&2 echo "Downloading ${KERNEL_2711}"
64+
download_package "$KERNEL_2711"
65+
66+
PACKAGE_NAME="boot-image-${BOOT_IMAGE_VENDOR}-${LINUX_IMAGE}"
67+
68+
# Temp directory cleanup
69+
TEMP_DIRS=()
70+
function remove_temp_dirs() {
71+
>&2 echo "Removing temporary directories"
72+
for dir in "${TEMP_DIRS[@]}"
73+
do
74+
rm -rf "$dir"
75+
done
76+
}
77+
trap remove_temp_dirs EXIT
78+
79+
>&2 echo -n "Creating filesystem hierarchy for deb package: "
80+
DEB_HIER="$(mktemp --directory --tmpdir debhier.XXX)"
81+
TEMP_DIRS+=("${DEB_HIER}")
82+
>&2 echo "${DEB_HIER}"
83+
84+
>&2 echo -n "Create rootfs working directory: "
85+
WORK_DIR="$(mktemp --directory --tmpdir boot-image-rootfs.XXX)"
86+
TEMP_DIRS+=("${WORK_DIR}")
87+
>&2 echo "${WORK_DIR}"
88+
89+
function latest_pkg_dir() {
90+
echo "/var/cache/rpi-package-download@$(systemd-escape ${1}).service/latest"
91+
}
92+
93+
>&2 echo "Extracting package contents"
94+
dpkg-deb --raw-extract "$(latest_pkg_dir $KERNEL_2711)/package.deb" "${WORK_DIR}"
95+
96+
function get_dctrl_field() {
97+
grep-dctrl \
98+
--field=Package \
99+
--exact-match "${2}" \
100+
--no-field-names \
101+
--show-field="${3}" \
102+
"${1}"
103+
}
104+
105+
# Determine package version for later reuse
106+
KERNEL_2711_VERSION="$(get_dctrl_field ${WORK_DIR}/DEBIAN/control ${KERNEL_2711} Version)"
107+
>&2 echo "Extracted ${KERNEL_2711}, version ${KERNEL_2711_VERSION}"
108+
109+
# rootfs kernel modules
110+
>&2 echo "Copy kernel modules into deb package"
111+
mkdir -p "${DEB_HIER}/lib/modules"
112+
rsync -crt "${WORK_DIR}/lib/modules/"* "${DEB_HIER}/lib/modules"
113+
114+
>&2 echo -n "Create ramdisk working directory: "
115+
BFS_DIR="$(mktemp --directory --tmpdir boot-image-bootfs.XXX)"
116+
TEMP_DIRS+=("${BFS_DIR}")
117+
>&2 echo "${BFS_DIR}"
118+
119+
# Kernel Images
120+
>&2 echo "Copy kernel to ramdisk"
121+
cp "${WORK_DIR}/boot/vmlinuz-${LINUX_IMAGE}" "${BFS_DIR}/zImage"
122+
123+
# Overlays
124+
>&2 echo "Copy overlays to ramdisk"
125+
OVERLAY_PATH="${WORK_DIR}/usr/lib/${KERNEL_2711}/overlays"
126+
rsync -crt "${OVERLAY_PATH}"/*.dtb* "${OVERLAY_PATH}/README" "${BFS_DIR}/overlays"
127+
128+
# DTBs
129+
>&2 echo "Copy DTBs to ramdisk"
130+
DTB_PATH="${WORK_DIR}/usr/lib/${KERNEL_2711}/broadcom"
131+
rsync -crt "${DTB_PATH}"/bcm27*.dtb "${BFS_DIR}"
132+
133+
# Insert an initramfs
134+
>&2 echo "Add cryptoot initramfs to ramdisk (with necessary kernel modules)"
135+
INITRAMFS_EXTRACT="$(mktemp --directory --tmpdir initramfs-extract.XXX)"
136+
TEMP_DIRS+=("${INITRAMFS_EXTRACT}")
137+
zstd -q -d "$(get_cryptroot)" -o "${INITRAMFS_EXTRACT}/initramfs.cpio"
138+
mkdir -p "${INITRAMFS_EXTRACT}/initramfs"
139+
pushd "${INITRAMFS_EXTRACT}/initramfs" > /dev/null
140+
cpio --quiet -id < ../initramfs.cpio > /dev/null
141+
rm ../initramfs.cpio
142+
pushd "${WORK_DIR}" > /dev/null
143+
find lib/modules \
144+
\( \
145+
-name 'dm-mod.*' \
146+
-o \
147+
-name 'dm-crypt.*' \
148+
-o \
149+
-name 'af_alg.*' \
150+
-o \
151+
-name 'algif_skcipher.*' \
152+
-o \
153+
-name 'libaes.*' \
154+
-o \
155+
-name 'aes_generic.*' \
156+
-o \
157+
-name 'aes-arm64.*' \
158+
\) \
159+
-exec cp -r --parents "{}" "${INITRAMFS_EXTRACT}/initramfs/usr/" \;
160+
popd > /dev/null
161+
find . -print0 | cpio --quiet --null -ov --format=newc > ../initramfs.cpio 2> /dev/null
162+
popd > /dev/null
163+
zstd -q -6 "${INITRAMFS_EXTRACT}/initramfs.cpio" -o "${BFS_DIR}/rootfs.cpio.zst"
164+
165+
# raspi-firmware
166+
>&2 echo "Downloading raspi-firmware"
167+
download_package raspi-firmware
168+
169+
>&2 echo -n "Create temp directory to extract firmware: "
170+
FW_EXTRACT_DIR="$(mktemp --directory --tmpdir boot-image-firmware.XXX)"
171+
TEMP_DIRS+=("${FW_EXTRACT_DIR}")
172+
>&2 echo "${FW_EXTRACT_DIR}"
173+
174+
>&2 echo "Extracting firmware package contents"
175+
dpkg-deb --raw-extract "$(latest_pkg_dir raspi-firmware)/package.deb" "${FW_EXTRACT_DIR}"
176+
177+
>&2 echo "Add firmware to ramdisk"
178+
rsync -v -crt "${FW_EXTRACT_DIR}/usr/lib/raspi-firmware/" "${BFS_DIR}"
179+
180+
# cmdline.txt
181+
>&2 echo "Add cmdline.txt to ramdisk"
182+
cp "$(get_ramdisk_cmdline_file)" "${BFS_DIR}/cmdline.txt"
183+
184+
# Inner config.txt
185+
>&2 echo "Add config.txt to ramdisk"
186+
cp "$(get_internal_config_file)" "${BFS_DIR}/config.txt"
187+
188+
# Invoke make-boot-image
189+
>&2 echo "Finalise ramdisk in deb package (boot.img)"
190+
mkdir -p "${DEB_HIER}/boot/firmware"
191+
make-boot-image \
192+
-b pi${RPI_DEVICE_FAMILY} \
193+
-d "${BFS_DIR}" \
194+
-o "${DEB_HIER}/boot/firmware/boot.img" > /dev/null
195+
196+
# Outer config.txt
197+
>&2 echo "Add config.txt to deb package (ensure boot.img is used)"
198+
cp "$(get_fastboot_config_file)" "${DEB_HIER}/boot/firmware/config.txt"
199+
200+
# boot.sig generation
201+
>&2 echo "Signing ramdisk image"
202+
sha256sum "${DEB_HIER}/boot/firmware/boot.img" | awk '{print $1}' > "${DEB_HIER}/boot/firmware/boot.sig"
203+
echo -n "rsa2048: " >> "${DEB_HIER}/boot/firmware/boot.sig"
204+
${OPENSSL} dgst \
205+
-sign "${CUSTOMER_KEY_FILE_PEM}" \
206+
-keyform PEM \
207+
-sha256 \
208+
"${DEB_HIER}/boot/firmware/boot.img" \
209+
| xxd -c 4096 -p >> "${DEB_HIER}/boot/firmware/boot.sig"
210+
211+
# Insert control file
212+
mkdir "${DEB_HIER}/DEBIAN"
213+
echo \
214+
"Package: ${PACKAGE_NAME}
215+
Source: linux
216+
Version: ${KERNEL_2711_VERSION}
217+
Architecture: arm64
218+
Maintainer: ${BOOT_IMAGE_MAINTAINER}
219+
Section: kernel
220+
Priority: optional
221+
Homepage: https://github.com/raspberrypi/linux/
222+
Provides: ${KERNEL_2711}
223+
Conflicts: ${KERNEL_2711}
224+
Replaces: ${KERNEL_2711}
225+
Description: Repackaged ${KERNEL_2711} for signed/cryptroot boot" \
226+
> "${DEB_HIER}/DEBIAN/control"
227+
228+
# Create Debian package
229+
dpkg-deb --build "${DEB_HIER}" "${CACHE_DIRECTORY}"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[Unit]
2+
Description=Creates a signed boot image using a Raspberry Pi OS kernel / bootloader
3+
4+
[Service]
5+
Type=oneshot
6+
ExecStart=/usr/local/bin/make-boot-image-from-kernel "%I"
7+
CacheDirectory=%n
8+
9+
[Install]
10+
WantedBy=multi-user.target

nfpm.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ depends:
144144
- diffutils
145145
- findutils
146146
- python3-textual
147+
- dpkg
148+
- zstd
147149

148150
# Recommended packages. (overridable)
149151
# This will expand any env var you set in the field, e.g. ${RECOMMENDS_BLA}
@@ -201,6 +203,12 @@ contents:
201203
- src: host-support/fastboot-gadget.img
202204
dst: /var/lib/rpi-sb-provisioner/fastboot-gadget.img
203205

206+
- src: host-support/ramdisk_internal_config.txt
207+
dst: /var/lib/rpi-sb-provisioner/ramdisk_internal_config.txt
208+
209+
- src: host-support/ramdisk_cmdline.txt
210+
dst: /var/lib/rpi-sb-provisioner/ramdisk_cmdline.txt
211+
204212
- src: host-support/make-boot-image
205213
dst: /usr/local/bin/make-boot-image
206214

@@ -225,6 +233,12 @@ contents:
225233
- src: rpi-package-download/rpi-package-download
226234
dst: /usr/local/bin/rpi-package-download
227235

236+
- src: make-boot-image/make-boot-image.service
237+
dst: /usr/local/lib/systemd/system/[email protected]
238+
239+
- src: make-boot-image/make-boot-image-from-kernel
240+
dst: /usr/local/bin/make-boot-image-from-kernel
241+
228242
- src: monitor/monitor.sh
229243
dst: /usr/local/bin/monitor.sh
230244

0 commit comments

Comments
 (0)