Skip to content

Commit 5737a48

Browse files
authored
Hook bootable media for amd64/arm64 UEFI + RPi 3/4/5 + Rockchip/Amlogic u-boot (#261)
> "build and write bootable media image to SDCard/eMMC/USBkey/etc, configure kernel params, then use Tinkerbell to provision a separate NVMe/SATA/USB disk, all PXE and Smee-free" - "L3" provisioning for small (and big) machines - **WiP**; **RFC**. - Still missing: - `0README.txt` generation for each variant - Taking `TINK_SERVER`/`TINK_GRPC_PORT`/`TINK_WORKER_IMAGE`/`MAC` vars/params and presetting in grub.cfg/cmdline.txt/extlinux.conf for self-builder's convenience - docs (ofc; at least the self-builder part, user-facing should go into `0README.txt`) - CI (GHA) -- I've this ready, but I'm witholding it to prove the everything else still works without changes - Some refactor of the temporary-Dockerfile for fat32/mtools that is being abused for the u-boot specific `mkimage` --- How to test this? - Build the media - For UEFI-capable amd64 (default 5.10 Hook kernel): `./build.sh bootable-media grub-amd64` - For RaspberryPi 3/4/5 (Armbian kernel): `./build.sh bootable-media rpi` - Write the produced `.img.xz` to media (use eg the "RaspberryPi Imager" software) - Edit `cmdline.txt` / `grub.cfg` / `extlinux.conf` on the media with a text editor, configure your kernel cmdline - Configure your device to boot from your NVMe/SATA disk "first", and the Hook bootable-media device later - Boot the bootable media on your device - To reprovision, cripple your NVMe/SATA disk's boot partition or change next-boot order --- Comments are welcome; pending stuff is coming in the next few weeks.
2 parents bd1acbd + c33caa2 commit 5737a48

15 files changed

+1006
-51
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ hook*.*.yaml
99
!linuxkit-templates
1010
out/
1111
cache/
12+
bootable/
13+
!bash/bootable
1214
*.swp
1315
.idea
1416
kernel/Dockerfile.autogen.*

bash/bootable-media.sh

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
function build_bootable_media() {
2+
log info "would build build_bootable_media: '${*}'"
3+
4+
declare -r -g bootable_id="${1}" # read-only variable from here
5+
6+
declare -g -A bootable_info=()
7+
get_bootable_info_dict "${bootable_id}"
8+
9+
# Dump the bootable_info dict
10+
log info "bootable_info: $(declare -p bootable_info)"
11+
12+
# Get the kernel info from the bootable_info INVENTORY_ID
13+
declare -g -A kernel_info=()
14+
declare -g inventory_id="${bootable_info['INVENTORY_ID']}"
15+
get_kernel_info_dict "${inventory_id}"
16+
log info "kernel_info: $(declare -p kernel_info)"
17+
set_kernel_vars_from_info_dict
18+
kernel_obtain_output_id # sets OUTPUT_ID
19+
20+
# A few scenarios we want to support:
21+
# A) UEFI bootable media; GPT + ESP, FAT32, GRUB, kernel/initrd, grub.conf + some kernel command line.
22+
# B) RPi 3b/4/5 bootable media; GPT, non-ESP partition, FAT32, kernel/initrd, config.txt, cmdline.txt + some kernel command line.
23+
# C) Rockchip bootable media; GPT, non-ESP partition, FAT32, extlinux.conf + some kernel command line; write u-boot bin on top of GPT via Armbian sh
24+
# D) Amlogic bootable media; MBR, FAT32, extlinux.conf + some kernel command line; write u-boot bin on top of MBR via Armbian sh
25+
26+
# General process:
27+
# Obtain extra variables from environment (BOARD/BRANCH for armbian); optional.
28+
# Obtain the latest Armbian u-boot version from the OCI registry, using Skopeo.
29+
# 1) (C/D) Obtain the u-boot artifact binaries using ORAS, given the version above; massage using Docker and extract the binaries.
30+
# 1) (A) Obtain grub somehow; LinuxKit has them ready-to-go in a Docker image.
31+
# 1) (B) Obtain the rpi firmware files (bootcode.bin, start.elf, fixup.dat) from the RaspberryPi Foundation
32+
# 2) Prepare the FAT32 contents; kernel/initrd, grub.conf, config.txt, cmdline.txt, extlinux.conf depending on scenario
33+
# 3) Create a GPT+ESP, GTP+non-ESP, or MBR partition table image with the contents of the FAT32 (use libguestfs)
34+
# 4) For the scenarios with u-boot, write u-boot binaries to the correct offsets in the image.
35+
36+
# @TODO: possibly make sure the kernel and lk is built before delegating?
37+
38+
# Call the bootable build function
39+
declare bootable_build_func="${bootable_info['BOOTABLE_BUILD_FUNC']}"
40+
log info "Calling bootable build function: ${bootable_build_func}"
41+
"${bootable_build_func}"
42+
43+
}
44+
45+
function get_bootable_info_dict() {
46+
declare bootable="${1}"
47+
declare bootable_data_str="${bootable_inventory_dict[${bootable}]}"
48+
if [[ -z "${bootable_data_str}" ]]; then
49+
log error "No bootable data found for '${bootable}'; valid ones are: ${bootable_inventory_ids[*]} "
50+
exit 1
51+
fi
52+
log debug "Bootable data for '${bootable}': ${bootable_data_str}"
53+
declare -g -A bootable_info
54+
eval "bootable_info=(${bootable_data_str})"
55+
56+
# Post process; calculate bash function names given the handler
57+
bootable_info['BOOTABLE_LIST_FUNC']="list_bootable_${bootable_info['HANDLER']}"
58+
bootable_info['BOOTABLE_BUILD_FUNC']="build_bootable_${bootable_info['HANDLER']}"
59+
60+
# Ensure bootable_info a valid TAG
61+
if [[ -z "${bootable_info['TAG']}" ]]; then
62+
log error "No TAG found for bootable '${bootable}'"
63+
exit 1
64+
fi
65+
}
66+
67+
function output_bootable_media() {
68+
declare input_file="${1}"
69+
declare output_fn="${2}"
70+
declare full_output_fn="out/${output_fn}.xz"
71+
72+
# If CARD_DEVICE is set, write the image to the device; otherwise, compress it
73+
if [[ -n "${CARD_DEVICE}" ]]; then
74+
write_image_to_device "${input_file}" "${CARD_DEVICE}"
75+
log info "Wrote image file ${input_file} to device ${CARD_DEVICE}; done."
76+
return 0
77+
fi
78+
79+
# Use pixz to compress the image; use all CPU cores, default compression level
80+
log info "Compressing image file ${input_file} to ${full_output_fn} -- wait..."
81+
pixz -i "${input_file}" -o "${full_output_fn}"
82+
ls -lah "${full_output_fn}"
83+
log info "Compressed image file ${input_file} to ${full_output_fn}"
84+
85+
return 0
86+
}
87+
88+
function write_image_to_device() {
89+
local image_file="${1}"
90+
local device="${2}"
91+
if [[ -b "${device}" && -f "${image_file}" ]]; then
92+
log info "Writing image file ${image_file} to device ${device}"
93+
pv -p -b -r -c -N "dd" "${image_file}" | dd "of=${device}" bs=1M iflag=fullblock oflag=direct status=none
94+
log info "Waiting for fsync()..."
95+
sync
96+
else
97+
if [[ -n ${device} ]]; then
98+
log error "Device ${device} not found or image file ${image_file} not found"
99+
exit 3
100+
fi
101+
fi
102+
}

0 commit comments

Comments
 (0)