Compare commits

...
10 Commits
Author SHA1 Message Date
ergosteurandClaude Opus 5.5 7b64fdb539 Document remaining neon customizations from shell history; fix zram loose ends
Persist vm.swappiness=100 in /etc/sysctl.d/99-zram.conf and delete the
unused /swapfile. New NEON-CUSTOMIZATIONS.md covers keyd, USB-C DP
modules, HWE-edge kernel, tmpfs /tmp and localepurge, r8152 DKMS
history, tailscale, and third-party repos/tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:08:43 -04:00
ergosteurandClaude Opus 5.5 637dd035cc Document zram swap setup on neon
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:06:45 -04:00
ergosteurandClaude Opus 5.5 f55d51fccc Document neon audio fixes (avs driver, topologies, cros UCM)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:05:00 -04:00
ergosteurandClaude Opus 5.5 c7231628b8 Update docs for FydeOS install on eMMC, stale Flex UEFI entry removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:48:39 -04:00
ergosteurandClaude Opus 5.5 172f5c9f75 Note FydeOS iris build supports Intel 6th-14th gen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:43:40 -04:00
ergosteurandClaude Opus 5.5 083ca95134 Note decision to keep ChromeOS GRUB label for FydeOS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:43:12 -04:00
ergosteurandClaude Opus 5.5 442cffaec2 Document Flex/FydeOS taking over UEFI BootOrder on updates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:42:27 -04:00
ergosteurandClaude Opus 5.5 0b1e709366 Note FydeOS image compatibility with install-flex-emmc.sh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:27:52 -04:00
ergosteurandClaude Opus 5.5 08ae7257ca Move ChromeOS Flex notes into CHROMEOS-FLEX.md
Standalone doc covering install/reinstall via install-flex-emmc.sh, why a
raw dd boots as live media, chromeos-install-from-Linux gotchas, the
resulting layout, and boot selection. CLAUDE.md keeps a short quick
reference pointing to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:22:23 -04:00
ergosteurandClaude Opus 5.5 93f4eca05c Add install-flex-emmc.sh for a proper ChromeOS Flex install on eMMC
The earlier raw dd of the Flex recovery image left the installer layout
(2MB ROOT-B placeholder, fixed 6GB STATE), so Flex booted as live media
and couldn't A/B update. The script runs the image's own chromeos-install
against the eMMC from neon, chrooted into the image in a private mount
namespace with all other disks masked. Handles the cros_efi cmdline
requirement, udisks auto-mount interference, and unreliable exit status.
Tested end to end on hp-chell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:20:08 -04:00
6 changed files with 906 additions and 50 deletions
+144
View File
@@ -0,0 +1,144 @@
# Audio on hp-chell (KDE neon)
Working state (verified 2026-09-27): speakers, headset jack and internal
mic work under neon. PipeWire/WirePlumber shows `Built-in Audio Speaker`
and `Built-in Audio Internal Microphone`, and the headset appears when
something is plugged in. None of this works out of the box. It took the
fixes below, all applied **2026-03-05**, reconstructed on 2026-09-27
from `~/.bash_history` and file timestamps.
## Hardware
HP Chromebook 13 G1 ("chell", Skylake). The audio DSP is Intel Skylake
SST, driven by the kernel's **`avs`** driver (`snd_soc_avs`), with three
board-specific machine drivers. Each one is its own ALSA card:
| Card | Driver | What |
|---|---|---|
| `avs_ssm4567` | `snd_soc_avs_ssm4567` | speakers: 2x ADI SSM4567 amps |
| `avs_nau8825` | `snd_soc_avs_nau8825` | headset jack: Nuvoton NAU88L25 codec |
| `avs_dmic` | `snd_soc_avs_dmic` | internal digital mic |
| `avs_probe_mb` | `snd_soc_avs_probe` | DSP debug probe, not a real device |
The cards report the long name `Google-Chell-1.0`.
Speaker safety: the upstream script disables speakers on Chromebooks
with a **MAX98357A** amp (no volume limiter, so it can blow the
speakers). Chell uses **SSM4567**, not MAX98357A, so that doesn't apply
and the speakers are enabled.
## The fixes
### 1. WeirdTreeThing's `chromebook-linux-audio` script
Checkout: `~/github/chromebook-linux-audio`
(<https://github.com/WeirdTreeThing/chromebook-linux-audio>, shallow
clone at `8c7e15e`, 2026-02-16). Run as `sudo ./setup-audio -b chell`.
`-b` is the branch of the UCM repo to clone, and there's no `chell`
branch; a plain `./setup-audio` is what's intended. For a Skylake board
(`skl`/`kbl`/`apl`) it:
- **Switches the DSP driver to `avs`**:
`/etc/modprobe.d/snd-avs.conf`
```
options snd-intel-dspcfg dsp_driver=4
options snd-soc-avs ignore_fw_version=1
options snd-soc-avs obsolete_card_names=1
```
`dsp_driver=4` selects `avs`. Without it `snd-intel-dspcfg` picks
legacy HDA or SOF, and neither works on chell. `obsolete_card_names=1`
gives the `avs_ssm4567`-style card names that the UCM profiles below
match on.
- **Installs Chromebook UCM profiles**: clones
<https://github.com/WeirdTreeThing/alsa-ucm-conf-cros> and copies
`ucm2/` into `/usr/share/alsa/ucm2/`, and `overrides/` into
`/usr/share/alsa/ucm2/conf.d/`. The ones this board uses are
`conf.d/avs_ssm4567/`, `conf.d/avs_nau8825/` and `conf.d/avs_dmic/`
(`avs_ssm4567/HiFi.conf` sets amp boost, the master volumes at 219,
and `Left/Right Speaker Switch`).
- **Adds WirePlumber headroom**:
`/etc/wireplumber/wireplumber.conf.d/51-increase-headroom.conf` sets
`api.alsa.headroom = 2048` on all `alsa_output.*` nodes (the script
says it fixes instability and crashes on these DSPs).
- Runs `alsactl init`.
### 2. Duplicate modprobe option (harmless)
`/etc/modprobe.d/inteldsp.conf`: `options snd-intel-dspcfg dsp_driver=4`,
written by hand a few minutes before the script ran. It duplicates the
first line of `snd-avs.conf`. You can delete it.
### 3. AVS topology files, downloaded by hand
The `avs` driver needs a per-board **topology** blob for each machine
driver. Ubuntu's `linux-firmware-intel-misc` ships only the base DSP
firmware (`/lib/firmware/intel/avs/skl/dsp_basefw.bin.zst` plus one
module), not the topologies, and the script doesn't provide them
either. They were fetched straight from upstream linux-firmware:
```
cd /lib/firmware/intel/avs/
BASE=https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/avs
sudo wget $BASE/ssm4567-tplg.bin $BASE/nau8825-tplg.bin $BASE/dmic-tplg.bin
```
Installed: `ssm4567-tplg.bin`, `nau8825-tplg.bin` and `dmic-tplg.bin`
(not owned by any package, so apt won't update or remove them). The
history also shows `hda-80862809-tplg.bin` and
`hda-8086-generic-tplg.bin` being fetched, but they aren't present now
and aren't needed; chell has no HDA codec.
### 4. UCM `Syntax 7` → `Syntax 6`
The cros UCM repo writes its files as `Syntax 7`, but neon's alsa-lib
(`libasound2t64` 1.2.11) only understands up to `Syntax 6`, so
`alsaucm -c avs_ssm4567 list _verbs` failed and PipeWire fell back to
raw, unusable profiles. Fixed with:
```
sudo find /usr/share/alsa/ucm2/ -type f -exec sed -i 's/Syntax 7/Syntax 6/g' {} +
```
As of 2026-09-27 there are 0 `Syntax 7` files and `dpkg -V
alsa-ucm-conf` reports no modified package files, so the sed only hit
the cros files. **If the cros UCM is ever re-copied** (re-running
`setup-audio`, or `cp -r alsa-ucm-conf-cros/ucm2/*`), re-run this sed,
unless alsa-lib has been upgraded to a version that accepts Syntax 7.
Then `systemctl --user restart wireplumber pipewire pipewire-pulse`
(or reboot).
## Reapplying on a fresh neon install
```
# 1. script: avs driver, cros UCM, wireplumber headroom
git clone --depth 1 https://github.com/WeirdTreeThing/chromebook-linux-audio
cd chromebook-linux-audio && sudo ./setup-audio
# 2. topologies (not in Ubuntu's linux-firmware packages)
BASE=https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/avs
cd /lib/firmware/intel/avs && sudo wget $BASE/ssm4567-tplg.bin $BASE/nau8825-tplg.bin $BASE/dmic-tplg.bin
# 3. UCM syntax downgrade for alsa-lib 1.2.11
sudo find /usr/share/alsa/ucm2/ -type f -exec sed -i 's/Syntax 7/Syntax 6/g' {} +
sudo reboot
```
Checks: `aplay -l` should list `avs_ssm4567` / `avs_nau8825`,
`alsaucm -c avs_ssm4567 list _verbs` should print `HiFi`, and
`wpctl status` should show the Speaker sink and the Internal Microphone
source.
## FydeOS on the eMMC has no audio
Seen 2026-09-27, not investigated yet. The neon fixes don't carry over
directly. FydeOS is ChromiumOS: a read-only verified rootfs, CRAS instead
of PipeWire, and its own kernel and UCM. Things to check from a FydeOS
shell (developer mode, or `crosh` → `shell`):
- which DSP driver loaded (`lsmod | grep -E 'avs|sof|skl'`, `dmesg`)
and whether `aplay -l` shows any cards;
- whether `/lib/firmware/intel/avs/*-tplg.bin` exist there;
- whether its UCM has `avs_ssm4567` etc. (`/usr/share/alsa/ucm2`).
The same three pieces (driver selection, topologies, UCM) are the likely
gaps.
+295
View File
@@ -0,0 +1,295 @@
# ChromeOS (Flex / FydeOS) on hp-chell's internal eMMC
**Currently installed: FydeOS** (`iris` build, 2026-09-27). It replaced
ChromeOS Flex; `install-flex-emmc.sh` handles both images.
hp-chell (HP Chromebook 13 G1, MrChromebox UEFI firmware) dual-boots:
| OS | Disk | Notes |
|---|---|---|
| KDE neon | `sda`, external USB/SD card (117GB) | primary OS, owns GRUB |
| FydeOS (was ChromeOS Flex) | `mmcblk0`, internal eMMC (29GB) | installed from neon, see below |
Because neon lives entirely on external media, the eMMC can be wiped and
reinstalled from the running neon system with no risk to neon.
## Installing / reinstalling
```
sudo ./install-flex-emmc.sh ~/Downloads/chromeos_<ver>_reven_recovery_stable-channel_<keys>.bin.zip
```
- Accepts the `.bin.zip` straight from Google's Flex download or an
already-extracted `.bin` (zips are extracted to `/var/tmp/flex-install`).
- Destination defaults to `/dev/mmcblk0`; pass a second argument to
override. **The destination is wiped entirely.**
- Refuses to run if the destination holds the running root filesystem,
shows the target's layout, and asks you to type `yes`.
- Takes about 2.5 minutes. Full installer log: `/var/log/flex-install.log`.
UEFI boot entries from before the run are saved to
`/var/log/flex-install-efiboot-before.txt` and the current ones are
printed at the end.
- Updates the GRUB chainload entry (below) with the new EFI partition's
UUID and runs `update-grub`.
Install history on the eMMC:
- 2026-09-27: ChromeOS Flex `16640.57.0` (stable, `reven`). It
auto-updated to `16640.61.0` on first boot.
- 2026-09-27: **FydeOS** `FydeOS_for_PC_iris_v23.0-SP1-io.bin`
(`amd64-fydeos_iris-io`, `16700.56.23.51`), which replaced Flex.
## Why not just `dd` the image
The Flex recovery `.bin` is a full GPT disk image, but it's the
**installer** layout, not an installed system:
| Partition | Installer image (raw `dd`) | Proper install |
|---|---|---|
| ROOT-A | 2.4GB | 4GB |
| ROOT-B | 2MB placeholder | 4GB (A/B updates) |
| STATE | fixed 6GB | rest of the disk (~20.6GB) |
| unallocated | ~20.7GB | none |
Flex decides whether it's running from installer media with
`/usr/sbin/is_running_from_installer`, which compares the ROOT-A and
ROOT-B sizes. A raw `dd` therefore boots as live media ("Install
ChromeOS Flex / Try it first"), can't auto-update, and never grows
STATE. **There's no flag to set**, and resizing in place isn't
practical (ROOT-B sits in a 2MB gap between the ESP and ROOT-A). This
was the state from 2026-09-04 (raw `dd`) until the 2026-09-27 reinstall.
The fix is to run the image's own `chromeos-install`, which is what the
"Install ChromeOS Flex" button runs, against the eMMC.
## How the script works
1. Adds a temporary udev rule (`/run/udev/rules.d/99-flex-install-ignore.rules`,
`UDISKS_IGNORE=1`) so the Plasma desktop doesn't auto-mount eMMC
partitions mid-install, then unmounts anything already mounted there.
2. Attaches the `.bin` read-only as a partitioned loop device.
3. In a private mount namespace (`unshare -m`; every mount is discarded on
exit), mounts the image's ROOT-A read-only and builds a chroot:
tmpfs `/tmp` `/run` `/var` `/mnt/stateful_partition`, bind `/dev`
`/sys`, fresh `/proc`.
4. **Masks every disk except the destination**: each `/dev/sd*`,
`/dev/nvme*`, other `/dev/mmcblk*`, and `/dev/vd*` node gets
`/dev/null` bind-mounted over it, and `/dev/disk` and `/dev/block`
are hidden. Inside the chroot, reading `/dev/sda` returns 0 bytes, so
neon is unreachable.
5. Runs:
```
chromeos-install --dst /dev/mmcblk0 --payload_image /tmp/payload.bin \
--skip_dst_removable --yes --target_bios efi --debug
```
6. Checks the log for success, re-reads the partition table, and updates
GRUB. Cleanup (loop device, udev rule) runs on exit, including on failure.
## Gotchas when running `chromeos-install` from another Linux
Each of these broke an attempt during the 2026-09-27 install:
- **`No recognized cros_XXX bios option on kernel command line`**:
`chromeos_postinst` reads `/proc/cmdline` for `cros_efi` (or
`cros_legacy` etc.), even though it's passed `--bios efi`. The script
bind-mounts a copy of the command line with `cros_efi` appended over
`/proc/cmdline`. `chromeos-postinst` then re-chroots into the freshly
written root with a *non-recursive* `mount --bind /proc`, which drops
that overmount. So the image's `chroot` (a coreutils shim at
`/bin/chroot`) is also overmounted with a wrapper that re-applies the
fake cmdline inside the target root before exec'ing the real
`coreutils --coreutils-prog=chroot`.
- **`sfdisk: This disk is currently in use`**: udisks auto-mounted the
new STATE partition (`/media/matt/H-STATE`) from a previous attempt.
The udev rule in step 1 above handles this.
- **Exit status can't be trusted**: `chromeos-install` exited 0 while
its inner `chromeos-install.sh` had failed. It also logs harmless
`ERROR cros_installer` lines on success (metrics
`/var/lib/metrics/uma-events`, "Couldn't check the current
permission"). The script treats the run as successful only if the log
has `Installation to '/dev/mmcblk0' complete.` and no final `Error:` line.
- **`Touch(/mnt/stateful_partition/.install_completed) failed`**:
postinst marks the *installer's* STATE as done. In the chroot that's
the read-only image, so the script gives it a throwaway tmpfs.
- Harmless noise: `initctl` (no Upstart), `Failed to find physical
volume`, `BLKRRPART: Device or resource busy`, and ChromeOS ACPI
`GetAcpiSysfsPath` errors (not stock ChromeOS firmware).
- `nohup … &` over SSH got killed when the session closed, so long runs
go through `systemd-run --unit=... -p StandardOutput=file:/var/log/...`.
The log must not be in `/tmp` if another user owns the file (root is
denied by `fs.protected_regular`).
## Resulting layout
FydeOS (current, 2026-09-27):
```
mmcblk0 29.1G
├─mmcblk0p1 23.7G ext4 STATE
├─mmcblk0p2 16M KERN-A
├─mmcblk0p3 2.7G ext4 ROOT-A
├─mmcblk0p4 16M KERN-B
├─mmcblk0p5 2.7G ext4 ROOT-B
├─mmcblk0p6-7 512B KERN-C / ROOT-C
├─mmcblk0p8 16M ext4 OEM
├─mmcblk0p9-10 512B reserved / reserved
├─mmcblk0p11 8M RWFW
└─mmcblk0p12 32M vfat EFI-SYSTEM (UUID F35E-F13C, PARTUUID 18151E4F-...)
```
ChromeOS Flex (2026-09-27, before FydeOS replaced it):
```
mmcblk0 29.1G
├─mmcblk0p1 20.6G ext4 STATE
├─mmcblk0p2 64M KERN-A (priority=1 successful=1)
├─mmcblk0p3 4G ext4 ROOT-A
├─mmcblk0p4 64M KERN-B
├─mmcblk0p5 4G ext4 ROOT-B
├─mmcblk0p6-7 512B KERN-C / ROOT-C
├─mmcblk0p8 16M ext4 OEM
├─mmcblk0p9-11 512B reserved / reserved / RWFW
└─mmcblk0p12 256M vfat EFI-SYSTEM (UUID CBE4-703E)
```
The ESP is copied from the image, so its FAT UUID comes from the image:
`CBE4-703E` for Flex (it survived the Flex reinstall unchanged) and
`F35E-F13C` for FydeOS. The install itself left the UEFI boot entries
and `BootOrder` untouched, but Flex's first auto-update changed them.
See "Flex takes over the UEFI boot order on updates" below.
## Booting ChromeOS (FydeOS)
Two ways:
1. **From GRUB** (neon's menu, shown for 5s): the entry "ChromeOS Flex
(internal eMMC)" in `/etc/grub.d/40_custom` chainloads the eMMC ESP's
own loader: FydeOS's GRUB now (Flex's was `crdyboot`). The title was
kept on purpose, since FydeOS is still ChromeOS.
```
menuentry "ChromeOS Flex (internal eMMC)" {
insmod part_gpt
insmod fat
insmod chain
search --no-floppy --fs-uuid --set=root F35E-F13C
chainloader /EFI/BOOT/BOOTX64.EFI
}
```
Run `sudo update-grub` after editing `40_custom`. The install script
rewrites the `--fs-uuid` value automatically.
2. **From the firmware boot picker** (MrChromebox UEFI, `Esc` during
POST): `efibootmgr -v` shows `neon` (Boot0005, on the USB card) and
`Internal eMMC` (Boot0001), with `BootOrder` `0005,0001,0000,0003,0002`.
With the USB card unplugged, the firmware falls through to `Internal
eMMC` and boots FydeOS with no menu.
## Flex takes over the UEFI boot order on updates
Seen 2026-09-27: after the install, Flex booted fine, found an update,
installed it, and rebooted. The next boot went straight to Flex, and
getting back to neon took the firmware setup. `efibootmgr` afterwards:
```
BootOrder: 0004,0005,0001,0000,0003,0002 (was 0005,0001,0000,0003,0002)
Boot0004* ChromeOS Flex HD(12,GPT,2877e60b-...)/File(\efi\boot\bootx64.efi) <- new
Boot0005* neon HD(1,GPT,34facff5-...)/File(\EFI\neon\shimx64.efi)
```
**What does it:** `cros_installer`'s post-install
(`platform2/installer/efi_boot_management.cc`, strings "Adding EFI Boot
entry." / "Creating new BootOrder."). It makes sure a "ChromeOS Flex"
entry for the eMMC's ESP exists and puts it first in `BootOrder`. Nothing
else in the rootfs writes `BootOrder` (only `cros_installer`,
`efibootmgr` and fwupd's EFI binary mention it), and no boot-time
upstart job runs `cros_installer`. It runs:
- at **install** time, but not under `install-flex-emmc.sh`: the chroot
binds `/sys` non-recursively, so `efivarfs` isn't visible and it logs
`EFI runtime services not [available]` and skips EFI management.
That's why the entry list printed straight after the install was
unchanged.
- **after every auto-update**, when `update_engine` runs post-install
on the running Flex system with real EFI access. That's what happened
here: the eMMC afterwards had ROOT-A `16640.57.0` (the install) and
ROOT-B `16640.61.0` (the update), with KERN-B the higher priority.
So resetting the order once won't last: expect it again after each
Flex update. No flag to turn it off was found. The binary does have an
"EFI is buggy continue without EFI management" fallback, but nothing
user-settable was found that triggers it.
**FydeOS has the same code**: its `cros_installer` includes the same
`efi_boot_management.cc` and `libefivar`, so FydeOS updates will very
likely do the same. The FydeOS `fydeos_dualboot` flag is *not* a fix:
it's for FydeOS's dual-boot mode, where it runs from an image file on
another OS's partition, and only gates its `update_initrd.sh`.
Ways to live with it (none implemented yet):
- Fix it by hand when it happens: pick neon from the firmware boot
picker (`Esc` at POST), then
`sudo efibootmgr -o 0005,0001,0000,0003,0002` (adjust to taste).
- A small neon systemd oneshot at boot that puts `neon` back first
whenever a ChromeOS entry is ahead of it. It still needs one manual
`Esc` boot into neon after each ChromeOS update, but then it's fixed
for good until the next update.
- Deleting `Boot0004` doesn't help: the next update recreates it.
Neon's GRUB entry chainloads the ESP directly and doesn't need it.
After the FydeOS install, `Boot0004 "ChromeOS Flex"` was stale: it
pointed at the old Flex ESP (PARTUUID `2877e60b…`, start sector
`0x49000`), which no longer exists, so the firmware skipped it and fell
through to neon. It was deleted 2026-09-27 (`efibootmgr -b 0004 -B`),
which put `BootOrder` back to `0005,0001,0000,0003,0002`. Expect FydeOS
to add its own entry, and move it first, after its first update.
## FydeOS
Checked 2026-09-27 by reading the image, then **installed the same day
with `install-flex-emmc.sh` unchanged. It worked on the first try.**
`FydeOS_for_PC_iris_v23.0-SP1-io.bin.zip` (board `amd64-fydeos_iris-io`,
version `16700.56.23.51`), in `matt`'s `~/Downloads` (extracted copy in
`~/fydeos-check`).
- **Same installer image layout:** ROOT-A is p3 (2.7GB), ROOT-B is a
2MB placeholder, the ESP is p12. The same `chromeos-install` (Rust)
and `chromeos-install.sh`, `write_gpt.sh`, `cros_installer`,
`is_running_from_installer`, and coreutils `/bin/chroot` shim, so the
chroot wrapper applies.
- `cros_installer` has the same `No recognized cros_XXX bios option`
check, so the fake `cros_efi` is still needed. The success line is the
same `Installation to '${DST}' complete.`
- The target layout from `write_gpt.sh` is 2.66GB each for ROOT-A and
ROOT-B, with STATE filling the rest of the disk.
- **FydeOS's `chromeos-postinst` adds steps:** it mounts the
destination's ESP (`${install_dev%[0-9]}12`) to sync a `cros_debug`
(dev-mode) flag in its `grub.cfg`, then runs
`/usr/share/dualboot/update_initrd.sh` if `rootdev` returns a loop
device, which it does in our chroot. That's harmless:
`update_initrd.sh` exits unless `fydeos_dualboot` is on
`/proc/cmdline`, which isn't in our faked cmdline, and other disks are
masked anyway.
- The ESP uses GRUB (`/efi/boot/bootx64.efi` + `grub.cfg`, kernels in
`/syslinux/vmlinuz.{A,B}`) instead of Flex's `crdyboot`. The
`40_custom` chainload path still works. It's unsigned, which is fine
because Secure Boot is disabled on hp-chell (`mokutil --sb-state`).
The image's ESP UUID is `F35E-F13C`.
Notes:
- It replaced Flex (same eMMC, full wipe).
- The script rewrites the UUID in the `40_custom` entry titled
"ChromeOS Flex", so FydeOS boots under that title. Decided
2026-09-27 to keep it, since FydeOS is effectively still ChromeOS.
- Hardware support: FydeOS's site says the `iris` build supports Intel
6th to 14th gen, which covers this 6th-gen (Skylake) Core m7-6Y75 /
HD 515 (checked 2026-09-27).
## History
- **2026-09-04**: first install by streaming the image with
`unzip -p … | dd of=/dev/mmcblk0 bs=4M conv=fsync` (~18 min; eMMC
writes dropped from ~130MB/s to ~9MB/s as its SLC cache filled, which
is normal for this class of eMMC). Booted as live/installer media, as
explained above. The note at the time that Flex expands STATE on
first boot was wrong.
- **2026-09-27**: reinstalled Flex properly with `install-flex-emmc.sh`.
It worked and auto-updated, but took over the UEFI boot order.
- **2026-09-27**: switched to FydeOS (`iris` build) with the same
script; deleted the stale Flex UEFI entry.
+45 -50
View File
@@ -16,7 +16,7 @@ This is a real **HP Chromebook 13 G1** ("chell" board), converted from ChromeOS
- System: Manufacturer `Google`, Product Name `Chell`, Family `Google_Glados`, Serial `5CD732BLC9` — `Chell`/`Glados` is the Google Chrome OS board/reference codename for the HP Chromebook 13 G1. - System: Manufacturer `Google`, Product Name `Chell`, Family `Google_Glados`, Serial `5CD732BLC9` — `Chell`/`Glados` is the Google Chrome OS board/reference codename for the HP Chromebook 13 G1.
- Baseboard: Manufacturer `Google`, Product Name `Chell`, Serial `PFYTU018J90043`. - Baseboard: Manufacturer `Google`, Product Name `Chell`, Serial `PFYTU018J90043`.
- Implication: fanless Core m CPU, no way to add a fan — keep sustained-load expectations modest. - Implication: fanless Core m CPU, no way to add a fan — keep sustained-load expectations modest.
- **Storage correction (2026-09-04)**: `sda` (117GB, where `/` lives) is actually an external USB/SD card (`lsblk` shows `TRAN=usb`) — the whole KDE neon install boots from that external card, not from anything internal. The real internal eMMC (`mmcblk0`, ~29GB) was blank until 2026-09-04, when ChromeOS Flex was installed onto it — see "Dual boot" section below. (Superseded the original note here, which incorrectly assumed `sda` was internal.) - **Storage correction (2026-09-04)**: `sda` (117GB, where `/` lives) is actually an external USB/SD card (`lsblk` shows `TRAN=usb`) — the whole KDE neon install boots from that external card, not from anything internal. The real internal eMMC (`mmcblk0`, ~29GB) was blank until 2026-09-04, when ChromeOS Flex was installed onto it — see "Dual boot" below. (Superseded the original note here, which incorrectly assumed `sda` was internal.)
## Kernel 7.x black-screen bug (diagnosed and fixed 2026-09-03) ## Kernel 7.x black-screen bug (diagnosed and fixed 2026-09-03)
@@ -117,57 +117,52 @@ flash itself, not a recurrence of the underlying i915 bug or a new
problem. If it happens again right after a firmware update, try a forced problem. If it happens again right after a firmware update, try a forced
power-off + power-on before assuming anything is actually broken. power-off + power-on before assuming anything is actually broken.
## Dual boot: KDE neon (USB) + ChromeOS Flex (internal eMMC) ## Dual boot: KDE neon (USB) + ChromeOS (internal eMMC)
2026-09-04: installed ChromeOS Flex onto the internal eMMC **FydeOS** (ChromiumOS-based, `iris` build; replaced ChromeOS Flex on
(`/dev/mmcblk0`), which was blank (see storage correction above — the 2026-09-27) is installed on the internal eMMC (`/dev/mmcblk0`); neon
KDE neon system boots from an external USB/SD card on `sda`, not from stays on the external `sda`. **Full details are in `CHROMEOS-FLEX.md`**,
anything internal, so writing to the eMMC was safe to do live from the covering install/reinstall via `install-flex-emmc.sh`, why a raw `dd` of
running system with zero risk to the neon install). the recovery image boots as live media, `chromeos-install`-from-Linux
gotchas, the partition layout, and boot selection.
**How it was installed**: the Flex recovery image Quick reference:
(`chromeos_16640.57.0_reven_recovery_stable-channel_RevenMPKeys-v10.bin.zip`, - Reinstall: `sudo ./install-flex-emmc.sh <Flex or FydeOS .bin.zip or .bin>` (wipes
in `matt`'s `~/Downloads`) is a full raw GPT disk image — exactly what the eMMC, masks every other disk, about 2.5 min, log in
the normal "boot from USB, then Install CloudReady/Chrome OS Flex" flow `/var/log/flex-install.log`).
`dd`s onto the target disk. Since neon's root already lives on external - Boot it from neon's GRUB menu with the "ChromeOS Flex (internal
media, that install flow was skipped entirely and the image was eMMC)" entry (in `/etc/grub.d/40_custom`; chainloads the ESP with FS
streamed straight onto the eMMC instead: UUID `F35E-F13C`; title kept from Flex), or from the firmware picker (`Esc` at POST, then
`Internal eMMC`). With the USB card unplugged, the firmware boots it
directly.
- ChromeOS updates re-add a UEFI entry and put it first in `BootOrder`;
see `CHROMEOS-FLEX.md`.
``` ## Audio (neon)
sudo sh -c 'unzip -p "<path to .zip>" <path to .bin inside the zip> \
| dd of=/dev/mmcblk0 bs=4M status=progress conv=fsync'
```
Unzipped image is ~8.9GB; eMMC is 29GB, plenty of room (ChromeOS auto- Speakers, headset jack and mic work under neon thanks to custom fixes
expands the stateful partition to fill the rest on first real boot). applied 2026-03-05, documented in **`AUDIO.md`**: WeirdTreeThing's
eMMC write speed degraded over the transfer (~130MB/s at the start down `chromebook-linux-audio` script (`avs` driver via
to ~9MB/s by the end, total ~18 min for 8.9GB) — looks like the eMMC's `/etc/modprobe.d/snd-avs.conf`, cros UCM profiles, WirePlumber
SLC write cache exhausting partway through; expected/normal for eMMC of headroom), avs topology blobs hand-downloaded from upstream
this class, not a fault. linux-firmware into `/lib/firmware/intel/avs/`, and a `Syntax 7` →
`Syntax 6` sed over the UCM files for neon's alsa-lib 1.2.11. FydeOS on
the eMMC has no audio yet (not investigated).
**Boot selection** — both OSes are reachable two ways: ## zram swap (neon)
1. **From GRUB** (neon's own boot menu, visible for 5s per
`GRUB_TIMEOUT`): a custom entry "ChromeOS Flex (internal eMMC)" was Disk swapfile replaced by an 8GiB zstd zram device via `zram-tools`
added to `/etc/grub.d/40_custom`, which chainloads the eMMC's own EFI (`/etc/default/zramswap`), set up 2026-03-05. Documented in **`ZRAM.md`**.
bootloader: `vm.swappiness=100` is persisted in `/etc/sysctl.d/99-zram.conf`, and the
``` old `/swapfile` was deleted (both 2026-09-27).
menuentry "ChromeOS Flex (internal eMMC)" {
insmod part_gpt ## Other neon customizations
insmod fat
insmod chain See **`NEON-CUSTOMIZATIONS.md`** (reconstructed 2026-09-27 from shell
search --no-floppy --fs-uuid --set=root CBE4-703E history): keyd Chromebook keyboard remap (built from source,
chainloader /EFI/BOOT/BOOTX64.EFI `/etc/keyd/default.conf`, internal keyboard only), USB-C DisplayPort
} via `/etc/modules-load.d/cros-video.conf` (`cros_ec_typec`,
``` `typec_displayport`), `linux-generic-hwe-24.04-edge` (why it's on 7.x
`CBE4-703E` is the FAT filesystem UUID of the eMMC's `EFI-SYSTEM` kernels) plus the 6.17 fallback, `/tmp` on tmpfs + localepurge to spare
partition (`mmcblk0p12`); `/EFI/BOOT/BOOTX64.EFI` is Chrome OS's the USB-card root, the removed r8152 DKMS driver, disabled tailscaled,
fallback UEFI loader (`crdyboot`) on that partition. Run and third-party apt repos and hand-installed tools.
`sudo update-grub` after editing `40_custom` for changes to take
effect.
2. **From the firmware's own UEFI boot picker** (MrChromebox UEFI,
typically `Esc` during POST) — `efibootmgr -v` shows both `neon`
(Boot0005, on the USB stick) and `Internal eMMC` (Boot0001) as
registered entries, `BootOrder` currently `0005,0001,0000,0003,0002`
(neon first). If the USB stick is unplugged, firmware falls through
past the missing `neon` entry straight to `Internal eMMC` and boots
Flex automatically — no menu needed.
+154
View File
@@ -0,0 +1,154 @@
# Other neon customizations on hp-chell
Reconstructed on 2026-09-27 from `~matt/.bash_history` (551 lines,
roughly early March 2026 onward), `/root/.bash_history`, and the live
system. Topics with their own docs:
[`AUDIO.md`](AUDIO.md), [`ZRAM.md`](ZRAM.md),
[`CHROMEOS-FLEX.md`](CHROMEOS-FLEX.md), and the kernel 7.x black-screen
fix in `CLAUDE.md`.
## Keyboard: keyd remapping for the Chromebook layout
The Chromebook keyboard has no F-keys, Delete, Home/End, PgUp/PgDn or
Caps Lock; there's a "Search" key where Caps Lock would be. **keyd**
remaps it at the evdev level, so it works everywhere (console, X,
Wayland, the login screen).
- Built from source, not from apt (apt has no keyd for noble):
`~/github/keyd` (<https://github.com/rvaiya/keyd>, `7c0aecb`,
**v2.6.0**), `make && sudo make install`, which puts the binary at
`/usr/local/bin/keyd`. Then `sudo systemctl enable --now keyd`. Neither
apt nor unattended upgrades will ever update it; rebuild from the
checkout to upgrade.
- Config: `/etc/keyd/default.conf`. Apply edits with `sudo keyd reload`;
find key names and device IDs with `sudo keyd monitor`. Summary:
- `[ids] 0001:0001`: **only the internal keyboard** (`AT Translated Set
2 keyboard`); external USB keyboards are left alone (use `*` to
include them).
- `leftmeta = overload(meta, leftmeta)`: Search acts as Meta when held
and taps as Meta (opens the KDE launcher).
- **Search + top row gives F1 to F10** (back, forward, refresh, zoom,
scale, brightness down/up, mute, volume down/up). **Search + `-`/`=`
gives F11/F12.**
- The same top-row to F-key mapping with Alt, Ctrl or Shift held. The
`A-`/`C-`/`S-` prefixes re-apply the modifier, which keyd otherwise
strips, so e.g. Alt+F4 works as Alt+Search+Zoom.
- Search + Backspace = Delete; Search + arrows = Home/End/PgUp/PgDn.
- Alt + Backspace = Delete; **Alt + Search = Caps Lock**.
## USB-C DisplayPort output: load the cros_ec Type-C drivers
`/etc/modules-load.d/cros-video.conf` (2026-03-12):
```
cros_ec_typec
typec_displayport
```
These don't autoload on chell, and without them the USB-C ports don't
negotiate DisplayPort alt mode, so there's no video out over USB-C.
Both are loaded as of 2026-09-27.
## Kernel: HWE-edge meta package
`linux-generic-hwe-24.04-edge` is installed (alongside the plain
`linux-generic-hwe-24.04`), and it's why the machine is on 7.x kernels
(`7.0.0-34` now), not noble's regular HWE line. Also installed
deliberately as a known-good fallback: `linux-image-generic-6.17` /
`linux-headers-generic-6.17` (`6.17.0-42`), which is the fallback kernel
named in the black-screen notes in `CLAUDE.md`. Old 7.x kernels
(`7.0.0-30`, `7.0.0-14`) are still installed as well.
`/etc/default/grub` (edited by hand several times; both `matt` and root
history show `nano /etc/default/grub` + `update-grub`): `GRUB_DEFAULT=0`,
`GRUB_TIMEOUT=5`, `GRUB_TIMEOUT_STYLE=menu`,
`GRUB_CMDLINE_LINUX_DEFAULT='splash'` (see `setup-i915-kernel7-fix.sh`).
## Storage / wear on the USB-card root
neon's root is an external USB/SD card, so a few changes cut down writes
and space use:
- **`/tmp` is tmpfs** (in `/etc/fstab`:
`tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0`). Not in the shell
history, so it was probably added in the same `nano /etc/fstab` edit
that disabled the swapfile.
- The swapfile was replaced by zram (see `ZRAM.md`).
- **`localepurge`** is installed and configured (via `dpkg-reconfigure
localepurge`) to keep only `en`, `en_CA.UTF-8`, `en_US.UTF-8` and
`fr_CA.UTF-8`, and delete other locales' files on every package
install (`USE_DPKG`, `MANDELETE`). If another language's translations
or man pages are ever needed, add them with
`sudo dpkg-reconfigure localepurge`.
## Networking
- **Realtek USB Ethernet (`enx00e04c68003e`, r8152-family 2.5G
adapter):** the out-of-tree driver from
<https://github.com/awesometic/realtek-r8152-dkms> (`~/github/realtek-r8152-dkms`)
was once installed with DKMS, together with unloading `cdc_ether` /
`cdc_ncm` / `cdc_mbim` and testing with `iperf3` against `pet630`,
MTU 9000 and `ethtool -K` offloads. It was **later removed**
(`sudo ./dkms-remove.sh`), around a kernel upgrade that it probably
broke. `dkms status` is empty now, so the adapter gets the in-kernel
driver. The MTU and offload tweaks were runtime-only and aren't
persisted anywhere.
- **Tailscale** is installed (repo in `tailscale.list`) but
`tailscaled` is **disabled** (`systemctl disable tailscaled`, around
the time the agent account was created).
## Firmware and access
- MrChromebox firmware utility run twice
(`curl -LOf https://mrchromebox.tech/firmware-util.sh && sudo bash
firmware-util.sh`); the second run was the 2026-09-03 update to
`MrChromebox-2606.1` (see `CLAUDE.md`).
- `agent-user` created with
`create-agent-user.sh` from
`https://gist.ergosteur.com/ergosteur/41df939a435741bfac431ac3d4b888d0`
(a copy is in `~matt`).
## Software and third-party apt repos
Not hardware-specific, but needed to rebuild the machine. The
repositories in `/etc/apt/sources.list.d/` beyond neon's own:
| Source file | What | Set up via |
|---|---|---|
| `vscodium.sources` | VSCodium (`codium`) | paulcarroty key into `/usr/share/keyrings/vscodium-archive-keyring.gpg` |
| `google-chrome.sources` | Google Chrome | installing the `.deb` from Downloads; set as `x-www-browser` / `gnome-www-browser` via `update-alternatives` |
| `docker.sources` | Docker CE | Docker's official apt instructions; `matt` added to the `docker` group |
| `ansible-ubuntu-ansible-noble.sources` | `ansible-core` | `ppa:ansible/ansible` |
| `nodesource.sources` | Node.js 24 | `deb.nodesource.com/setup_24.x`; then `npm i -g @google/gemini-cli` |
| `tailscale.list` | Tailscale | (disabled, see above) |
Other tools installed by hand:
- **`bws`** (Bitwarden Secrets Manager CLI) v2.0.0: unzipped into
`/opt/bitwarden/bws/`, symlinked to `/usr/local/bin/bws`, with
completions in `/opt/bitwarden/bws/completions.bash` sourced from
`~/.bashrc`.
- **GlobalProtect VPN** (`PanGPLinux-6.3.3-c31`, `gp_install.sh`, from
`~/Downloads`).
- `pipx`: `mkdocs` plus the injected `mkdocs-material`.
- Claude Code (`curl -fsSL https://claude.ai/install.sh | bash`).
Notable apt packages installed on top of neon: `codium ffmpeg yt-dlp
imagemagick python3-pip python3-venv pipx python3-bs4 build-essential
ncdu bwm-ng wireshark mpv filezilla rpi-imager ntfs-3g exfatprogs
exfat-fuse hfsutils hfsprogs samba samba-vfs-modules krdc remmina ssh
iperf3 netcat-openbsd htop btop git pavucontrol-qt bash-completion
tesseract-ocr{,-eng,-fra,-kor} inxi lsscsi qrencode localepurge
zram-tools dkms`.
Git identities: `~/.gitconfig` plus `~/.gitconfig.ergosteur` and
`~/.gitconfig.tfo`, with SSH key aliases in `~/.ssh`:
`git_ssh_id_ergosteur` → `id_rsa`, and `git_ssh_id_myip4tfo` →
`id_ed25519`.
## Dead ends in the history (nothing left installed)
- `~/github/chell_audio` (iofq's chell audio repo) was cloned and
`linux-modules-extra` / `snd_soc_skl` poked at, before switching to
WeirdTreeThing's scripts (see `AUDIO.md`).
- `ntfs2btrfs` / `ntfsfix` against a `/dev/sda1` of the time. That was
an external data card before this layout, not the current neon
install, whose `sda1` is its ESP.
+88
View File
@@ -0,0 +1,88 @@
# zram swap on hp-chell (KDE neon)
Set up **2026-03-05**, reconstructed on 2026-09-27 from `~/.bash_history`
and the live config. It replaces the disk swapfile with compressed swap
in RAM. That suits this machine: 15GiB RAM, and neon's root lives on a
slow external USB/SD card (`sda`) that you don't want to swap to or wear
out.
## Current state (2026-09-27)
```
$ zramctl
NAME ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 zstd 8G 4K 59B 20K [SWAP]
$ swapon --show
NAME TYPE SIZE USED PRIO
/dev/zram0 partition 8G 0B 100
```
The zram device is the only swap. zswap is off
(`/sys/module/zswap/parameters/enabled` = `N`), and there's nothing
zram-related on the kernel command line.
## What was done
1. **Installed `zram-tools`** (Debian's simple `zramswap` service; not
`systemd-zram-generator`, which isn't installed):
`sudo apt install zram-tools`, `sudo systemctl enable --now zramswap`.
2. **Configured `/etc/default/zramswap`**. The file is still the
package's own conffile (`dpkg -V zram-tools` flags it as modified),
with these lines added at the end:
```
ENABLED=true
ALGO=zstd
SIZE=8192
PRIORITY=100
```
- `ALGO=zstd`: better compression than the default `lz4`, at some
CPU cost. Fine on this 4-core Core m7 for swap-sized traffic.
- `SIZE=8192`: an 8GiB zram device, a fixed size in MiB. `PERCENT`
isn't set; if set, it would override `SIZE`. That's the uncompressed
capacity; actual RAM used is the compressed size (`COMPR`/`TOTAL`
in `zramctl`).
- `PRIORITY=100`: above any disk swap, so zram is used first.
Tuned over several stop/edit/start rounds (the history shows
`systemctl stop zramswap` / `nano` / `start` three or four times).
Only the final values are known. Apply changes with
`sudo systemctl restart zramswap`.
3. **Disabled the disk swapfile**: in `/etc/fstab`,
```
# removed swapfile, using zram instead
#/swapfile swap swap defaults 0 0
```
then `sudo swapoff /swapfile`.
4. **Raised `vm.swappiness` to 100**, at runtime only
(`sudo sysctl vm.swappiness=100`). See the loose ends below.
## Loose ends (fixed 2026-09-27)
- **`vm.swappiness=100` hadn't been made persistent**: it had been set
with `sysctl` at runtime only and was back to the default 60. Now
persisted in `/etc/sysctl.d/99-zram.conf` (`vm.swappiness=100`) and
applied live. Swapping to zram is cheap, so a high swappiness is the
usual choice. Not changed: `vm.page-cluster` (still `3`). Some zram
guides set it to `0`, since zram has no seek cost and readahead just
wastes work.
- **The unused `/swapfile`** (512MiB, dated 2026-03-04, fstab line
commented out) was deleted.
## Reapplying on a fresh neon install
```
sudo apt install zram-tools
sudo tee -a /etc/default/zramswap >/dev/null <<'EOF'
ENABLED=true
ALGO=zstd
SIZE=8192
PRIORITY=100
EOF
sudo systemctl enable --now zramswap
# comment out any /swapfile line in /etc/fstab, then:
sudo swapoff /swapfile
# persist swappiness
echo 'vm.swappiness=100' | sudo tee /etc/sysctl.d/99-zram.conf && sudo sysctl --system
```
Check with `zramctl` and `swapon --show`.
+180
View File
@@ -0,0 +1,180 @@
#!/bin/bash
# install-flex-emmc.sh
#
# Properly install ChromeOS Flex onto hp-chell's internal eMMC from the
# running KDE neon system (which boots from an external USB/SD card).
#
# Why not just dd the image: the Flex recovery .bin is an *installer*
# image -- ROOT-B is a 2MB placeholder and STATE is a fixed ~6GB with
# the rest of the disk unallocated. Flex's is_running_from_installer
# compares the ROOT-A/ROOT-B sizes, so a raw dd boots as "live USB"
# (Install / Try it first) and can't do A/B updates. This script instead
# runs the image's own chromeos-install (what the "Install ChromeOS Flex"
# button runs) against the eMMC, which lays out full-size A/B roots and a
# STATE partition filling the disk.
#
# How: loop-mount the image, chroot into its ROOT-A, and run
# chromeos-install --dst <eMMC> --payload_image <image> ...
# inside a private mount namespace where every block device except the
# destination (and loop devices) is masked with /dev/null -- so the neon
# disk is unreachable from inside the chroot.
#
# Afterwards, updates the "ChromeOS Flex" GRUB chainload entry in
# /etc/grub.d/40_custom with the new EFI-SYSTEM filesystem UUID.
#
# WIPES THE DESTINATION DEVICE ENTIRELY.
#
# Usage: sudo ./install-flex-emmc.sh <flex .bin.zip or .bin> [dst, default /dev/mmcblk0]
set -euo pipefail
SRC=${1:?usage: $0 <flex .bin.zip or .bin> [dst device]}
DST=${2:-/dev/mmcblk0}
WORK=${WORK:-/var/tmp/flex-install}
ROOTMNT=/mnt/flexchroot
LOG=/var/log/flex-install.log
if [ "$(id -u)" -ne 0 ]; then
echo "must run as root (sudo $0 ...)" >&2
exit 1
fi
[ -b "$DST" ] || { echo "$DST is not a block device" >&2; exit 1; }
dst_name=$(basename "$DST")
# Refuse if the destination holds the running system.
root_src=$(findmnt -no SOURCE /)
root_disk=$(lsblk -no PKNAME "$root_src" | head -1)
if [ "$root_disk" = "$dst_name" ]; then
echo "refusing: $DST holds the running root filesystem ($root_src)" >&2
exit 1
fi
echo "==> destination: $DST"
lsblk -o NAME,SIZE,TYPE,TRAN,PARTLABEL,MOUNTPOINTS "$DST"
echo "==> running system root is on /dev/$root_disk (will be masked)"
read -r -p "ERASE $DST and install ChromeOS Flex? type 'yes': " ans
[ "$ans" = yes ] || { echo aborted; exit 1; }
# Keep udisks (the Plasma desktop) from auto-mounting the new partitions
# mid-install -- chromeos-install's sfdisk step refuses a busy disk.
UDEV_RULE=/run/udev/rules.d/99-flex-install-ignore.rules
mkdir -p /run/udev/rules.d
LOOP=
cleanup() {
if [ -n "$LOOP" ]; then losetup -d "$LOOP" 2>/dev/null || true; fi
rm -f "$UDEV_RULE"
udevadm control --reload
}
trap cleanup EXIT
echo "KERNEL==\"${dst_name}*\", ENV{UDISKS_IGNORE}=\"1\"" > "$UDEV_RULE"
udevadm control --reload
udevadm trigger --settle "$DST"
# Unmount anything mounted from the destination.
for p in $(lsblk -lno PATH "$DST" | tail -n +2); do
for m in $(findmnt -rno TARGET "$p" || true); do umount "$m"; done
done
# Get a raw .bin.
case "$SRC" in
*.zip)
mkdir -p "$WORK"
bin_name=$(unzip -Z1 "$SRC" | grep '\.bin$' | head -1)
IMG="$WORK/$bin_name"
if [ ! -s "$IMG" ]; then
echo "==> extracting $bin_name to $WORK"
unzip -o -q "$SRC" "$bin_name" -d "$WORK"
fi
;;
*) IMG=$SRC ;;
esac
IMG=$(realpath "$IMG")
echo "==> attaching image read-only"
LOOP=$(losetup -r -P -f --show "$IMG")
mkdir -p "$ROOTMNT"
efibootmgr > /var/log/flex-install-efiboot-before.txt 2>&1 || true
echo "==> running chromeos-install (log: $LOG); this takes a while on eMMC"
# Everything below runs in a private mount namespace; all mounts vanish
# when it exits.
unshare -m --propagation private bash -euo pipefail -c '
LOOP=$1; R=$2; IMG=$3; DSTNAME=$4
mount -o ro,noload "${LOOP}p3" "$R"
mount -t tmpfs tmpfs "$R/tmp"
mount -t tmpfs tmpfs "$R/run"
mount -t tmpfs tmpfs "$R/var"
# postinst touches .install_completed on the *installer'"'"'s* stateful
# partition; give it somewhere writable (discarded afterwards).
mount -t tmpfs tmpfs "$R/mnt/stateful_partition"
mount --bind /dev "$R/dev"
mount -t devpts devpts "$R/dev/pts" 2>/dev/null || true
mount -t proc proc "$R/proc"
mount --bind /sys "$R/sys"
# chromeos_postinst picks the bootloader setup from a cros_XXX flag on
# the kernel cmdline ("No recognized cros_XXX bios option" otherwise);
# neon obviously has none, so fake cros_efi.
echo "$(cat /proc/cmdline) cros_efi" > "$R/tmp/cmdline"
mount --bind "$R/tmp/cmdline" "$R/proc/cmdline"
# chromeos-postinst re-chroots into the freshly written root with a plain
# (non-recursive) bind of /proc, which drops that overmount. Wrap the
# image'"'"'s chroot (a coreutils shim) to re-apply it in the target root.
cat > "$R/tmp/chroot-wrap" <<"WRAP"
#!/bin/sh
for a in "$@"; do case "$a" in -*) ;; *) d=$a; break ;; esac; done
if [ -n "$d" ] && [ -f "$d/proc/cmdline" ] && ! grep -q cros_efi "$d/proc/cmdline"; then
mount --bind /tmp/cmdline "$d/proc/cmdline"
fi
exec /usr/bin/coreutils --coreutils-prog=chroot "$@"
WRAP
chmod 755 "$R/tmp/chroot-wrap"
mount --bind "$R/tmp/chroot-wrap" "$R/bin/chroot"
# Mask every disk (and its partitions) except the destination.
for n in "$R"/dev/sd* "$R"/dev/nvme* "$R"/dev/mmcblk* "$R"/dev/vd*; do
[ -b "$n" ] || continue
case "$(basename "$n")" in
"$DSTNAME"|"$DSTNAME"p[0-9]*) ;;
*) mount --bind /dev/null "$n" ;;
esac
done
mount -t tmpfs tmpfs "$R/dev/disk"
mount -t tmpfs tmpfs "$R/dev/block"
touch "$R/tmp/payload.bin"
mount --bind "$IMG" "$R/tmp/payload.bin"
chroot "$R" env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin HOME=/tmp TERM=dumb \
chromeos-install --dst "/dev/$DSTNAME" --payload_image /tmp/payload.bin \
--skip_dst_removable --yes --target_bios efi --debug
' _ "$LOOP" "$ROOTMNT" "$IMG" "$dst_name" > "$LOG" 2>&1 || true
# chromeos-install can exit 0 even when its inner chromeos-install.sh
# failed, so check the log rather than trusting the exit status. (It also
# logs a few harmless "ERROR cros_installer" lines -- metrics etc. -- even
# on success, so key off its final result lines instead.)
if grep -q '^Error: ' "$LOG" || ! grep -q "^Installation to '/dev/$dst_name' complete" "$LOG"; then
echo "chromeos-install FAILED; see $LOG. Tail:" >&2
tail -30 "$LOG" >&2
exit 1
fi
echo "==> chromeos-install succeeded"
partprobe "$DST" 2>/dev/null || true
udevadm settle
echo "==> new layout:"
lsblk -o NAME,SIZE,FSTYPE,PARTLABEL "$DST"
# Point the GRUB chainload entry at the new EFI-SYSTEM partition.
esp=$(lsblk -lno PATH,PARTLABEL "$DST" | awk '$2=="EFI-SYSTEM"{print $1}')
esp_uuid=$(blkid -s UUID -o value "$esp")
custom=/etc/grub.d/40_custom
if grep -q 'ChromeOS Flex' "$custom" 2>/dev/null; then
echo "==> updating $custom to EFI-SYSTEM UUID $esp_uuid"
sed -i "/ChromeOS Flex/,/^}/ s/--fs-uuid --set=root [0-9A-Fa-f-]*/--fs-uuid --set=root $esp_uuid/" "$custom"
update-grub
else
echo "==> no ChromeOS Flex entry in $custom; EFI-SYSTEM UUID is $esp_uuid"
fi
echo "==> UEFI boot entries (compare with /var/log/flex-install-efiboot-before.txt):"
efibootmgr
echo "==> done. First Flex boot should go straight to setup (no Install/Try screen)."