mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 09:13:01 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea315bdb10 | ||
|
|
3f23aa92be | ||
|
|
4984d5ac7f | ||
|
|
d945bb703b | ||
|
|
b047bd2a52 | ||
|
|
efc97f6566 | ||
|
|
39f9c1e166 | ||
|
|
f81de1d3ec | ||
|
|
e7fabfc37d | ||
|
|
956e604dcb | ||
|
|
9d3669b647 | ||
|
|
17a3ca58a0 | ||
|
|
7e2d0cf2f8 |
@@ -1,11 +1,21 @@
|
||||
|
||||
menu "Images"
|
||||
|
||||
comment "DDI Based"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi-disk/Config.in"
|
||||
|
||||
comment "ITB Based"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rootfs/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-aux/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-qcow/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-gns3a/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rauc/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-dl-release/Config.in"
|
||||
|
||||
comment "General"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-barebox-esp/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-lvm-stub/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-readme/Config.in"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#Simple dracut config for a system without systemd
|
||||
|
||||
#Dracut configuration
|
||||
|
||||
show_modules=yes
|
||||
i18n_install_all=no
|
||||
lvmconf=no
|
||||
mdadmconf=no
|
||||
early_microcode=no
|
||||
hostonly=no
|
||||
hostonly_cmdline=no
|
||||
use_fstab=no
|
||||
kernel_cmdline="rd.break=initqueue"
|
||||
do_strip=no
|
||||
|
||||
# Dracut modules needed
|
||||
add_dracutmodules+=" \
|
||||
busybox \
|
||||
"
|
||||
|
||||
# Modules to ignore
|
||||
omit_dracutmodules+=" \
|
||||
base \
|
||||
bash \
|
||||
biosdevname \
|
||||
btrfs \
|
||||
bluetooth \
|
||||
caps \
|
||||
cifs \
|
||||
connman \
|
||||
crypt \
|
||||
crypt-gpg \
|
||||
dash \
|
||||
dbus-broker \
|
||||
dbus-daemon \
|
||||
dm \
|
||||
dmraid \
|
||||
dmsquash-live-ntfs \
|
||||
dracut-systemd \
|
||||
fcoe \
|
||||
fcoe-uefi \
|
||||
fs-lib \
|
||||
iscsi \
|
||||
i18n \
|
||||
kernel-modules \
|
||||
kernel-modules-extra \
|
||||
lvmmerge \
|
||||
lvm \
|
||||
lunmask \
|
||||
mdraid \
|
||||
memstrack \
|
||||
mksh \
|
||||
modsign \
|
||||
mount-root \
|
||||
multipath \
|
||||
nbd \
|
||||
network-legacy \
|
||||
network-wicked \
|
||||
nfs \
|
||||
nvdimm \
|
||||
nvmf \
|
||||
parse-lunmask \
|
||||
pcsc \
|
||||
qemu \
|
||||
qemu-net \
|
||||
resume \
|
||||
rootfs-block \
|
||||
rngd \
|
||||
shutdown \
|
||||
systemd \
|
||||
systemd-udevd \
|
||||
systemd-coredump \
|
||||
systemd-ask-password \
|
||||
systemd-timedated \
|
||||
systemd-rfkill \
|
||||
systemd-resolved \
|
||||
systemd-hostnamed \
|
||||
systemd-initrd \
|
||||
systemd-integritysetup \
|
||||
systemd-journald \
|
||||
systemd-ldconfig \
|
||||
systemd-networkd \
|
||||
systemd-timesyncd \
|
||||
systemd-veritysetup \
|
||||
systemd-modules-load \
|
||||
systemd-pcrphase \
|
||||
systemd-portabled \
|
||||
systemd-pstore \
|
||||
systemd-sysctl \
|
||||
systemd-repart \
|
||||
systemd-sysext \
|
||||
systemd-sysusers \
|
||||
systemd-tmpfiles \
|
||||
squash \
|
||||
ssh-client \
|
||||
stratis \
|
||||
tpm2-tss \
|
||||
udev-rules \
|
||||
url-lib \
|
||||
usrmount \
|
||||
virtiofs \
|
||||
"
|
||||
@@ -0,0 +1,6 @@
|
||||
config IMAGE_BAREBOX_ESP
|
||||
bool "Barebox EFI System Partition"
|
||||
depends on BR2_TARGET_BAREBOX
|
||||
default y if IMAGE_DDI
|
||||
help
|
||||
An EFI System Partition (ESP) with Barebox installed.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "$BR2_ARCH" in
|
||||
x86_64)
|
||||
BOOT_EFI=BOOTX64.EFI
|
||||
;;
|
||||
*)
|
||||
echo "Unknown EFI boot path for $BR2_ARCH" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "${WORKDIR}"/root
|
||||
rm -rf "${WORKDIR}"/tmp
|
||||
mkdir -p "${WORKDIR}"/tmp
|
||||
|
||||
cat <<EOF >"${WORKDIR}"/genimage.cfg
|
||||
image barebox-esp.vfat {
|
||||
size = "16M"
|
||||
vfat {
|
||||
file EFI/BOOT/$BOOT_EFI {
|
||||
image = $BINARIES_DIR/barebox.efi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Silence genimage warnings
|
||||
config {}
|
||||
EOF
|
||||
|
||||
genimage \
|
||||
--tmppath "${WORKDIR}"/tmp \
|
||||
--rootpath "${WORKDIR}"/root \
|
||||
--inputpath "${WORKDIR}" \
|
||||
--outputpath "${BINARIES_DIR}" \
|
||||
--config "${WORKDIR}"/genimage.cfg
|
||||
@@ -0,0 +1,7 @@
|
||||
################################################################################
|
||||
#
|
||||
# image-barebox-esp
|
||||
#
|
||||
################################################################################
|
||||
|
||||
$(eval $(ix-image))
|
||||
@@ -0,0 +1,8 @@
|
||||
menuconfig IMAGE_DDI_DISK
|
||||
bool "LVM2 based provisioning disk (DDI)"
|
||||
depends on IMAGE_BAREBOX_ESP
|
||||
depends on IMAGE_DDI
|
||||
help
|
||||
Compose a full disk image consisting of an EFI System
|
||||
Partition (ESP) containing Barebox, and an LVM PV in which
|
||||
Infix is installed as an LV.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
mkdir -p "${WORKDIR}"/root
|
||||
rm -rf "${WORKDIR}"/tmp
|
||||
mkdir -p "${WORKDIR}"/tmp
|
||||
|
||||
"${BR2_EXTERNAL_INFIX_PATH}"/utils/lvm-mkinternal \
|
||||
"${BINARIES_DIR}"/"${ARTIFACT}".raw >"${WORKDIR}"/internal.lvm
|
||||
|
||||
cat <<EOF >"${WORKDIR}"/genimage.cfg
|
||||
|
||||
image ${ARTIFACT}.disk {
|
||||
hdimage {
|
||||
partition-table-type = "gpt"
|
||||
}
|
||||
|
||||
partition esp {
|
||||
partition-type-uuid = "esp"
|
||||
image = "$BINARIES_DIR/barebox-esp.vfat"
|
||||
}
|
||||
|
||||
partition esp-backup {
|
||||
partition-type-uuid = "esp"
|
||||
image = "$BINARIES_DIR/barebox-esp.vfat"
|
||||
}
|
||||
|
||||
partition internal {
|
||||
growfs = "true"
|
||||
partition-type-uuid = "lvm"
|
||||
image = "internal.lvm"
|
||||
}
|
||||
}
|
||||
|
||||
# Silence genimage warnings
|
||||
config {}
|
||||
EOF
|
||||
|
||||
genimage \
|
||||
--tmppath "${WORKDIR}"/tmp \
|
||||
--rootpath "${WORKDIR}"/root \
|
||||
--inputpath "${WORKDIR}" \
|
||||
--outputpath "${BINARIES_DIR}" \
|
||||
--config "${WORKDIR}"/genimage.cfg
|
||||
@@ -0,0 +1,9 @@
|
||||
################################################################################
|
||||
#
|
||||
# image-ddi-disk
|
||||
#
|
||||
################################################################################
|
||||
|
||||
IMAGE_DDI_DISK_DEPENDENCIES := image-barebox-esp image-ddi
|
||||
|
||||
$(eval $(ix-image))
|
||||
@@ -0,0 +1,33 @@
|
||||
menuconfig IMAGE_DDI
|
||||
bool "Discoverable Disk Image (DDI)"
|
||||
select BR2_TARGET_ROOTFS_SQUASHFS
|
||||
select BR2_PACKAGE_HOST_GENIMAGE
|
||||
help
|
||||
Create a Discoverable Disk Image containing:
|
||||
- SquashFS of the root filesystem
|
||||
- dm-verity hash tree of the root filesystem
|
||||
- DDI compliant JSON signature object of the hash tree
|
||||
|
||||
https://uapi-group.org/specifications/specs/discoverable_disk_image/
|
||||
|
||||
config IMAGE_DDI_KEY
|
||||
string "signing key"
|
||||
depends on IMAGE_DDI
|
||||
default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.key"
|
||||
help
|
||||
Path to the private RSA key, in PKCS#8 format, used to sign
|
||||
the dm-verity hash tree.
|
||||
|
||||
config IMAGE_DDI_CERT
|
||||
string "signing cert"
|
||||
depends on IMAGE_DDI
|
||||
default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.crt"
|
||||
help
|
||||
Path to the X509 certificate whose associated public key can
|
||||
validate the authenticity of the generated signature.
|
||||
|
||||
The SHA256 fingerprint of this certificate is recorded
|
||||
alongside the standard PKCS#7 signature in the JSON object,
|
||||
which can be useful to locate corresponding key on systems
|
||||
that lack full X509 parsing capabilities.
|
||||
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Generate the DDI in two phases. First we create the verity hash tree
|
||||
# and signature, then we collect them together with the rootfs in the
|
||||
# ddi. The reason for this is that genimage (at least up to v19) can
|
||||
# not infer the size of a partition if the image to be placed in it is
|
||||
# created by the same genimage instance. With the two-phase approach,
|
||||
# we work around that problem and can create a DDI without any wasted
|
||||
# space.
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf "${WORKDIR}"/tmp
|
||||
mkdir -p "${WORKDIR}"/tmp "${WORKDIR}"/verity
|
||||
|
||||
cat <<EOF >"${WORKDIR}"/genimage-verity.cfg
|
||||
image rootfs.verity {
|
||||
verity {
|
||||
image = "rootfs.squashfs"
|
||||
}
|
||||
}
|
||||
|
||||
image rootfs.verity-sig {
|
||||
verity-sig {
|
||||
image = "rootfs.verity"
|
||||
cert = "${CERT}"
|
||||
key = "${KEY}"
|
||||
}
|
||||
}
|
||||
|
||||
config {}
|
||||
EOF
|
||||
|
||||
genimage \
|
||||
--loglevel 1 \
|
||||
--tmppath "${WORKDIR}"/tmp \
|
||||
--rootpath "${WORKDIR}" \
|
||||
--inputpath "${BINARIES_DIR}" \
|
||||
--outputpath "${WORKDIR}"/verity \
|
||||
--config "${WORKDIR}"/genimage-verity.cfg
|
||||
|
||||
case "${BR2_ARCH}" in
|
||||
"x86_64")
|
||||
arch=x86-64
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: missing mapping from ${BR2_ARCH} to genimage arch" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
cat <<EOF >"${WORKDIR}"/genimage-ddi.cfg
|
||||
image ${ARTIFACT}.raw {
|
||||
hdimage {
|
||||
partition-table-type = "gpt"
|
||||
}
|
||||
|
||||
partition root {
|
||||
partition-type-uuid = "root-${arch}"
|
||||
image = "rootfs.squashfs"
|
||||
}
|
||||
partition root-verity {
|
||||
partition-type-uuid = "root-${arch}-verity"
|
||||
image = "${WORKDIR}/verity/rootfs.verity"
|
||||
}
|
||||
partition root-verity-sig {
|
||||
partition-type-uuid = "root-${arch}-verity-sig"
|
||||
image = "${WORKDIR}/verity/rootfs.verity-sig"
|
||||
}
|
||||
}
|
||||
|
||||
config {}
|
||||
EOF
|
||||
|
||||
genimage \
|
||||
--loglevel 1 \
|
||||
--tmppath "${WORKDIR}"/tmp \
|
||||
--rootpath "${WORKDIR}" \
|
||||
--inputpath "${BINARIES_DIR}" \
|
||||
--outputpath "${BINARIES_DIR}" \
|
||||
--config "${WORKDIR}"/genimage-ddi.cfg
|
||||
@@ -0,0 +1,32 @@
|
||||
image rootfs.verity {
|
||||
verity {
|
||||
image = "rootfs.squashfs"
|
||||
}
|
||||
}
|
||||
|
||||
image rootfs.verity-sig {
|
||||
verity-sig {
|
||||
image = "rootfs.verity"
|
||||
cert = "@CERT@"
|
||||
key = "@KEY@"
|
||||
}
|
||||
}
|
||||
|
||||
image @ARTIFACT@.raw {
|
||||
hdimage {
|
||||
partition-table-type = "gpt"
|
||||
}
|
||||
|
||||
partition root {
|
||||
partition-type-uuid = "root-@ARCH@"
|
||||
image = "rootfs.squashfs"
|
||||
}
|
||||
partition root-verity {
|
||||
partition-type-uuid = "root-@ARCH@-verity"
|
||||
image = "rootfs.verity"
|
||||
}
|
||||
partition root-verity-sig {
|
||||
partition-type-uuid = "root-@ARCH@-verity-sig"
|
||||
image = "rootfs.verity-sig"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
################################################################################
|
||||
#
|
||||
# image-ddi
|
||||
#
|
||||
################################################################################
|
||||
|
||||
IMAGE_DDI_DEPENDENCIES := host-genimage rootfs-squashfs
|
||||
IMAGE_DDI_CONFIG_VARS := KEY CERT
|
||||
|
||||
$(eval $(ix-image))
|
||||
@@ -0,0 +1,15 @@
|
||||
tmpfs /tmp tmpfs mode=0755,nosuid,nodev 0 0
|
||||
tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
|
||||
tmpfs /media tmpfs mode=1755,nosuid,nodev 0 0
|
||||
tmpfs /mnt/tmp tmpfs defaults 0 0
|
||||
|
||||
# Locate and mount /cfg, /home, /root and /var from the internal
|
||||
# volume, wherever that is located - or, as a last resort, setup tmpfs
|
||||
# mounts at these points. Setup an ephemeral writable overlay on /etc.
|
||||
/usr/libexec/infix/mnt# /mnt/rw helper none 0 0
|
||||
|
||||
# Optional, nice-to-have mounts
|
||||
debugfs /sys/kernel/debug debugfs nofail 0 0
|
||||
tracefs /sys/kernel/tracing tracefs nofail 0 0
|
||||
cfgfs /config configfs nofail,noauto 0 0
|
||||
host /mnt/host 9p nofail,noauto,cache=none,msize=16384 0 0
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
#!/bin/sh
|
||||
# Called from /etc/fstab to ensure we have something writable mounted
|
||||
# at /cfg, /home, /root, and /var.
|
||||
#
|
||||
# In the normal case, the "rw" LV from the "internal" LVM VG is used
|
||||
# to back all persistant storage. In the event that "internal" is
|
||||
# present, but the "rw" LV is missing, one will be provisioned, along
|
||||
# with the required BTRFS subvolumes.
|
||||
#
|
||||
# In all other scenarios where the subvolumes are not available, the
|
||||
# system will fall back to temporary storage for all mount points.
|
||||
#
|
||||
# For /etc, a writable, tmpfs backed, layer is always overlayed on top
|
||||
# of the read-only contents from the OS image.
|
||||
|
||||
set -e
|
||||
|
||||
nm=$(basename "$0")
|
||||
need_restore=
|
||||
code=0
|
||||
|
||||
logmsg()
|
||||
{
|
||||
logger -k -t "$nm" -p user.crit "$@"
|
||||
}
|
||||
|
||||
status()
|
||||
{
|
||||
GREEN="$(printf '\033[1;32m')"
|
||||
RED="$(printf '\033[1;31m')"
|
||||
YELLOW="$(printf '\033[1;33m')"
|
||||
BOLD="$(printf '\033[1m')"
|
||||
RESET="$(printf '\033[0m')"
|
||||
|
||||
case $1 in
|
||||
0) color=$GREEN; text=' OK ' ;;
|
||||
1) color=$RED; text='FAIL' ;;
|
||||
2) color=$YELLOW;text='WARN' ;;
|
||||
*) color=$YELLOW;text=' ⋯ ' ;;
|
||||
esac
|
||||
|
||||
printf '%s[%s%s%s%s]%s ' "$BOLD" "$color" "$text" "$RESET" "$BOLD" "$RESET"
|
||||
}
|
||||
|
||||
print_start()
|
||||
{
|
||||
printf '\r\033[K%s%s' "$(status 3)" "$*" > /dev/console
|
||||
need_restore=YES
|
||||
}
|
||||
|
||||
print_end()
|
||||
{
|
||||
rc=$1; shift
|
||||
if [ $# -gt 0 ]; then
|
||||
printf '\r\033[K%s%s\n' "$(status "$rc")" "$*" > /dev/console
|
||||
else
|
||||
printf '\r%s\n' "$(status "$rc")" > /dev/console
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore Finit's original progress message so its [ OK ] appears correctly
|
||||
print_restore()
|
||||
{
|
||||
[ "$need_restore" ] || return 0
|
||||
print_start "Mounting filesystems from /etc/fstab"
|
||||
}
|
||||
|
||||
# # External button or bootloader changed kernel command line
|
||||
# check_factory()
|
||||
# {
|
||||
# if [ -f /mnt/cfg/infix/.reset ]; then
|
||||
# return 0;
|
||||
# fi
|
||||
|
||||
# if grep -q 'finit.cond=factory-reset' /proc/cmdline; then
|
||||
# return 0;
|
||||
# fi
|
||||
|
||||
# # Add to your br2-external to extend factory-reset check
|
||||
# if [ ! -x /usr/libexec/infix/check-factory ]; then
|
||||
# return 1;
|
||||
# fi
|
||||
|
||||
# /usr/libexec/infix/check-factory
|
||||
# }
|
||||
|
||||
# factory_reset()
|
||||
# {
|
||||
# find /sys/class/leds/ -type l -exec sh -c 'echo 100 > $0/brightness' {} \;
|
||||
# print_start "Resetting to factory defaults."
|
||||
# logger $opt -p user.notice -t "$nm" "Resetting to factory defaults."
|
||||
|
||||
# rm -rf /mnt/cfg/* /mnt/var/*
|
||||
# sync
|
||||
|
||||
# logger $opt -p user.notice -t "$nm" "Factory reset complete."
|
||||
# print_end 0 "Factory reset complete."
|
||||
# print_restore
|
||||
# }
|
||||
|
||||
subvols="cfg var root home"
|
||||
|
||||
internal_rw_provision()
|
||||
{
|
||||
print_start "Provisioning persistent storage on internal volume"
|
||||
|
||||
lvm lvcreate --name rw --extents 50%FREE internal || {
|
||||
logmsg "No rw LV available, nor could it be created"
|
||||
print_end 1
|
||||
return 1
|
||||
}
|
||||
|
||||
mkfs.btrfs -L rw /dev/internal/rw || {
|
||||
logmsg "Could not create BTRFS on rw LV"
|
||||
print_end 1
|
||||
return 1
|
||||
}
|
||||
|
||||
mount -t btrfs /dev/internal/rw /mnt/rw || {
|
||||
logmsg "Failed to mount rw"
|
||||
print_end 1
|
||||
return 1
|
||||
}
|
||||
|
||||
for subvol in $subvols; do
|
||||
btrfs subvolume create /mnt/rw/@"$subvol" || {
|
||||
logmsg "Failed to create @$subvol on rw"
|
||||
print_end 1
|
||||
return 1
|
||||
}
|
||||
done
|
||||
|
||||
umount /mnt/rw || {
|
||||
logmsg "Failed to unmount rw"
|
||||
print_end 1
|
||||
return 1
|
||||
}
|
||||
|
||||
print_end 0
|
||||
}
|
||||
|
||||
internal_rw_activate()
|
||||
{
|
||||
lvm vgs internal || {
|
||||
logmsg "No internal volume available"
|
||||
return 1
|
||||
}
|
||||
|
||||
lvm vgchange -ay internal || {
|
||||
logmsg "Unable to bring internal volume online"
|
||||
return 1
|
||||
}
|
||||
|
||||
# The only condition under which we do BTRFS provisioning is when
|
||||
# the LV is absent. In all other failure scenarios: fall back to
|
||||
# tmpfs and let the user sort it out. This way we do not risk any
|
||||
# loss of data.
|
||||
[ -b /dev/internal/rw ] && return 0
|
||||
|
||||
internal_rw_provision
|
||||
}
|
||||
|
||||
internal_rw_mount()
|
||||
{
|
||||
internal_rw_activate || return 1
|
||||
|
||||
for subvol in $subvols; do
|
||||
mount -t btrfs /dev/internal/rw /"$subvol" -o subvol=@"$subvol" || {
|
||||
logmsg "Failed to mount /$subvol"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
tmpfs_rw_mount()
|
||||
{
|
||||
for subvol in $subvols; do
|
||||
mkdir -p -m 0755 /mnt/tmp/"$subvol"
|
||||
mount /mnt/tmp/"$subvol" /"$subvol" -o bind || {
|
||||
logmsg "Failed to mount temporary /$subvol"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
tmpfs_etc_mount()
|
||||
{
|
||||
mkdir -p -m 0755 /mnt/tmp/etc/
|
||||
tar c -C /etc . | tar x -C /mnt/tmp/etc
|
||||
mount /mnt/tmp/etc /etc -o bind
|
||||
}
|
||||
|
||||
exec >/mnt/tmp/mnt.log 2>&1
|
||||
|
||||
tmpfs_etc_mount || {
|
||||
code=1
|
||||
logmsg "Failed to make /etc writable"
|
||||
}
|
||||
|
||||
if ! internal_rw_mount; then
|
||||
code=1
|
||||
print_end 1 "Mounting persistent storage from internal volume"
|
||||
logmsg "Internal volume not found, falling back to tmpfs"
|
||||
|
||||
print_start "Falling back to ephemeral tmpfs storage!"
|
||||
tmpfs_rw_mount
|
||||
print_end $?
|
||||
fi
|
||||
|
||||
print_restore
|
||||
exit $code
|
||||
@@ -0,0 +1,8 @@
|
||||
config IMAGE_LVM_STUB
|
||||
bool "LVM2 stub image"
|
||||
default y if IMAGE_DDI
|
||||
help
|
||||
An empty LVM2 physical volume attached to the internal
|
||||
volume group. This is useful as a base image for persistent
|
||||
storage in QEMU emulation scenarios where the OS is supplied
|
||||
in a separate image.
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
mkdir -p "${WORKDIR}"/root
|
||||
rm -rf "${WORKDIR}"/tmp
|
||||
mkdir -p "${WORKDIR}"/tmp
|
||||
|
||||
"${BR2_EXTERNAL_INFIX_PATH}"/utils/lvm-mkinternal >"${WORKDIR}"/stub.lvm
|
||||
|
||||
cat <<EOF >"${WORKDIR}"/genimage.cfg
|
||||
image lvm-stub.disk {
|
||||
hdimage {
|
||||
partition-table-type = "gpt"
|
||||
}
|
||||
|
||||
partition internal {
|
||||
growfs = "true"
|
||||
image = "stub.lvm"
|
||||
partition-type-uuid = "lvm"
|
||||
}
|
||||
}
|
||||
|
||||
# Silence genimage warnings
|
||||
config {}
|
||||
EOF
|
||||
|
||||
genimage \
|
||||
--tmppath "${WORKDIR}"/tmp \
|
||||
--rootpath "${WORKDIR}"/root \
|
||||
--inputpath "${WORKDIR}" \
|
||||
--outputpath "${BINARIES_DIR}" \
|
||||
--config "${WORKDIR}"/genimage.cfg
|
||||
@@ -0,0 +1,7 @@
|
||||
################################################################################
|
||||
#
|
||||
# image-lvm-stub
|
||||
#
|
||||
################################################################################
|
||||
|
||||
$(eval $(ix-image))
|
||||
@@ -10,6 +10,7 @@ $(1): $$($(2)_DEPENDENCIES)
|
||||
PKGDIR=$$($(2)_DIR) \
|
||||
WORKDIR=$$(BUILD_DIR)/$(1) \
|
||||
BINARIES_DIR=$$(BINARIES_DIR) \
|
||||
BR2_ARCH=$$(BR2_ARCH) \
|
||||
BR2_EXTERNAL_INFIX_PATH=$$(BR2_EXTERNAL_INFIX_PATH) \
|
||||
ARTIFACT=$$(INFIX_ARTIFACT) \
|
||||
COMPATIBLE=$$(INFIX_COMPATIBLE) \
|
||||
@@ -19,6 +20,7 @@ $(1): $$($(2)_DEPENDENCIES)
|
||||
|
||||
ifeq ($$($(2)),y)
|
||||
TARGETS_ROOTFS += $(1)
|
||||
BR2_ROOTFS_OVERLAY := "$$(realpath $$(pkgdir)/rootfs) $$(call qstrip,$$(BR2_ROOTFS_OVERLAY))"
|
||||
endif
|
||||
|
||||
endef
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
check() {
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
return 0
|
||||
}
|
||||
|
||||
install() {
|
||||
# Not sure if this should be handled by buildroot's merged-usr
|
||||
# module. Fix it here for now.
|
||||
ln -s "lib" "${initdir?}/lib64"
|
||||
}
|
||||
Executable
+279
@@ -0,0 +1,279 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo -ne "\e[31m●\e[0m \e[33m●\e[0m \e[32m●\e[0m Infix Initramfs — " >/dev/console
|
||||
source /etc/os-release >/dev/console
|
||||
echo "$VERSION"
|
||||
|
||||
set -e
|
||||
|
||||
getkopt()
|
||||
{
|
||||
grep -q -E "(^| )$1( |$)" /proc/cmdline
|
||||
}
|
||||
|
||||
getkparam()
|
||||
{
|
||||
sed -n "s/.*\<$1=\([^ ]*\).*/\1/p" /proc/cmdline
|
||||
}
|
||||
|
||||
debug_shell()
|
||||
{
|
||||
[ "$(getkparam rd.shell)" = "$1" ] || return 0
|
||||
|
||||
exec </dev/console &>/dev/console
|
||||
echo "Starting debug shell at $1"
|
||||
exec sh -i
|
||||
}
|
||||
|
||||
die()
|
||||
{
|
||||
step="${*:-$step}"
|
||||
|
||||
echo -e "\r\e[0K[\e[31mFAIL\e[0m] $step" >/dev/console
|
||||
echo "<0>ix-initramfs: ERROR: $step" >/dev/kmsg
|
||||
debug_shell error
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn()
|
||||
{
|
||||
step="${*:-$step}"
|
||||
|
||||
echo -e "\r\e[0K[\e[33mWARN\e[0m] $step" >/dev/console
|
||||
echo "<5>ix-initramfs: WARNING: $step" >/dev/kmsg
|
||||
debug_shell warning
|
||||
}
|
||||
|
||||
ok()
|
||||
{
|
||||
step="${*:-$step}"
|
||||
|
||||
echo -e "\r\e[0K[\e[32m OK \e[0m] $step" >/dev/console
|
||||
echo "<5>ix-initramfs: $step" >/dev/kmsg
|
||||
}
|
||||
|
||||
begin()
|
||||
{
|
||||
step="$*"
|
||||
echo -ne "[\e[33m ⋯ \e[0m] $step" >/dev/console
|
||||
}
|
||||
|
||||
debug()
|
||||
{
|
||||
echo "<7>ix-initramfs: $*" >/dev/kmsg
|
||||
}
|
||||
|
||||
mountcore()
|
||||
{
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devtmpfs dev /dev
|
||||
}
|
||||
|
||||
mountroot()
|
||||
{
|
||||
mkdir /newroot
|
||||
mount "$1" /newroot || \
|
||||
die "Unable to mount rootfs on $1"
|
||||
|
||||
ok "Mounting root filesystem on $(basename $1)"
|
||||
}
|
||||
|
||||
switchroot()
|
||||
{
|
||||
local innerinit=$(getkparam init)
|
||||
|
||||
ok "Switching to root filesystem"
|
||||
exec switch_root /newroot ${innerinit:-/sbin/init}
|
||||
}
|
||||
|
||||
internal_get_part()
|
||||
{
|
||||
local _i _pv
|
||||
|
||||
for _i in $(seq 50); do
|
||||
_pv=$(lvm pvs --reportformat json \
|
||||
| jq -r '.report[].pv[] | select(.vg_name == "internal") | .pv_name')
|
||||
if [ "$_pv" ]; then
|
||||
[ $_i -gt 5 ] && ok
|
||||
echo "$_pv"
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ $_i -eq 5 ] && begin "Waiting for the internal volume to come online"
|
||||
sleep .2
|
||||
done
|
||||
|
||||
warn "Proceeding without the internal volume"
|
||||
return 0
|
||||
}
|
||||
|
||||
internal_inflate()
|
||||
{
|
||||
local _dev _growfs _index _part
|
||||
|
||||
getkopt rd.nointernal && return 0
|
||||
|
||||
GPT_PART_GROWFS_BIT=59
|
||||
|
||||
_part=$(internal_get_part)
|
||||
[ "$_part" ] || return 0
|
||||
|
||||
_index=$(cat /sys/class/block/$(basename "$_part")/partition)
|
||||
[ "$_index" ] || return 1
|
||||
|
||||
_dev=/dev/$(basename $(realpath /sys/class/block/$(basename "$_part")/../))
|
||||
[ -b "$_dev" ] || return 1
|
||||
|
||||
sgdisk "$_dev" -A "$_index":get:$GPT_PART_GROWFS_BIT \
|
||||
| grep -q "$_index:$GPT_PART_GROWFS_BIT:1" \
|
||||
|| return 0
|
||||
|
||||
begin "Inflating internal VG in $_part"
|
||||
|
||||
# First, expand the GPT to cover the entire disk and expand the
|
||||
# LVM partition as much as possible.
|
||||
if ! sgdisk "$_dev" \
|
||||
--disk-guid=random \
|
||||
--set-alignment=1 \
|
||||
--move-second-header \
|
||||
--delete="$_index" \
|
||||
--largest-new="$_index" --typecode="$_index":8e00 \
|
||||
>/tmp/internal-inflate.log 2>&1; then
|
||||
warn "Failed to inflate GPT on $_dev"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! lvm pvresize "$_part" >>/tmp/internal-inflate.log 2>&1; then
|
||||
warn "Failed to resize PV $_part"
|
||||
return 0
|
||||
fi
|
||||
|
||||
ok "Inflated internal VG in $_part"
|
||||
}
|
||||
|
||||
addparts()
|
||||
{
|
||||
local _disk="$1"
|
||||
local _partscan="/sys/class/block/"$(basename "$_disk")/partscan
|
||||
|
||||
[ -f "$_partscan" ] && [ $(cat "$_partscan") -eq 1 ] && return
|
||||
|
||||
kpartx -a "$_disk" 2>/dev/null || die "addparts: Unable to add partitions from $_disk"
|
||||
}
|
||||
|
||||
gptuuid_probe()
|
||||
{
|
||||
local _uuid=$(echo "$1" | sed -e 's/^GPTUUID=//' | tr ABCDEF abcdef)
|
||||
local _cand _disk _initrd
|
||||
|
||||
[ -f /initrd.image ] && _initrd=/initrd.image
|
||||
|
||||
for _disk in $_initrd $(find /dev/ -type b); do
|
||||
_cand=$(ddi "$_disk" inspect uuid | tr ABCDEF abcdef)
|
||||
[ $? -eq 0 ] || continue
|
||||
|
||||
if [ "$_cand" = "$_uuid" ]; then
|
||||
ok "Located $_uuid in $_disk"
|
||||
echo "$_disk"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
die "gptuuid_probe: Unable to find disk $_uuid"
|
||||
}
|
||||
|
||||
ddi_probe()
|
||||
{
|
||||
local _disk="$1"
|
||||
local _dm="$(basename $_disk).root"
|
||||
local _hash=$(getkparam rd.ddi.roothash)
|
||||
local _v _trust
|
||||
|
||||
if [ -n "$_hash" ]; then
|
||||
ok "Trusting pre-authenticated root hash"
|
||||
_v="-V $_hash"
|
||||
else
|
||||
_v="-v"
|
||||
fi
|
||||
|
||||
_trust=$(ddi "$_disk" part root open $_v "$_dm" 2>&1 \
|
||||
|| die "ddi_probe: veritysetup of $_disk failed")
|
||||
|
||||
ok "$_trust"
|
||||
ok "Opened verity protected root from $(basename $_disk)"
|
||||
echo /dev/mapper/"$_dm"
|
||||
}
|
||||
|
||||
lvm_probe()
|
||||
{
|
||||
local _vg=$(dirname "$1") _lv=$(basename "$1")
|
||||
|
||||
lvm vgchange -ay "$_vg" &>/dev/null || \
|
||||
die "lvm_probe: Failed to activate the $_vg VG"
|
||||
|
||||
[ -b /dev/"$1" ] || die "lvm_probe: Unable to locate the $_lv LV in $_vg"
|
||||
|
||||
echo /dev/"$1"
|
||||
}
|
||||
|
||||
lo_probe()
|
||||
{
|
||||
local _lo=$(losetup -f || die "probe_lo: found no free loop device for $1")
|
||||
losetup -P "$_lo" "$1" || die "probe_lo: losetup of $1 to $_lo failed"
|
||||
|
||||
debug "lo_probe: setup $_lo backed by $1"
|
||||
echo "$_lo"
|
||||
}
|
||||
|
||||
probe()
|
||||
{
|
||||
local _real _kind
|
||||
|
||||
case "$1" in
|
||||
/*)
|
||||
_real=$(realpath "$1")
|
||||
_kind=$(stat -c %F "$_real" 2>/dev/null || true)
|
||||
case "$_kind" in
|
||||
"block special file")
|
||||
addparts "$_real"
|
||||
echo "$_real"
|
||||
;;
|
||||
"regular file")
|
||||
lo_probe "$_real"
|
||||
;;
|
||||
"")
|
||||
die "Unknown root \"$1\""
|
||||
;;
|
||||
*)
|
||||
die "Unsupported root \"$1\": $_kind"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
lvm)
|
||||
shift
|
||||
probe $(lvm_probe "$@")
|
||||
;;
|
||||
ddi)
|
||||
shift
|
||||
ddi_probe $(probe "$@")
|
||||
;;
|
||||
GPTUUID=*)
|
||||
gptuuid_probe "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
mountcore
|
||||
|
||||
debug_shell internal
|
||||
internal_inflate
|
||||
|
||||
debug_shell probe
|
||||
rootdev=$(probe $(getkparam root | sed -e 's/:/ /g'))
|
||||
|
||||
debug_shell mount
|
||||
mountroot "$rootdev"
|
||||
|
||||
debug_shell switchroot
|
||||
switchroot
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
progs="blkid ddi dmsetup kpartx jq lvm openssl sgdisk veritysetup"
|
||||
|
||||
check() {
|
||||
require_binaries $progs || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
return 0
|
||||
}
|
||||
|
||||
install() {
|
||||
inst_multiple $progs
|
||||
inst '/etc/os-release'
|
||||
inst '/etc/rauc/keys/*'
|
||||
|
||||
cp "$moddir/init" "${initdir?}/init"
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
mainmenu "QEMU @ARCH@ Virtualization"
|
||||
|
||||
config QEMU_@ARCH@
|
||||
bool
|
||||
default y
|
||||
|
||||
config QEMU_ARCH
|
||||
string
|
||||
default "@ARCH@"
|
||||
|
||||
config QEMU_VIRTIO_BUS
|
||||
string
|
||||
default "device" if QEMU_arm
|
||||
default "pci"
|
||||
|
||||
config QEMU_BIN_DDI
|
||||
string
|
||||
default "@IMAGE@"
|
||||
|
||||
config QEMU_BIN_DISK
|
||||
string
|
||||
default "@DISK@"
|
||||
|
||||
config QEMU_BIN_ESP
|
||||
string
|
||||
default "@ESP@"
|
||||
|
||||
config QEMU_BIN_KERNEL
|
||||
string
|
||||
depends on QEMU_LOADER_QEMU
|
||||
default "../zImage" if QEMU_arm
|
||||
default "../Image" if QEMU_aarch64
|
||||
default "../bzImage" if QEMU_x86_64
|
||||
|
||||
config QEMU_BIN_LVM_STUB
|
||||
string
|
||||
default "../lvm-stub.disk"
|
||||
|
||||
|
||||
choice
|
||||
prompt "Loader"
|
||||
default QEMU_LOADER_QEMU
|
||||
|
||||
config QEMU_LOADER_QEMU
|
||||
bool "QEMU"
|
||||
|
||||
config QEMU_LOADER_OVMF
|
||||
bool "OVMF (UEFI)"
|
||||
|
||||
endchoice
|
||||
|
||||
config QEMU_BIN_BIOS
|
||||
string
|
||||
depends on !QEMU_LOADER_KERNEL
|
||||
default "OVMF.fd" if QEMU_LOADER_OVMF
|
||||
|
||||
menu "Machine"
|
||||
|
||||
config QEMU_MACHINE
|
||||
string "Machine"
|
||||
default "pc" if QEMU_x86_64
|
||||
default "virt"
|
||||
|
||||
config QEMU_CPU
|
||||
string "CPU"
|
||||
default "host" if QEMU_x86_64
|
||||
default "cortex-a53" if QEMU_aarch64
|
||||
default "max"
|
||||
|
||||
config QEMU_RAM
|
||||
string "RAM size (k/M/G)"
|
||||
default "384M"
|
||||
|
||||
choice
|
||||
prompt "Console"
|
||||
default QEMU_CONSOLE_VIRTIO
|
||||
|
||||
config QEMU_CONSOLE_VIRTIO
|
||||
bool "Virtio (hvc0)"
|
||||
|
||||
config QEMU_CONSOLE_SERIAL
|
||||
bool "Serial (ttyS0/ttyAMA0)"
|
||||
|
||||
endchoice
|
||||
|
||||
config QEMU_CONSOLE
|
||||
string
|
||||
default "hvc0" if QEMU_CONSOLE_VIRTIO
|
||||
default "ttyAMA0" if QEMU_arm
|
||||
default "ttyAMA0" if QEMU_aarch64
|
||||
default "ttyS0"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Disks & Filesystems"
|
||||
|
||||
config QEMU_DISK_INITRD
|
||||
bool "Use the DDI as initrd"
|
||||
depends on QEMU_LOADER_QEMU
|
||||
help
|
||||
Pass, and boot from, the DDI as the initial RAM disk.
|
||||
|
||||
This is useful to test netbooting scenarios, e.g., in
|
||||
combination with an empty system disk to simulate an initial
|
||||
deployment.
|
||||
|
||||
comment "System disk"
|
||||
|
||||
choice
|
||||
prompt "Interface"
|
||||
default QEMU_DISK_SYS_IF_VIRTIO
|
||||
|
||||
config QEMU_DISK_SYS_IF_VIRTIO
|
||||
bool "virtio"
|
||||
help
|
||||
Attach system disks using virtio block devices.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Contents"
|
||||
default QEMU_DISK_SYS_SPLIT
|
||||
|
||||
config QEMU_DISK_SYS_EMPTY
|
||||
bool "Empty disk"
|
||||
help
|
||||
Attach an empty disk.
|
||||
|
||||
This is useful for testing things like installing Infix from a
|
||||
USB mass storage device (see "USB Mass Storage" below).
|
||||
|
||||
config QEMU_DISK_SYS_FULL
|
||||
bool "Full system disk"
|
||||
help
|
||||
Use the full system disk containing:
|
||||
- EFI partition (and backup) with Barebox
|
||||
- LVM PV with Infix installed
|
||||
|
||||
This is useful when testing system upgrades, fallback
|
||||
mechanisms related to corrupt disks etc.
|
||||
|
||||
WARNING: Usage of this option is fraught with peril as
|
||||
incremental builds of Infix are likely to cause corruption in
|
||||
the QCoW2 layer. If you do not understand the meaning of this
|
||||
you most likely to NOT want to make use of this feature.
|
||||
|
||||
config QEMU_DISK_SYS_NONE
|
||||
bool "None"
|
||||
help
|
||||
Do not attach any system disk.
|
||||
|
||||
This is useful for testing how Infix behaves when no persistent
|
||||
storage is available - a common scenario during bringup of a
|
||||
new board.
|
||||
|
||||
config QEMU_DISK_SYS_SPLIT
|
||||
bool "Split ESP/DDI/LVM"
|
||||
help
|
||||
Attach the EFI System partition, containing Barebox; the raw
|
||||
DDI image; and an (initially empty) LVM stub for persistent
|
||||
storage - each as a separate disk.
|
||||
|
||||
This is the simplest way of testing out incremental builds of
|
||||
Infix. Since the DDI is attached as a raw read-only disk, there
|
||||
is no risk of CoW corruption like with a full system disk.
|
||||
|
||||
NOTE: In this setup, the size below refers to the size of the
|
||||
LVM stub.
|
||||
|
||||
endchoice
|
||||
|
||||
config QEMU_DISK_SYS_SIZE
|
||||
string "Size (k/M/G)"
|
||||
default "8G"
|
||||
|
||||
comment "USB mass storage"
|
||||
|
||||
choice
|
||||
prompt "Contents"
|
||||
default QEMU_DISK_USB_NONE
|
||||
|
||||
config QEMU_DISK_USB_DISK
|
||||
bool "Full system disk"
|
||||
help
|
||||
Attach the full system disk containing:
|
||||
- EFI partition (and backup) with Barebox
|
||||
- LVM PV with Infix installed
|
||||
|
||||
config QEMU_DISK_USB_NONE
|
||||
bool "None"
|
||||
help
|
||||
Do not attach any disk over USB.
|
||||
|
||||
endchoice
|
||||
|
||||
config QEMU_DISK_USB_SIZE
|
||||
string "Size (k/M/G)"
|
||||
depends on !QEMU_DISK_USB_NONE
|
||||
default "4G"
|
||||
|
||||
comment "Host filesystem passthrough (9P)"
|
||||
|
||||
config QEMU_DISK_HOST
|
||||
string "Path"
|
||||
default "/tmp"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Networking"
|
||||
|
||||
choice
|
||||
prompt "Mode"
|
||||
default QEMU_NET_USER
|
||||
|
||||
config QEMU_NET_BRIDGE
|
||||
bool "Bridged"
|
||||
|
||||
config QEMU_NET_NONE
|
||||
bool "None"
|
||||
|
||||
config QEMU_NET_ROCKER
|
||||
bool "Rocker"
|
||||
|
||||
config QEMU_NET_TAP
|
||||
bool "TAP"
|
||||
|
||||
config QEMU_NET_USER
|
||||
bool "User"
|
||||
|
||||
endchoice
|
||||
|
||||
config QEMU_NET_MODE
|
||||
string
|
||||
default "bridge" if QEMU_NET_BRIDGE
|
||||
default "none" if QEMU_NET_NONE
|
||||
default "rocker" if QEMU_NET_ROCKER
|
||||
default "tap" if QEMU_NET_TAP
|
||||
default "user" if QEMU_NET_USER
|
||||
|
||||
config QEMU_NET_MODEL
|
||||
string "Interface model"
|
||||
default "virtio-net-device" if QEMU_arm
|
||||
default "virtio-net-pci"
|
||||
help
|
||||
The default virtio NIC works for most use-cases, but if you
|
||||
want to play with low-level stuff like ethtool, you might
|
||||
want to test the Intel 82545EM driver, e1000.
|
||||
|
||||
Note: ARM 32-bit uses virtio-net-device (MMIO) by default.
|
||||
|
||||
config QEMU_NET_BRIDGE_DEV
|
||||
string "Bridge device"
|
||||
depends on QEMU_NET_BRIDGE
|
||||
default "virbr0"
|
||||
|
||||
config QEMU_NET_USER_OPTS
|
||||
string "User mode options"
|
||||
depends on QEMU_NET_USER
|
||||
help
|
||||
Extra -nic user,<OPTIONS>
|
||||
|
||||
config QEMU_NET_TAP_N
|
||||
int "Number of TAPs"
|
||||
depends on QEMU_NET_TAP
|
||||
default 1
|
||||
|
||||
config QEMU_NET_PORTS
|
||||
int "Number of Rocker switch ports"
|
||||
depends on QEMU_NET_ROCKER
|
||||
default 10
|
||||
|
||||
endmenu
|
||||
|
||||
config QEMU_EXTRA
|
||||
string "Extra QEMU options"
|
||||
|
||||
config QEMU_APPEND
|
||||
string "Extra kernel options"
|
||||
depends on QEMU_LOADER_QEMU
|
||||
Executable
+480
@@ -0,0 +1,480 @@
|
||||
#!/bin/sh
|
||||
# This script can be used to start an Infix OS image in Qemu. It reads
|
||||
# either a .config, generated from Config.in, or qemu.cfg from a release
|
||||
# tarball, for the required configuration data.
|
||||
#
|
||||
# Debian/Ubuntu users can change the configuration post-release, install
|
||||
# the kconfig-frontends package:
|
||||
#
|
||||
# sudo apt install kconfig-frontends
|
||||
#
|
||||
# and then call this script with:
|
||||
#
|
||||
# ./run.sh -c
|
||||
#
|
||||
# To bring up a menuconfig dialog. Select `Exit` and save the changes.
|
||||
# For more help, see:_
|
||||
#
|
||||
# ./run.sh -h
|
||||
#
|
||||
# shellcheck disable=SC3037
|
||||
|
||||
# Add /sbin to PATH for mkfs.ext4 and such (not default in debian)
|
||||
export PATH="/sbin:/usr/sbin:$PATH"
|
||||
|
||||
qdir=$(dirname "$(readlink -f "$0")")
|
||||
imgdir=$(readlink -f "${qdir}/..")
|
||||
prognm=$(basename "$0")
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage: $prognm [OPTIONS] [-- KERNEL-ARGS]
|
||||
|
||||
Start Infix in a VM.
|
||||
|
||||
Any arguments after -- are passed to the kernel, provided that QEMU's
|
||||
native loader is being used (i.e., not UEFI, for example). This
|
||||
includes a second --, which the kernel uses to delimit between kernel
|
||||
arguments and those passed to the init process.
|
||||
|
||||
Options:
|
||||
-0
|
||||
Clear any copy-on-write layers, resetting all disks to their
|
||||
initial states, and exit.
|
||||
|
||||
-c
|
||||
Run menuconfig to change Qemu settings. This requires the
|
||||
'kconfig-frontends' package (Debian/Ubuntu).
|
||||
|
||||
-G
|
||||
Skip config generation. This is useful if you are testing out new
|
||||
QEMU features by manually modifying qemu.cfg.
|
||||
|
||||
-h
|
||||
Show this help message
|
||||
|
||||
Example:
|
||||
|
||||
$prognm -- loglevel=4 -- finit.debug
|
||||
|
||||
Will set the kernel's loglevel to 4, and pass finit.debug as an
|
||||
argument to PID 1.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
die()
|
||||
{
|
||||
echo "$prognm: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
binpath()
|
||||
{
|
||||
case "$1" in
|
||||
./*|../*)
|
||||
# Relative paths are relative to the location of .config
|
||||
printf "$qdir/$1"
|
||||
;;
|
||||
*)
|
||||
printf "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
q()
|
||||
{
|
||||
local _cmd="$1"
|
||||
shift
|
||||
|
||||
case "$_cmd" in
|
||||
sect)
|
||||
printf ' \\\n\t' >>"$qdir"/qemu.sh
|
||||
|
||||
case $# in
|
||||
1)
|
||||
printf -- "-$1" >>"$qdir"/qemu.sh
|
||||
_delim=" "
|
||||
;;
|
||||
2)
|
||||
printf -- "-$1 $2" >>"$qdir"/qemu.sh
|
||||
_delim=","
|
||||
;;
|
||||
*)
|
||||
die "q sect: Invalid arguments"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
param)
|
||||
[ $# -eq 2 ] || die "q param: Takes exactly two arguments"
|
||||
printf -- "$_delim$1=$2" >>"$qdir"/qemu.sh
|
||||
_delim=","
|
||||
;;
|
||||
option)
|
||||
[ $# -eq 1 ] || die "q option: Takes exactly one argument"
|
||||
printf -- "$_delim$1" >>"$qdir"/qemu.sh
|
||||
_delim=","
|
||||
;;
|
||||
*)
|
||||
die "q: Unknown command: $_cmd"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
q_sect_device_virtio()
|
||||
{
|
||||
q sect device virtio-$1-$QEMU_VIRTIO_BUS
|
||||
}
|
||||
|
||||
q_add_disk()
|
||||
{
|
||||
if [ "$QEMU_DISK_SYS_IF_VIRTIO" ]; then
|
||||
q_sect_device_virtio blk
|
||||
q param drive "$1"
|
||||
else
|
||||
die "Unknown system disk interface"
|
||||
fi
|
||||
}
|
||||
|
||||
q_sect_device_usb()
|
||||
{
|
||||
if [ -z "$usb_bus_added" ]; then
|
||||
q sect usb
|
||||
q sect device usb-ehci
|
||||
q param id ehci
|
||||
usb_bus_added=YES
|
||||
fi
|
||||
|
||||
q sect device "$1"
|
||||
q param bus ehci.0
|
||||
}
|
||||
|
||||
qcowed()
|
||||
{
|
||||
local _qcow="$qdir"/"$1".qcow2
|
||||
local _base _size
|
||||
|
||||
if [ -f "$_qcow" ] && qemu-img check -q "$_qcow"; then
|
||||
echo "$_qcow"
|
||||
return
|
||||
fi
|
||||
|
||||
rm -f "$_qcow"
|
||||
|
||||
case $# in
|
||||
2)
|
||||
_size="$2"
|
||||
|
||||
qemu-img create -q -f qcow2 "$_qcow" "$_size" \
|
||||
|| die "Unable to create $1"
|
||||
;;
|
||||
3)
|
||||
|
||||
_base="$2"
|
||||
_size="$3"
|
||||
|
||||
[ "$_size" = "auto" ] && _size=
|
||||
|
||||
qemu-img create -q -f qcow2 -F raw \
|
||||
-o backing_file="$_base" "$_qcow" $_size \
|
||||
|| die "Unable to create CoW layer for $_base"
|
||||
;;
|
||||
*)
|
||||
die "qcowed: usage: qcowed <name> [<backing-file>] <size>"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "$_qcow"
|
||||
}
|
||||
|
||||
append()
|
||||
{
|
||||
echo -n " $*" >>"$qdir"/append
|
||||
}
|
||||
|
||||
gen_machine()
|
||||
{
|
||||
q sect machine
|
||||
q param type "$QEMU_MACHINE"
|
||||
q param accel kvm:tcg
|
||||
|
||||
q sect cpu "$QEMU_CPU"
|
||||
|
||||
q sect m
|
||||
q param size "$QEMU_RAM"
|
||||
}
|
||||
|
||||
gen_loader()
|
||||
{
|
||||
if [ "$QEMU_LOADER_QEMU" ]; then
|
||||
q sect kernel $(binpath "$QEMU_BIN_KERNEL")
|
||||
elif [ "$QEMU_LOADER_OVMF" ]; then
|
||||
q sect bios $(binpath "$QEMU_BIN_BIOS")
|
||||
fi
|
||||
}
|
||||
|
||||
gen_serial()
|
||||
{
|
||||
q sect display none
|
||||
|
||||
q_sect_device_virtio serial
|
||||
|
||||
q sect chardev stdio
|
||||
q param id console0
|
||||
q param mux on
|
||||
|
||||
q sect mon
|
||||
q param chardev console0
|
||||
|
||||
case "$QEMU_CONSOLE" in
|
||||
hvc0)
|
||||
q sect device virtconsole
|
||||
q param nr 0
|
||||
q param name console
|
||||
q param chardev console0
|
||||
;;
|
||||
ttyS0|ttyAMA0)
|
||||
q sect serial chardev:console0
|
||||
;;
|
||||
*)
|
||||
die "Unsupported console: $QEMU_CONSOLE"
|
||||
;;
|
||||
esac
|
||||
|
||||
append console="$QEMU_CONSOLE"
|
||||
|
||||
[ "$V" ] && append debug || append loglevel=4
|
||||
}
|
||||
|
||||
gpt_uuid()
|
||||
{
|
||||
sgdisk "$1" -p | awk '
|
||||
BEGIN { err = 1; } END { exit(err); }
|
||||
/^Creating new GPT/ { exit; }
|
||||
|
||||
/^Disk identifier \(GUID\): / {
|
||||
print($4); err = 0; exit;
|
||||
}' || die "Unable to determine GPT UUID of $1"
|
||||
}
|
||||
|
||||
gen_disk_sys_empty()
|
||||
{
|
||||
q sect drive
|
||||
q param id disk-sys
|
||||
q param format qcow2
|
||||
q param if none
|
||||
q param file $(qcowed disk-sys "$QEMU_DISK_SYS_SIZE")
|
||||
|
||||
q_add_disk disk-sys
|
||||
}
|
||||
|
||||
gen_disk_sys_full()
|
||||
{
|
||||
q sect drive
|
||||
q param id disk-sys
|
||||
q param format qcow2
|
||||
q param if none
|
||||
q param file $(qcowed disk-sys $(binpath "$QEMU_BIN_DISK") \
|
||||
"$QEMU_DISK_SYS_SIZE")
|
||||
|
||||
q_add_disk disk-sys
|
||||
|
||||
append root=ddi:lvm:internal/$(basename "$QEMU_BIN_DDI")
|
||||
}
|
||||
|
||||
gen_disk_sys_split()
|
||||
{
|
||||
# EFI System Partition
|
||||
q sect drive
|
||||
q param id disk-esp
|
||||
q param format qcow2
|
||||
q param if none
|
||||
q param file $(qcowed disk-esp $(binpath "$QEMU_BIN_ESP") auto)
|
||||
|
||||
q_add_disk disk-esp
|
||||
|
||||
# LVM stub
|
||||
q sect drive
|
||||
q param id disk-sys
|
||||
q param format qcow2
|
||||
q param if none
|
||||
q param file $(qcowed disk-sys $(binpath "$QEMU_BIN_LVM_STUB") \
|
||||
"$QEMU_DISK_SYS_SIZE")
|
||||
|
||||
q_add_disk disk-sys
|
||||
|
||||
# DDI (ro)
|
||||
q sect drive
|
||||
q param id disk-ddi
|
||||
q param format raw
|
||||
q param file $(binpath "$QEMU_BIN_DDI")
|
||||
q param if none
|
||||
q param read-only on
|
||||
|
||||
q_add_disk disk-ddi
|
||||
|
||||
append root=ddi:GPTUUID=$(gpt_uuid $(binpath "$QEMU_BIN_DDI"))
|
||||
}
|
||||
|
||||
gen_disk_sys()
|
||||
{
|
||||
[ "$QEMU_DISK_SYS_NONE" ] && return
|
||||
|
||||
if [ "$QEMU_DISK_SYS_EMPTY" ]; then
|
||||
gen_disk_sys_empty
|
||||
elif [ "$QEMU_DISK_SYS_FULL" ]; then
|
||||
gen_disk_sys_full
|
||||
elif [ "$QEMU_DISK_SYS_SPLIT" ]; then
|
||||
gen_disk_sys_split
|
||||
else
|
||||
die "Unknown system disk mode"
|
||||
fi
|
||||
}
|
||||
|
||||
gen_disk_usb()
|
||||
{
|
||||
[ "$QEMU_DISK_USB_NONE" ] && return
|
||||
|
||||
if [ "$QEMU_DISK_USB_DISK" ]; then
|
||||
q sect drive
|
||||
q param id disk-usb
|
||||
q param format qcow2
|
||||
q param file $(qcowed disk-usb \
|
||||
$(binpath "$QEMU_BIN_DISK") \
|
||||
"$QEMU_DISK_USB_SIZE")
|
||||
q param if none
|
||||
|
||||
q_sect_device_usb usb-storage
|
||||
q param drive disk-usb
|
||||
else
|
||||
die "Unknown USB attachment"
|
||||
fi
|
||||
}
|
||||
|
||||
gen_disk_initrd()
|
||||
{
|
||||
[ "$QEMU_DISK_INITRD" ] || return
|
||||
|
||||
q sect initrd $(binpath "$QEMU_BIN_DDI")
|
||||
append root=ddi:/initrd.image
|
||||
}
|
||||
|
||||
internal_is_available()
|
||||
{
|
||||
[ "$QEMU_DISK_SYS_FULL" ] || [ "$QEMU_DISK_SYS_SPLIT" ] \
|
||||
|| [ "$QEMU_DISK_USB_DISK" ]
|
||||
}
|
||||
|
||||
gen_disks()
|
||||
{
|
||||
gen_disk_sys
|
||||
gen_disk_usb
|
||||
|
||||
gen_disk_initrd
|
||||
|
||||
internal_is_available || append rd.nointernal
|
||||
}
|
||||
|
||||
gen_gdb()
|
||||
{
|
||||
# Create a UNIX socket on the host that is connected to a virtio
|
||||
# console in the guest, which gdbserver can attach to for
|
||||
# userspace debugging.
|
||||
q sect chardev socket
|
||||
q param id gdbserver
|
||||
q param path "$qdir"/gdbserver.sock
|
||||
q param server on
|
||||
q param wait off
|
||||
|
||||
q sect device virtconsole
|
||||
q param nr 1
|
||||
q param name gdbserver
|
||||
q param chardev gdbserver
|
||||
|
||||
# Create a UNIX socket on the host that is connected to QEMU's GDB
|
||||
# stub, for bootloader/kernel debugging.
|
||||
q sect chardev socket
|
||||
q param id gdbqemu
|
||||
q param path "$qdir"/gdbqemu.sock
|
||||
q param server on
|
||||
q param wait off
|
||||
|
||||
q sect gdb chardev:gdbqemu
|
||||
}
|
||||
|
||||
gen_all()
|
||||
{
|
||||
local _append
|
||||
|
||||
: >"$qdir"/append
|
||||
cat <<EOF >"$qdir"/qemu.sh
|
||||
#!/bin/sh
|
||||
|
||||
echo "Starting Qemu :: Ctrl-a x -- exit | Ctrl-a c -- toggle console/monitor"
|
||||
|
||||
line=\$(stty -g)
|
||||
stty raw
|
||||
trap 'stty "\$line"' EXIT INT TERM
|
||||
|
||||
qemu-system-$QEMU_ARCH -nodefaults \\
|
||||
EOF
|
||||
chmod +x "$qdir"/qemu.sh
|
||||
|
||||
gen_machine
|
||||
gen_loader
|
||||
gen_serial
|
||||
gen_disks
|
||||
gen_gdb
|
||||
|
||||
if [ "$QEMU_LOADER_QEMU" ]; then
|
||||
_append=$(cat "$qdir"/append)
|
||||
q sect append "\"$_append $QEMU_APPEND $*\""
|
||||
fi
|
||||
|
||||
echo >>"$qdir"/qemu.sh
|
||||
}
|
||||
|
||||
menuconfig()
|
||||
{
|
||||
command -v kconfig-mconf >/dev/null \
|
||||
|| die "cannot find kconfig-mconf for menuconfig"
|
||||
|
||||
CONFIG_= KCONFIG_CONFIG="$qdir"/.config \
|
||||
kconfig-mconf "$qdir"/Config.in
|
||||
}
|
||||
|
||||
_generate=YES
|
||||
|
||||
while getopts "0cGh-" opt; do
|
||||
case ${opt} in
|
||||
0)
|
||||
echo "Clearing all copy-on-write layers" >&2
|
||||
rm -f "$qdir"/*.qcow2
|
||||
exit 0
|
||||
;;
|
||||
c)
|
||||
menuconfig
|
||||
;;
|
||||
G)
|
||||
_generate=
|
||||
;;
|
||||
h)
|
||||
usage && exit 0
|
||||
;;
|
||||
-)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage && exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
. "$qdir"/.config
|
||||
|
||||
[ "$_generate" ] && gen_all "$*"
|
||||
|
||||
exec "$qdir"/qemu.sh
|
||||
@@ -149,7 +149,7 @@ config QEMU_HOST
|
||||
|
||||
config QEMU_APPEND
|
||||
string "Extra kernel options"
|
||||
depends on !QEMU_ROOTFS_MMC
|
||||
depends on QEMU_LOADER_KERNEL
|
||||
|
||||
config QEMU_EXTRA
|
||||
string "Extra QEMU options"
|
||||
@@ -4,9 +4,15 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
QEMU_SCRIPTS_DIR := $(pkgdir)
|
||||
qemu-scripts-dir := $(pkgdir)/$(if $(IMAGE_DDI),ddi,itb)
|
||||
qemu-image := ../$(INFIX_ARTIFACT).$(if $(IMAGE_DDI),raw,qcow2)
|
||||
qemu-disk := $(if $(IMAGE_DDI_DISK),../$(INFIX_ARTIFACT).disk)
|
||||
qemu-esp := $(if $(IMAGE_BAREBOX_ESP),../barebox-esp.vfat)
|
||||
|
||||
qemu-kconfig-prefix := $(if $(IMAGE_DDI),,CONFIG_)
|
||||
|
||||
qemu-kconfig = \
|
||||
CONFIG_="CONFIG_" \
|
||||
CONFIG_="$(qemu-kconfig-prefix)" \
|
||||
BR2_CONFIG="$(BINARIES_DIR)/qemu/.config" \
|
||||
$(BUILD_DIR)/buildroot-config/$(1) $(2) "$(BINARIES_DIR)/qemu/Config.in"
|
||||
|
||||
@@ -25,17 +31,19 @@ qemu-scripts: \
|
||||
$(BINARIES_DIR)/qemu/Config.in \
|
||||
$(BINARIES_DIR)/qemu/.config
|
||||
|
||||
$(BINARIES_DIR)/qemu/run.sh: $(QEMU_SCRIPTS_DIR)/run.sh
|
||||
$(BINARIES_DIR)/qemu/run.sh: $(qemu-scripts-dir)/run.sh
|
||||
@$(call IXMSG,"Installing QEMU scripts")
|
||||
@mkdir -p $(dir $@)
|
||||
@cp $< $@
|
||||
|
||||
$(BINARIES_DIR)/qemu/Config.in: $(QEMU_SCRIPTS_DIR)/Config.in.in
|
||||
$(BINARIES_DIR)/qemu/Config.in: $(qemu-scripts-dir)/Config.in.in
|
||||
@mkdir -p $(dir $@)
|
||||
@sed \
|
||||
-e "s:@ARCH@:QEMU_$(BR2_ARCH):" \
|
||||
-e "s:@DISK_IMG@:../$(INFIX_ARTIFACT).qcow2:" \
|
||||
< $< >$@
|
||||
-e "s:@ARCH@:$(BR2_ARCH):g" \
|
||||
-e "s:@IMAGE@:$(qemu-image):g" \
|
||||
-e "s:@DISK@:$(qemu-disk):g" \
|
||||
-e "s:@ESP@:$(qemu-esp):g" \
|
||||
<$< >$@
|
||||
|
||||
$(BINARIES_DIR)/qemu/.config: $(BINARIES_DIR)/qemu/Config.in
|
||||
@$(call qemu-kconfig,conf,--olddefconfig)
|
||||
|
||||
Executable
+343
@@ -0,0 +1,343 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
capath=/etc/rauc/keys
|
||||
|
||||
usage()
|
||||
{
|
||||
local _ddi="$(basename $0)"
|
||||
|
||||
cat <<EOF
|
||||
usage: $_ddi [<options>] <image> <command> [<args>]
|
||||
|
||||
Discoverable Disk Image manipulation
|
||||
|
||||
Options:
|
||||
|
||||
-A <arch>
|
||||
Override the system architecture.
|
||||
|
||||
-C <path>
|
||||
Source trusted signing keys from the supplied directory.
|
||||
Default: $capath
|
||||
|
||||
-h
|
||||
Show this message and exit.
|
||||
|
||||
Commands:
|
||||
|
||||
inspect <property>
|
||||
|
||||
uuid
|
||||
Print GPT disk identifier
|
||||
|
||||
part <partition> <command> [<args]
|
||||
Where <partition> is one of:
|
||||
root The first root partition
|
||||
|
||||
open [<options>] <name>
|
||||
|
||||
Create a device mapping for the specified partition, called
|
||||
<name>.
|
||||
|
||||
Options:
|
||||
|
||||
-v
|
||||
Use the associated dm-verity hash tree, whose root hash is
|
||||
available in the associated signature partition.
|
||||
|
||||
-V <root-hash>
|
||||
Use the associated dm-verity hash tree, along with an
|
||||
externally validated root hash.
|
||||
|
||||
verify [<options>]
|
||||
|
||||
Verify that the specified partition exists and, optionally,
|
||||
is accompanied by a trusted dm-verity hash tree.
|
||||
|
||||
Options:
|
||||
|
||||
-v
|
||||
Require the presence of an associated dm-verity hash tree,
|
||||
whose root hash is available in the associated signature
|
||||
partition.
|
||||
|
||||
-V <root-hash>
|
||||
Require the presence of an associated dm-verity hash tree
|
||||
whose root hash matches the provided one.
|
||||
|
||||
Examples:
|
||||
|
||||
Map and mount the verity protected SquashFS root partition from an
|
||||
image:
|
||||
$_ddi infix-x86_64.raw part root open -v infix
|
||||
mount -t squashfs /dev/mapper/infix /opt
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
X86_64_CODE_ROOT=0x8304
|
||||
X86_64_CODE_ROOT_VERITY=0x830c
|
||||
X86_64_CODE_ROOT_VERITY_SIG=0x8370
|
||||
|
||||
die()
|
||||
{
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
ok()
|
||||
{
|
||||
echo "$*" >&2
|
||||
}
|
||||
|
||||
setup_arch()
|
||||
{
|
||||
case "$1" in
|
||||
x86_64)
|
||||
CODE_ROOT=$X86_64_CODE_ROOT
|
||||
CODE_ROOT_VERITY=$X86_64_CODE_ROOT_VERITY
|
||||
CODE_ROOT_VERITY_SIG=$X86_64_CODE_ROOT_VERITY_SIG
|
||||
;;
|
||||
*)
|
||||
die "Unsupported architecture"
|
||||
esac
|
||||
}
|
||||
|
||||
setup_work()
|
||||
{
|
||||
local _umask
|
||||
|
||||
_umask=$(umask)
|
||||
umask 077
|
||||
workdir=$(mktemp -d)
|
||||
umask $_umask
|
||||
|
||||
trap 'rm -rf "$workdir"' EXIT INT TERM
|
||||
}
|
||||
|
||||
ddi_to_block()
|
||||
{
|
||||
local _kind _lo
|
||||
|
||||
_kind=$(stat -Lc %F "$1")
|
||||
case "$_kind" in
|
||||
"block special file")
|
||||
echo "$1"
|
||||
return
|
||||
;;
|
||||
"regular file")
|
||||
_lo=$(losetup -f || die "Found no free loop device for $1")
|
||||
losetup -P "$_lo" "$1" || die "Failed to setup loop device of $1"
|
||||
echo "$_lo"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
die "$1 is a $_kind, only regular and block devices are supported"
|
||||
}
|
||||
|
||||
ddi_get_part_info()
|
||||
{
|
||||
local _code _item
|
||||
|
||||
case "$2" in
|
||||
root) _code="$CODE_ROOT";;
|
||||
root-verity) _code="$CODE_ROOT_VERITY";;
|
||||
root-verity-sig) _code="$CODE_ROOT_VERITY_SIG";;
|
||||
*) die "Unknown partition type \"$1\"" ;;
|
||||
esac
|
||||
_code=$(printf "%04X" "$_code")
|
||||
_item=${3:-exists}
|
||||
|
||||
sgdisk "$1" -p | awk -v code="$_code" -v item="$_item" '
|
||||
/^[ ]*[0-9]+/ {
|
||||
if ($6 != code)
|
||||
next;
|
||||
|
||||
if (item == "exists") exit(0);
|
||||
if (item == "index") { print($1); exit(0); }
|
||||
|
||||
exit(1);
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
ddi_get_part_dev()
|
||||
{
|
||||
local _dev _index _lvuuid _subsys
|
||||
|
||||
_index=$(ddi_get_part_info "$1" "$2" index \
|
||||
|| die "No $2 partition found on $1")
|
||||
|
||||
_subsys=$(dmsetup info -c --noheadings -o subsystem "$1" 2>/dev/null || true)
|
||||
if [ "$_subsys" = "LVM" ]; then
|
||||
_lvuuid=$(dmsetup info -c --noheadings -o uuid "$1")
|
||||
_dev=/dev/mapper/$(dmsetup info -c --noheadings -o name -u "part${_index}-$_lvuuid")
|
||||
else
|
||||
_dev=$(basename "$1")
|
||||
_dev=$(find /sys/block/"$_dev"/ -mindepth 1 -maxdepth 1 -type d \
|
||||
-name "${_dev}$_index" -o -name "${_dev}p$_index")
|
||||
_dev=/dev/$(basename "$_dev")
|
||||
fi
|
||||
|
||||
[ -b "$_dev" ] || die "Failed to locate $2 partition in $1"
|
||||
|
||||
echo "$_dev"
|
||||
}
|
||||
|
||||
ddi_get_root_hash()
|
||||
{
|
||||
local _subj
|
||||
|
||||
command -v openssl >/dev/null || die "Missing dependency: openssl"
|
||||
command -v jq >/dev/null || die "Missing dependency: jq"
|
||||
|
||||
jq -r .rootHash "$1" | tr -d '\n' >"$workdir"/hash \
|
||||
|| die "Invalid signature: rootHash is missing"
|
||||
|
||||
jq -r .signature "$1" | base64 -d >"$workdir"/sig \
|
||||
|| die "Invalid signature: signature data is missing"
|
||||
|
||||
openssl cms -verify -verify_retcode -CApath "$capath" \
|
||||
-content "$workdir"/hash \
|
||||
-in "$workdir"/sig -inform DER \
|
||||
-certsout "$workdir"/certs >/dev/null 2>&1 \
|
||||
|| die "Signature verification failed"
|
||||
|
||||
_subj=$(openssl x509 -noout -subject <"$workdir"/certs | sed -e 's/^subject=//')
|
||||
ok "Trusting root hash signature from $_subj"
|
||||
|
||||
cat "$workdir"/hash
|
||||
}
|
||||
|
||||
ddipart_get_verity()
|
||||
{
|
||||
local _hash
|
||||
|
||||
while getopts "vV:" opt; do
|
||||
case "$opt" in
|
||||
V)
|
||||
vkind="${kind}-verity"
|
||||
vhash="$OPTARG"
|
||||
;;
|
||||
v)
|
||||
vkind="${kind}-verity"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$vkind" ] || return
|
||||
|
||||
vpart=$(ddi_get_part_dev "$img" "$vkind")
|
||||
|
||||
[ "$vhash" ] && return
|
||||
|
||||
vskind="${vkind}-sig"
|
||||
vspart=$(ddi_get_part_dev "$img" "$vskind")
|
||||
vhash=$(ddi_get_root_hash "$vspart")
|
||||
}
|
||||
|
||||
ddipart_open()
|
||||
{
|
||||
[ "$vpart" ] || die "$kind partition is already accessible at $part"
|
||||
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
veritysetup open "$part" "$1" "$vpart" "$vhash"
|
||||
}
|
||||
|
||||
ddipart_verify()
|
||||
{
|
||||
[ "$vpart" ] || return
|
||||
|
||||
veritysetup verify "$part" "$vpart" "$vhash"
|
||||
ok "Image in $img has $kind partition with valid dm-verity hash tree"
|
||||
}
|
||||
|
||||
ddipart()
|
||||
{
|
||||
local _cmd
|
||||
|
||||
kind="$1"
|
||||
shift
|
||||
|
||||
ddi_get_part_info "$img" "$kind" \
|
||||
|| die "No $kind partition found on $img"
|
||||
|
||||
_cmd="$1"
|
||||
shift
|
||||
|
||||
case "$_cmd" in
|
||||
open|verify)
|
||||
img=$(ddi_to_block $img)
|
||||
part=$(ddi_get_part_dev "$img" "$kind")
|
||||
ddipart_get_verity "$@"
|
||||
|
||||
ddipart_$_cmd "$@"
|
||||
;;
|
||||
*)
|
||||
die "Unknown partition command \"$_cmd\""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ddiinspect()
|
||||
{
|
||||
case "$1" in
|
||||
uuid)
|
||||
sgdisk "$img" -p | awk '
|
||||
BEGIN { err = 1; } END { exit(err); }
|
||||
/^Creating new GPT/ { exit; }
|
||||
|
||||
/^Disk identifier \(GUID\): / {
|
||||
print($4); err = 0; exit;
|
||||
}'
|
||||
;;
|
||||
*)
|
||||
die "Unknown property: $1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_arch=$(uname -m)
|
||||
|
||||
while getopts "A:C:h" opt; do
|
||||
case ${opt} in
|
||||
A)
|
||||
_arch="$OPTARG"
|
||||
;;
|
||||
C)
|
||||
capath="$OPTARG"
|
||||
;;
|
||||
h)
|
||||
usage && exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
usage && exit 1
|
||||
fi
|
||||
|
||||
img="$1"
|
||||
cmd="$2"
|
||||
shift 2
|
||||
|
||||
setup_arch "$_arch"
|
||||
setup_work
|
||||
|
||||
case "$cmd" in
|
||||
inspect)
|
||||
ddiinspect "$@"
|
||||
;;
|
||||
part)
|
||||
ddipart "$@"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command \"$cmd\"" >2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,3 @@
|
||||
CODE_ROOT=0x8304
|
||||
CODE_ROOT_VERITY=0x830c
|
||||
CODE_ROOT_VERITY_SIG=0x8370
|
||||
+1
-1
Submodule buildroot updated: 66450a898a...cde6f777f9
@@ -37,6 +37,7 @@ BR2_PACKAGE_STRESS_NG=y
|
||||
BR2_PACKAGE_JQ=y
|
||||
BR2_PACKAGE_E2FSPROGS=y
|
||||
BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y
|
||||
BR2_PACKAGE_CRYPTSETUP=y
|
||||
BR2_PACKAGE_DBUS_CXX=y
|
||||
BR2_PACKAGE_DBUS_GLIB=y
|
||||
BR2_PACKAGE_EUDEV_RULES_GEN=y
|
||||
@@ -87,6 +88,7 @@ BR2_PACKAGE_BASH_COMPLETION=y
|
||||
BR2_PACKAGE_SUDO=y
|
||||
BR2_PACKAGE_GETENT=y
|
||||
BR2_PACKAGE_KMOD_TOOLS=y
|
||||
BR2_PACKAGE_MULTIPATH_TOOLS=y
|
||||
BR2_PACKAGE_PWGEN=y
|
||||
BR2_PACKAGE_RAUC=y
|
||||
BR2_PACKAGE_RAUC_DBUS=y
|
||||
@@ -95,6 +97,7 @@ BR2_PACKAGE_RAUC_NETWORK=y
|
||||
BR2_PACKAGE_RAUC_JSON=y
|
||||
BR2_PACKAGE_SYSKLOGD=y
|
||||
BR2_PACKAGE_SYSKLOGD_LOGGER=y
|
||||
BR2_PACKAGE_UTIL_LINUX_PARTX=y
|
||||
BR2_PACKAGE_WATCHDOGD=y
|
||||
BR2_PACKAGE_WATCHDOGD_GENERIC=y
|
||||
BR2_PACKAGE_WATCHDOGD_LOADAVG=y
|
||||
@@ -105,13 +108,16 @@ BR2_PACKAGE_WATCHDOGD_TEMPMON=y
|
||||
BR2_PACKAGE_LESS=y
|
||||
BR2_PACKAGE_MG=y
|
||||
BR2_PACKAGE_NANO=y
|
||||
BR2_TARGET_ROOTFS_CPIO_DRACUT=y
|
||||
BR2_TARGET_ROOTFS_CPIO_DRACUT_MODULES="${BR2_EXTERNAL_INFIX_PATH}/board/common/initramfs"
|
||||
BR2_TARGET_ROOTFS_CPIO_DRACUT_CONF_FILES="${BR2_EXTERNAL_INFIX_PATH}/board/common/dracut.conf"
|
||||
BR2_TARGET_ROOTFS_INITRAMFS=y
|
||||
# BR2_TARGET_ROOTFS_TAR is not set
|
||||
BR2_TARGET_EDK2=y
|
||||
BR2_TARGET_GRUB2=y
|
||||
BR2_TARGET_GRUB2_X86_64_EFI=y
|
||||
BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 squash4 part_gpt normal efi_gop configfile loadenv test echo reboot net efinet tftp loopback cat search"
|
||||
BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/grub-embed.cfg"
|
||||
BR2_TARGET_GRUB2_INSTALL_TOOLS=y
|
||||
BR2_TARGET_BAREBOX=y
|
||||
BR2_TARGET_BAREBOX_CUSTOM_VERSION=y
|
||||
BR2_TARGET_BAREBOX_CUSTOM_VERSION_VALUE="2025.11.0"
|
||||
BR2_TARGET_BAREBOX_BOARD_DEFCONFIG="efi"
|
||||
BR2_TARGET_BAREBOX_IMAGE_FILE="barebox.efi"
|
||||
BR2_PACKAGE_HOST_DOSFSTOOLS=y
|
||||
BR2_PACKAGE_HOST_E2FSPROGS=y
|
||||
BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y
|
||||
@@ -150,9 +156,6 @@ BR2_PACKAGE_MDNS_ALIAS=y
|
||||
BR2_PACKAGE_ONIEPROM=y
|
||||
BR2_PACKAGE_ROUSETTE=y
|
||||
BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y
|
||||
IMAGE_ITB_AUX=y
|
||||
IMAGE_ITB_QCOW=y
|
||||
IMAGE_ITB_RAUC=y
|
||||
IMAGE_README=y
|
||||
IMAGE_DDI=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import random
|
||||
import struct
|
||||
|
||||
class ID:
|
||||
def __init__(self):
|
||||
charset = "abcdefghijklmnopqrstuvwxyz"
|
||||
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
charset += "0123456789"
|
||||
|
||||
b = random.choices(charset, k=32)
|
||||
self.bytes = "".join(b).encode()
|
||||
|
||||
def dashed(self):
|
||||
return self.bytes[0:6].decode() + \
|
||||
"-" + self.bytes[6:10].decode() + \
|
||||
"-" + self.bytes[10:14].decode() + \
|
||||
"-" + self.bytes[14:18].decode() + \
|
||||
"-" + self.bytes[18:22].decode() + \
|
||||
"-" + self.bytes[22:26].decode() + \
|
||||
"-" + self.bytes[26:32].decode()
|
||||
|
||||
def crc32(data: bytes) -> int:
|
||||
crc = 0xf597a6cf
|
||||
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
if crc & 1:
|
||||
crc = (crc >> 1) ^ 0xedb88320
|
||||
else:
|
||||
crc >>= 1
|
||||
|
||||
return crc & 0xffffffff
|
||||
|
||||
def sendfile(dst, src, count):
|
||||
while count > 0:
|
||||
chunk = os.sendfile(dst, src, None, count)
|
||||
if chunk < 0:
|
||||
raise OSError("sendfile")
|
||||
|
||||
count -= chunk
|
||||
|
||||
class Linear:
|
||||
def __init__(self, start, align, path, name=None):
|
||||
self.id = ID()
|
||||
self.name = name if name else os.path.basename(path)
|
||||
self.path = path
|
||||
self.start = start
|
||||
self.align = align
|
||||
self.size = os.path.getsize(path)
|
||||
self.asize = (self.size + align - 1) & ~(align - 1)
|
||||
|
||||
def sendfile(self, fd):
|
||||
pad = self.asize - self.size
|
||||
|
||||
with open(self.path, "rb") as f:
|
||||
sendfile(fd, f.fileno(), self.size)
|
||||
|
||||
if pad:
|
||||
os.write(fd, b"\0" * pad)
|
||||
|
||||
def meta(self):
|
||||
return f""" {self.name} {{
|
||||
id = "{self.id.dashed()}"
|
||||
status = ["READ", "WRITE", "VISIBLE"]
|
||||
segment_count = 1
|
||||
|
||||
segment1 {{
|
||||
start_extent = 0
|
||||
extent_count = {self.asize // self.align }
|
||||
type = "striped"
|
||||
stripe_count = 1
|
||||
stripes = [
|
||||
"pv0", {self.start // self.align }
|
||||
]
|
||||
}}
|
||||
}}"""
|
||||
|
||||
class InternalPV:
|
||||
def __init__(self, images=[], align=(1 << 20), creation_time=0):
|
||||
self.pvid = ID()
|
||||
self.vgid = ID()
|
||||
self.align = align
|
||||
self.creation_time = creation_time
|
||||
|
||||
self.lvs = []
|
||||
self.dsize = 0
|
||||
for img in images:
|
||||
lv = Linear(self.dsize, align, img)
|
||||
self.lvs.append(lv)
|
||||
self.dsize += lv.asize
|
||||
|
||||
# Reserve `align` bytes for primary metadata area, the size
|
||||
# used by all LVs, and another `aligned` bytes for the backup
|
||||
# metadata area
|
||||
self.size = align + self.dsize + align
|
||||
|
||||
def write(self, dst):
|
||||
for i in range(4):
|
||||
dst.write(self._label(i))
|
||||
|
||||
dst.write(b"\0" * 0x800)
|
||||
|
||||
dst.write(self._metadata(True))
|
||||
|
||||
for lv in self.lvs:
|
||||
lv.sendfile(dst.fileno())
|
||||
|
||||
dst.write(self._metadata(False))
|
||||
|
||||
def _label(self, sector: int):
|
||||
headfmt, tailfmt = "<8s Q I", "<I8s 32sQ QQ QQ QQ QQ QQ II"
|
||||
padsize = 512 - struct.calcsize(headfmt) - struct.calcsize(tailfmt)
|
||||
|
||||
tail = struct.pack(tailfmt,
|
||||
32, b"LVM2 001",
|
||||
self.pvid.bytes, self.size,
|
||||
# Data area (size=0 ~= all available space (?))
|
||||
self.align, 0,
|
||||
0, 0,
|
||||
# Primary metadata area
|
||||
0x1000, self.align - 0x1000,
|
||||
# Backup metadata area
|
||||
self.align + self.dsize, self.align,
|
||||
0, 0,
|
||||
# Supported extensions (Unsure which
|
||||
# extensions this maps to, but it makes the
|
||||
# LVM tooling happy :))
|
||||
2, 1) + b"\0" * padsize
|
||||
|
||||
head = struct.pack(headfmt, b"LABELONE", sector, crc32(tail))
|
||||
|
||||
return head + tail
|
||||
|
||||
def _metadata(self, primary):
|
||||
headfmt, tailfmt = "<I", "<16sI QQ QQII QQII"
|
||||
padsize = 512 - struct.calcsize(headfmt) - struct.calcsize(tailfmt)
|
||||
|
||||
offs = 0x1000 if primary else (self.align + self.dsize)
|
||||
size = self.align - 0x1000 if primary else self.align
|
||||
|
||||
txt = self._meta_txt()
|
||||
txtpadsize = size - 512 - len(txt)
|
||||
|
||||
tail = struct.pack(tailfmt,
|
||||
b" LVM2 x[5A%r0N*>", 1,
|
||||
offs, size,
|
||||
512, len(txt), crc32(txt), 0,
|
||||
0, 0, 0, 0) + b"\0" * padsize
|
||||
|
||||
head = struct.pack(headfmt, crc32(tail))
|
||||
|
||||
return head + tail + txt + bytes([0] * txtpadsize)
|
||||
|
||||
def _meta_txt(self):
|
||||
return f"""internal {{
|
||||
id = "{self.vgid.dashed()}"
|
||||
seqno = 1
|
||||
status = ["RESIZEABLE", "READ", "WRITE"]
|
||||
extent_size = {self.align >> 9}
|
||||
max_pv = 0
|
||||
max_lv = 0
|
||||
metadata_copies = 2
|
||||
|
||||
physical_volumes {{
|
||||
pv0 {{
|
||||
id = "{self.pvid.dashed()}"
|
||||
dev = "/dev/internal"
|
||||
status = ["ALLOCATABLE"]
|
||||
dev_size = {self.size >> 9}
|
||||
pe_start = {self.align >> 9}
|
||||
pe_count = {self.dsize // self.align }
|
||||
}}
|
||||
}}
|
||||
|
||||
logical_volumes {{
|
||||
{"\n".join([lv.meta() for lv in self.lvs])}
|
||||
}}
|
||||
}}
|
||||
|
||||
contents = "Text Format Volume Group"
|
||||
version = 1
|
||||
description = "Internal image storage"
|
||||
creation_host = "infix-build-system"
|
||||
creation_time = {self.creation_time}
|
||||
""".encode()
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description="Create LVM PV containing a set of images")
|
||||
parser.add_argument("-T", "--creation-time", metavar="TIMESTAMP",
|
||||
default=int(datetime.datetime.now().timestamp()))
|
||||
parser.add_argument("images", metavar="IMAGE", nargs="*")
|
||||
args = parser.parse_args()
|
||||
|
||||
random.seed(args.creation_time)
|
||||
|
||||
pv = InternalPV(args.images, creation_time=args.creation_time)
|
||||
pv.write(sys.stdout.buffer)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user