Add install-to-disk.sh, and get the GPU firmware into the initramfs
Some checks failed
build image / build (push) Has been cancelled
Some checks failed
build image / build (push) Has been cancelled
Validated by running it on the machine, twice, and inspecting the result. Two properties of this laptop rule out the obvious approach, and the script exists mainly to encode them. Its internal UFS reports 4096-byte logical sectors, so the image — built with 512-byte geometry — cannot be dd'd onto it; the GPT header and every partition offset would land in the wrong place. And there are no EFI runtime variables, so efibootmgr cannot register a boot entry and GRUB has to sit at the removable-media path where the firmware looks unprompted. Three things the validation runs caught that review would not have: rsync is not in the image. I had put it in the build container and never in the package list, so the first run died at the copy. It now falls back to tar (--xattrs-include='*', or SELinux labels are silently dropped and the result does not boot), and rsync is in base.pkgs for the progress output. Copying a live root makes tar exit non-zero — files change underneath it, and this machine's clock is wrong besides, so every mtime looks like it is in the future. With pipefail that aborted the install after the root filesystem and before /boot, leaving a half-installed disk that looked plausible. Warning-level exits are now tolerated and only a fatal exit 2 stops the run. systemd-machine-id-setup keeps an existing valid id, and one had just been copied off the stick, so the installed system was a clone. The file is removed first now. Also: msm_dpu probes ~6s in, while the initramfs is still root, and asks for qcom/a630_sqe.fw before the real filesystem carrying it is reachable. It never retries. Adding the Adreno firmware to the initramfs is a few tens of kilobytes. c630-firmware does the same for the DSP blobs once they exist, since dracut rejects install_items globs that match nothing. chrony, because the RTC reads 1970 and nothing was correcting it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
This commit is contained in:
19
README.md
19
README.md
@@ -131,10 +131,23 @@ Confirmed on hardware, from a USB stick:
|
||||
subsystem, WiFi and IPA all appear as platform devices, so
|
||||
`DEVICE_CMDLINE` and the DTB are right
|
||||
- The framebuffer console works (`simple-framebuffer`, 240x67)
|
||||
- The root filesystem mounts and systemd starts
|
||||
- The root filesystem mounts, systemd starts, and it reaches a login prompt
|
||||
- Networking works over a USB WiFi dongle, and sshd is reachable
|
||||
- `build/install-to-disk.sh` copies it onto the internal UFS
|
||||
|
||||
Not yet confirmed: reaching a login prompt, and anything past it. SELinux is
|
||||
shipped permissive — see below.
|
||||
Not yet confirmed: booting from the internal drive rather than USB.
|
||||
|
||||
Onboard WiFi does not appear at all, and audio, sensors, video decode and
|
||||
accelerated graphics are all absent — every one of them waiting on the
|
||||
model-signed firmware described in [docs/firmware.md](docs/firmware.md). On
|
||||
this machine Windows has been wiped, so those blobs are gone; the kernel names
|
||||
each missing file explicitly in `dmesg`.
|
||||
|
||||
The internal drive needs `build/install-to-disk.sh` rather than `dd`: its UFS
|
||||
uses 4096-byte logical sectors, which the 512-byte image geometry cannot be
|
||||
written onto directly. See [docs/install.md](docs/install.md).
|
||||
|
||||
SELinux is shipped permissive — see below.
|
||||
|
||||
### SELinux
|
||||
|
||||
|
||||
254
build/install-to-disk.sh
Executable file
254
build/install-to-disk.sh
Executable file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/bash
|
||||
#
|
||||
# Copy the running system onto the C630's internal storage.
|
||||
#
|
||||
# Run this from the USB-booted image, on the laptop itself:
|
||||
#
|
||||
# sudo ./install-to-disk.sh --dry-run # show the plan, touch nothing
|
||||
# sudo ./install-to-disk.sh # do it, after confirmation
|
||||
#
|
||||
# Why this rather than dd'ing the image at the internal drive:
|
||||
#
|
||||
# * The internal UFS reports 4096-byte logical sectors. The disk image is
|
||||
# built with 512-byte sector geometry, so its GPT header and every
|
||||
# partition offset would land in the wrong place. Partitioning has to
|
||||
# happen natively, on the device, at its own sector size.
|
||||
#
|
||||
# * There are no EFI variables on this machine — efibootmgr reports "EFI
|
||||
# variables are not supported on this system" — so nothing can register a
|
||||
# boot entry. GRUB has to sit at the removable-media path,
|
||||
# EFI/BOOT/BOOTAA64.EFI, where the firmware looks without being told.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TARGET=/dev/sda
|
||||
ESP_SIZE=1GiB
|
||||
BOOT_SIZE=1GiB
|
||||
ASSUME_YES=0
|
||||
DRY_RUN=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: sudo $0 [options]
|
||||
|
||||
--target DEV Disk to install onto (default: ${TARGET})
|
||||
--esp-size SIZE EFI system partition (default: ${ESP_SIZE})
|
||||
--boot-size SIZE /boot partition (default: ${BOOT_SIZE})
|
||||
--dry-run Print the plan and exit without touching anything
|
||||
--yes Skip the confirmation prompt
|
||||
-h, --help This message
|
||||
|
||||
Everything on the target disk is destroyed.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--target) TARGET="$2"; shift 2 ;;
|
||||
--esp-size) ESP_SIZE="$2"; shift 2 ;;
|
||||
--boot-size) BOOT_SIZE="$2"; shift 2 ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
--yes) ASSUME_YES=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
die() { echo "error: $*" >&2; exit 1; }
|
||||
log() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
||||
[ -b "$TARGET" ] || die "$TARGET is not a block device"
|
||||
|
||||
# nvme0n1 -> nvme0n1p1, sda -> sda1
|
||||
partdev() { case "$TARGET" in *[0-9]) echo "${TARGET}p$1" ;; *) echo "${TARGET}$1" ;; esac; }
|
||||
ESP_PART=$(partdev 1); BOOT_PART=$(partdev 2); ROOT_PART=$(partdev 3)
|
||||
|
||||
# --- refuse to eat the system we are running from ------------------------
|
||||
RUNNING_ROOT="$(findmnt -no SOURCE /)"
|
||||
RUNNING_DISK="/dev/$(lsblk -no pkname "$RUNNING_ROOT" 2>/dev/null || true)"
|
||||
if [ "$RUNNING_DISK" = "$TARGET" ]; then
|
||||
die "$TARGET holds the running root filesystem ($RUNNING_ROOT). Boot the USB image and try again."
|
||||
fi
|
||||
while read -r mnt; do
|
||||
[ -n "$mnt" ] && die "$TARGET has a mounted partition at $mnt — unmount it first"
|
||||
done < <(lsblk -nro MOUNTPOINT "$TARGET" | grep -v '^$' || true)
|
||||
|
||||
SIZE_H="$(lsblk -dno SIZE "$TARGET")"
|
||||
SECTOR="$(blockdev --getss "$TARGET")"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Target ${TARGET} (${SIZE_H}, ${SECTOR}-byte logical sectors)
|
||||
Running from ${RUNNING_ROOT}
|
||||
|
||||
${ESP_PART} ${ESP_SIZE} fat32 ESP -> /boot/efi
|
||||
${BOOT_PART} ${BOOT_SIZE} ext4 boot -> /boot
|
||||
${ROOT_PART} rest ext4 fedora -> /
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
echo "dry run — nothing written"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" != 1 ]; then
|
||||
echo "This destroys everything on ${TARGET}."
|
||||
read -rp "Type the target device to confirm: " reply
|
||||
[ "$reply" = "$TARGET" ] || die "not confirmed"
|
||||
fi
|
||||
|
||||
MNT=$(mktemp -d /tmp/c630-install.XXXXXX)
|
||||
cleanup() { umount -R "$MNT" 2>/dev/null || true; rmdir "$MNT" 2>/dev/null || true; }
|
||||
trap cleanup EXIT
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Partitioning ${TARGET}"
|
||||
# ---------------------------------------------------------------------------
|
||||
# sgdisk works in the device's own sector size, which is the entire point of
|
||||
# doing this here rather than in the image.
|
||||
wipefs -a "$TARGET" >/dev/null
|
||||
sgdisk --zap-all "$TARGET" >/dev/null
|
||||
sgdisk \
|
||||
--new "1:0:+${ESP_SIZE}" --typecode 1:ef00 --change-name 1:ESP \
|
||||
--new "2:0:+${BOOT_SIZE}" --typecode 2:8300 --change-name 2:boot \
|
||||
--new "3:0:0" --typecode 3:8300 --change-name 3:root \
|
||||
"$TARGET" >/dev/null
|
||||
partprobe "$TARGET" 2>/dev/null || true
|
||||
udevadm settle
|
||||
sgdisk --print "$TARGET"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Creating filesystems"
|
||||
# ---------------------------------------------------------------------------
|
||||
# Let mkfs.vfat take the device's 4096-byte sectors rather than forcing 512 —
|
||||
# this firmware booted Windows off this disk, so it reads it natively.
|
||||
mkfs.vfat -F 32 -n ESP "$ESP_PART" >/dev/null
|
||||
|
||||
# orphan_file and metadata_csum_seed are recent ext4 features some GRUB builds
|
||||
# cannot read, and GRUB has to read /boot.
|
||||
mkfs.ext4 -q -F -O ^orphan_file,^metadata_csum_seed -L boot "$BOOT_PART"
|
||||
mkfs.ext4 -q -F -O ^orphan_file,^metadata_csum_seed -L fedora "$ROOT_PART"
|
||||
|
||||
ROOT_UUID=$(blkid -s UUID -o value "$ROOT_PART")
|
||||
BOOT_UUID=$(blkid -s UUID -o value "$BOOT_PART")
|
||||
ESP_UUID=$(blkid -s UUID -o value "$ESP_PART")
|
||||
echo "root=${ROOT_UUID} boot=${BOOT_UUID} esp=${ESP_UUID}"
|
||||
|
||||
mount "$ROOT_PART" "$MNT"
|
||||
mkdir -p "$MNT/boot"
|
||||
mount "$BOOT_PART" "$MNT/boot"
|
||||
mkdir -p "$MNT/boot/efi"
|
||||
mount "$ESP_PART" "$MNT/boot/efi"
|
||||
|
||||
# Copy one filesystem's worth of tree, hard links, ACLs and xattrs intact.
|
||||
# rsync is not in the minimal image and tar is, so tar is the fallback rather
|
||||
# than the exception. --xattrs-include='*' matters: without it tar drops
|
||||
# security.selinux, and an unlabelled root does not boot.
|
||||
copy_tree() {
|
||||
local src="$1" dst="$2"
|
||||
if command -v rsync >/dev/null; then
|
||||
# 24 is "some files vanished while transferring", which is normal when
|
||||
# copying a filesystem that is in use.
|
||||
rsync -aHAX -x --info=progress2 "$src/" "$dst/" || [ $? -eq 24 ]
|
||||
return
|
||||
fi
|
||||
|
||||
# This copies a *live* root, so files legitimately change underneath us and
|
||||
# tar exits 1 to say so. The clock is also often wrong on this machine
|
||||
# (no working RTC), which makes every mtime look like it is in the future.
|
||||
# Neither is a reason to abandon the install — but exit 2 is.
|
||||
local st
|
||||
set +e
|
||||
tar --create --file - --one-file-system --numeric-owner \
|
||||
--warning=no-timestamp --warning=no-file-changed --warning=no-file-removed \
|
||||
--xattrs --xattrs-include='*' --acls --selinux -C "$src" . \
|
||||
| tar --extract --file - --numeric-owner \
|
||||
--warning=no-timestamp \
|
||||
--xattrs --xattrs-include='*' --acls --selinux -C "$dst"
|
||||
st=("${PIPESTATUS[@]}")
|
||||
set -e
|
||||
[ "${st[0]}" -le 1 ] || die "reading ${src} failed (tar exit ${st[0]})"
|
||||
[ "${st[1]}" -le 1 ] || die "writing ${dst} failed (tar exit ${st[1]})"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Copying the root filesystem"
|
||||
# ---------------------------------------------------------------------------
|
||||
# One filesystem only, so /boot, /boot/efi and the pseudo-filesystems are all
|
||||
# skipped here and dealt with separately (or not at all).
|
||||
copy_tree / "$MNT"
|
||||
|
||||
log "Copying /boot"
|
||||
copy_tree /boot "$MNT/boot"
|
||||
|
||||
log "Copying the ESP"
|
||||
# vfat carries no ownership, permissions or xattrs, so do not ask for any.
|
||||
cp -r /boot/efi/. "$MNT/boot/efi/"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Pointing the new system at itself"
|
||||
# ---------------------------------------------------------------------------
|
||||
cat > "$MNT/etc/fstab" <<EOF
|
||||
UUID=${ROOT_UUID} / ext4 defaults 1 1
|
||||
UUID=${BOOT_UUID} /boot ext4 defaults 1 2
|
||||
UUID=${ESP_UUID} /boot/efi vfat umask=0077,shortname=winnt 0 2
|
||||
EOF
|
||||
|
||||
# Every place the old root UUID appears has to become the new one, or the
|
||||
# installed system quietly boots the USB stick's root — or nothing at all.
|
||||
OLD_ROOT_UUID=$(blkid -s UUID -o value "$RUNNING_ROOT")
|
||||
OLD_BOOT_UUID=$(findmnt -no UUID /boot 2>/dev/null || true)
|
||||
|
||||
for f in "$MNT"/boot/loader/entries/*.conf; do
|
||||
[ -e "$f" ] || continue
|
||||
sed -i "s/${OLD_ROOT_UUID}/${ROOT_UUID}/g" "$f"
|
||||
echo " $(basename "$f")"
|
||||
done
|
||||
|
||||
[ -f "$MNT/etc/kernel/cmdline" ] &&
|
||||
sed -i "s/${OLD_ROOT_UUID}/${ROOT_UUID}/g" "$MNT/etc/kernel/cmdline"
|
||||
|
||||
if [ -n "$OLD_BOOT_UUID" ]; then
|
||||
sed -i "s/${OLD_BOOT_UUID}/${BOOT_UUID}/g" "$MNT/boot/grub2/grub.cfg"
|
||||
for g in "$MNT"/boot/efi/EFI/*/grub.cfg; do
|
||||
[ -e "$g" ] && sed -i "s/${OLD_BOOT_UUID}/${BOOT_UUID}/g" "$g"
|
||||
done
|
||||
fi
|
||||
|
||||
# No EFI variables on this machine, so the firmware will only find a bootloader
|
||||
# at the removable-media path. Make sure one is there.
|
||||
mkdir -p "$MNT/boot/efi/EFI/BOOT"
|
||||
if [ -f "$MNT/boot/efi/EFI/fedora/grubaa64.efi" ]; then
|
||||
cp "$MNT/boot/efi/EFI/fedora/grubaa64.efi" "$MNT/boot/efi/EFI/BOOT/BOOTAA64.EFI"
|
||||
fi
|
||||
|
||||
# A fresh identity, so the installed system is not a clone of the stick — two
|
||||
# machines sharing one machine-id confuses DHCP leases and journal collection.
|
||||
# The removal matters: systemd-machine-id-setup keeps an existing valid id, and
|
||||
# one was just copied off the stick, so without this it is a no-op.
|
||||
#
|
||||
# Writing a real id rather than leaving the file empty also keeps systemd from
|
||||
# treating the next boot as a first boot and prompting on the console for
|
||||
# locale and passwords — which, on a machine reached over ssh, would look
|
||||
# exactly like a hang.
|
||||
rm -f "$MNT/etc/machine-id"
|
||||
systemd-machine-id-setup --root="$MNT" >/dev/null 2>&1 ||
|
||||
uuidgen | tr -d - > "$MNT/etc/machine-id"
|
||||
|
||||
# The root partition already fills the disk, so there is nothing to grow.
|
||||
mkdir -p "$MNT/var/lib/c630" && touch "$MNT/var/lib/c630/growfs-done"
|
||||
|
||||
sync
|
||||
log "Done"
|
||||
cat <<EOF
|
||||
|
||||
Installed to ${TARGET}. Reboot and pick the internal drive from the boot menu
|
||||
(Fn+F12), or remove the USB stick.
|
||||
|
||||
root ${ROOT_UUID}
|
||||
boot ${BOOT_UUID}
|
||||
esp ${ESP_UUID}
|
||||
|
||||
EOF
|
||||
@@ -56,6 +56,11 @@ wpa_supplicant
|
||||
iw
|
||||
openssh-server
|
||||
|
||||
# The C630's RTC comes up at 1970 and the clock drifts badly, which breaks TLS
|
||||
# and makes every file look like it has a future timestamp. NTP fixes it as
|
||||
# soon as there is a network.
|
||||
chrony
|
||||
|
||||
# --- power / peripherals ------------------------------------------------
|
||||
bluez
|
||||
alsa-utils
|
||||
@@ -64,6 +69,9 @@ libqmi-utils
|
||||
# --- basics -------------------------------------------------------------
|
||||
sudo
|
||||
shadow-utils
|
||||
# build/install-to-disk.sh falls back to tar without it, but rsync gives
|
||||
# progress and is worth having on a machine you are bringing up by hand.
|
||||
rsync
|
||||
vim-minimal
|
||||
less
|
||||
bash-completion
|
||||
|
||||
@@ -49,36 +49,41 @@ See [firmware.md](firmware.md) if it cannot find the Windows partition.
|
||||
|
||||
## Installing to internal storage
|
||||
|
||||
Once the USB image is behaving, copy it onto the internal UFS.
|
||||
Once the USB image is behaving, copy it onto the internal UFS. Boot from the
|
||||
USB stick and run, on the laptop:
|
||||
|
||||
Shrink the Windows partition from within Windows (**Disk Management → Shrink
|
||||
Volume**) rather than from Linux — Windows is fussy about its own filesystem
|
||||
being moved underneath it.
|
||||
|
||||
Then, booted from USB, write the image to the free space. The simplest approach
|
||||
that keeps Windows intact is to create the partitions by hand and copy the
|
||||
filesystems across rather than `dd`-ing the whole image, since `dd` would
|
||||
overwrite the existing GPT and the Windows ESP:
|
||||
|
||||
1. `sudo gdisk /dev/sda` — add a `/boot` partition (1 GiB, type 8300) and a root
|
||||
partition (type 8300) in the free space. Keep the existing Windows ESP.
|
||||
2. `mkfs.ext4 -O ^orphan_file,^metadata_csum_seed /dev/sdaN` for both.
|
||||
3. Copy the running system across with `rsync -aHAX --exclude=/dev --exclude=/proc
|
||||
--exclude=/sys --exclude=/run --exclude=/boot`, then `/boot` separately.
|
||||
4. Copy `EFI/fedora` from the USB stick's ESP onto the Windows ESP, and edit
|
||||
`EFI/fedora/grub.cfg` so the `--fs-uuid` matches the new `/boot`.
|
||||
5. Update `/etc/fstab` and the `root=UUID=` in
|
||||
`/boot/loader/entries/c630-*.conf` to the new UUIDs.
|
||||
|
||||
Finally, point the firmware at GRUB from an Administrator command prompt in
|
||||
Windows:
|
||||
|
||||
```
|
||||
bcdedit /set {bootmgr} path \EFI\fedora\grubaa64.efi
|
||||
```sh
|
||||
sudo ./build/install-to-disk.sh --dry-run # show the plan, touch nothing
|
||||
sudo ./build/install-to-disk.sh # do it
|
||||
```
|
||||
|
||||
Windows resets this on some updates. To get back, boot the USB stick and run it
|
||||
again, or use `efibootmgr` from Linux.
|
||||
It partitions the disk, copies the running system across, and rewrites every
|
||||
UUID that has to change. Reboot afterwards and pick the internal drive from the
|
||||
boot menu (**Fn+F12**), or just pull the stick out.
|
||||
|
||||
**This wipes the target disk.** If you still have Windows and want to keep it,
|
||||
do not run this — shrink the Windows partition from within Windows first and
|
||||
adapt the steps by hand.
|
||||
|
||||
Two things about this machine make the obvious approaches fail, both of which
|
||||
the script handles:
|
||||
|
||||
**You cannot `dd` the image onto the internal drive.** The UFS reports
|
||||
**4096-byte logical sectors**; the disk image is built with 512-byte sector
|
||||
geometry. Its GPT header and every partition offset would land in the wrong
|
||||
place. The disk has to be partitioned on the machine, at its own sector size.
|
||||
|
||||
**`efibootmgr` does not work here.** There are no EFI runtime variables —
|
||||
`efibootmgr` reports *"EFI variables are not supported on this system"* — so
|
||||
nothing can register a boot entry. GRUB therefore goes at the removable-media
|
||||
path, `EFI/BOOT/BOOTAA64.EFI`, where the firmware finds it unprompted.
|
||||
|
||||
If you would rather do it by hand, the sequence is: `sgdisk` an ESP, a `/boot`
|
||||
and a root partition; `mkfs.vfat` and `mkfs.ext4 -O
|
||||
^orphan_file,^metadata_csum_seed`; copy `/`, `/boot` and the ESP separately
|
||||
(preserving xattrs, or SELinux labels are lost); then update `/etc/fstab`,
|
||||
`/etc/kernel/cmdline`, `root=UUID=` in `/boot/loader/entries/*.conf`, and the
|
||||
`--fs-uuid` in both `/boot/grub2/grub.cfg` and `EFI/fedora/grub.cfg`.
|
||||
|
||||
## If it does not boot
|
||||
|
||||
|
||||
@@ -10,3 +10,16 @@ add_drivers+=" ufshcd-core ufshcd-pltfrm ufs-qcom phy-qcom-qmp-ufs "
|
||||
|
||||
# Needed before the display comes up, and cheap to include.
|
||||
add_drivers+=" nvmem_qfprom qcom_scm rtc-pm8xxx "
|
||||
|
||||
# msm_dpu probes about six seconds in — while the initramfs is still the root
|
||||
# filesystem, so anything under /usr/lib/firmware on the real root is not yet
|
||||
# reachable. It asks for the Adreno 630 firmware, does not find it, and does not
|
||||
# come back to try again:
|
||||
#
|
||||
# msm_dpu ae01000.display-controller: Direct firmware load for
|
||||
# qcom/a630_sqe.fw failed with error -2
|
||||
#
|
||||
# The files are installed — they are simply not visible that early. A few tens
|
||||
# of kilobytes in the initramfs fixes it. The model-signed zap shader is a
|
||||
# separate problem; see docs/firmware.md.
|
||||
install_items+=" /usr/lib/firmware/qcom/a630_sqe.fw.xz /usr/lib/firmware/qcom/a630_gmu.bin.xz /usr/lib/firmware/qcom/sdm845/a630_zap.mbn.xz "
|
||||
|
||||
@@ -34,7 +34,17 @@ fi
|
||||
qcom-firmware-extract "$@"
|
||||
|
||||
echo
|
||||
echo "Regenerating the initramfs so the DSP firmware is available early..."
|
||||
# The ADSP has to be up before the root filesystem is mounted, so these have to
|
||||
# travel in the initramfs. Only write the glob now that the files exist —
|
||||
# dracut objects to install_items that match nothing.
|
||||
FW_DIR=/usr/lib/firmware/updates/qcom/sdm850/LENOVO/81JL
|
||||
if compgen -G "${FW_DIR}/*.mbn" >/dev/null || compgen -G "${FW_DIR}/*.elf" >/dev/null; then
|
||||
printf 'install_items+=" %s/*.mbn %s/*.elf "\n' "$FW_DIR" "$FW_DIR" \
|
||||
> /etc/dracut.conf.d/20-c630-extracted-firmware.conf
|
||||
echo "Extracted firmware will be included in the initramfs."
|
||||
fi
|
||||
|
||||
echo "Regenerating the initramfs..."
|
||||
dracut --force --regenerate-all
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
Reference in New Issue
Block a user