Compare commits
18
Commits
9c3b8e48ee
..
main
@@ -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.
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
# 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`, microSD card in the built-in slot (117GB; the reader is USB-attached internally) | primary OS, owns GRUB |
|
||||||
|
| FydeOS (was ChromeOS Flex) | `mmcblk0`, internal eMMC (29GB) | installed from neon, see below |
|
||||||
|
|
||||||
|
Because neon lives entirely on the microSD card, 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 microSD card) and
|
||||||
|
`Internal eMMC` (Boot0001), with `BootOrder` `0005,0001,0000,0003,0002`.
|
||||||
|
With the microSD card removed, 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`. The extracted copies
|
||||||
|
(`~/fydeos-check`, `~/flex-install`) were deleted 2026-09-28; the
|
||||||
|
script takes the `.bin.zip` directly and extracts to `/var/tmp/flex-install`.
|
||||||
|
|
||||||
|
- **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.
|
||||||
@@ -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 a microSD card in the laptop's **built-in microSD slot**. The slot's reader hangs off the internal USB bus, which is why `lsblk` shows `TRAN=usb`, but it is *not* an external USB device. The whole KDE neon install boots from that microSD card, not from the eMMC. (Wording corrected 2026-09-27; earlier notes called it an "external USB/SD card".) 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 the internal eMMC.)
|
||||||
|
|
||||||
## Kernel 7.x black-screen bug (diagnosed and fixed 2026-09-03)
|
## Kernel 7.x black-screen bug (diagnosed and fixed 2026-09-03)
|
||||||
|
|
||||||
@@ -63,7 +63,9 @@ scaling) and it's only the greeter — the actual logged-in Plasma/Wayland
|
|||||||
session correctly renews the modeset itself and comes up at full native
|
session correctly renews the modeset itself and comes up at full native
|
||||||
`3200x1800` with 125% scaling, confirmed working. No kernel cmdline
|
`3200x1800` with 125% scaling, confirmed working. No kernel cmdline
|
||||||
workarounds are needed — `/etc/default/grub`'s `GRUB_CMDLINE_LINUX_DEFAULT`
|
workarounds are needed — `/etc/default/grub`'s `GRUB_CMDLINE_LINUX_DEFAULT`
|
||||||
is back to a clean `'splash'`.
|
has no i915 params (as of 2026-09-28 it's `'splash resume=UUID=…
|
||||||
|
resume_offset=…'`; the resume params are for hibernation, see
|
||||||
|
`NEON-CUSTOMIZATIONS.md`).
|
||||||
|
|
||||||
Confirmed reliable across multiple clean `sudo systemctl poweroff` /
|
Confirmed reliable across multiple clean `sudo systemctl poweroff` /
|
||||||
power-on cycles. The GRUB default entry (index 0, `GRUB_DEFAULT=0`) auto-
|
power-on cycles. The GRUB default entry (index 0, `GRUB_DEFAULT=0`) auto-
|
||||||
@@ -117,57 +119,58 @@ 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 (microSD) + 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 microSD card (`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 microSD card removed, 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 microSD 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. **Suspend must stay
|
||||||
`sudo update-grub` after editing `40_custom` for changes to take
|
`s2idle`**: S3 (`deep`) made the microSD reader drop off on resume and
|
||||||
effect.
|
remounted `/` read-only (2026-09-27). **Suspend-then-hibernate** (after
|
||||||
2. **From the firmware's own UEFI boot picker** (MrChromebox UEFI,
|
3h asleep) is set up instead (2026-09-28): 16GB `/swapfile` at pri 10,
|
||||||
typically `Esc` during POST) — `efibootmgr -v` shows both `neon`
|
`resume=`/`resume_offset=17674240` in GRUB + initramfs, a polkit rule
|
||||||
(Boot0005, on the USB stick) and `Internal eMMC` (Boot0001) as
|
re-enabling hibernate (Ubuntu blocks it); details in
|
||||||
registered entries, `BootOrder` currently `0005,0001,0000,0003,0002`
|
`NEON-CUSTOMIZATIONS.md`.
|
||||||
(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.
|
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
# 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 resume=UUID=3af45daa-b8c2-4053-9574-a958e0c3ea68
|
||||||
|
resume_offset=17674240'` (the `resume` params were added 2026-09-28 for
|
||||||
|
hibernation, see below; see also `setup-i915-kernel7-fix.sh`).
|
||||||
|
|
||||||
|
## Storage / wear on the microSD root
|
||||||
|
|
||||||
|
neon's root is a microSD card in the built-in slot (`sda`; the slot's
|
||||||
|
reader is USB-attached internally), and microSD handles lots of small
|
||||||
|
writes poorly, 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`.
|
||||||
|
- **Root mounted with `commit=30`** (2026-09-27): the root line in
|
||||||
|
`/etc/fstab` is `defaults,commit=30`; the pre-change copy is
|
||||||
|
`/etc/fstab.bak-2026-09-27`. ext4 commits its journal every 30s
|
||||||
|
instead of every 5s, so writes are batched into fewer, larger bursts.
|
||||||
|
Trade-off: a crash or forced power-off can lose up to ~30s of recent
|
||||||
|
changes that weren't fsync'd (apt/dpkg, editors and databases fsync, so
|
||||||
|
they're unaffected). 30 rather than 60 because this machine has needed
|
||||||
|
forced power-offs. `relatime` is kept deliberately (not `noatime`).
|
||||||
|
- **journald capped at 100MB** (2026-09-27):
|
||||||
|
`/etc/systemd/journald.conf.d/50-max-size.conf` sets
|
||||||
|
`SystemMaxUse=100M` (it was 455MB).
|
||||||
|
- **`smartmontools` disabled** (2026-09-27, `systemctl disable --now`):
|
||||||
|
it failed on every boot because neither the microSD card nor the eMMC
|
||||||
|
supports SMART.
|
||||||
|
- **Marked non-rotational** (2026-09-27): the Genesys reader reported
|
||||||
|
`rotational=1` (a spinning disk). `/etc/udev/rules.d/61-microsd-nonrotational.rules`
|
||||||
|
sets `queue/rotational=0` for `ID_VENDOR_ID=05e3`, `ID_MODEL_ID=0747`,
|
||||||
|
so `lsblk -d -o NAME,ROTA` shows `0`.
|
||||||
|
- The card doesn't support discard/TRIM (`lsblk -D` shows 0), so
|
||||||
|
`fstrim.timer` does nothing for it. That's harmless and was left alone.
|
||||||
|
|
||||||
|
## Suspend: stay on `s2idle`; do NOT use S3 (`deep`)
|
||||||
|
|
||||||
|
Tested 2026-09-27. The firmware offers both `s2idle` (default) and `deep`
|
||||||
|
(ACPI S3) in `/sys/power/mem_sleep`.
|
||||||
|
- An RTC-woken 30s S3 test (`rtcwake -m mem -s 30` with `mem_sleep=deep`)
|
||||||
|
entered and left S3 cleanly, and the display came back. The i915
|
||||||
|
bug didn't show.
|
||||||
|
- **A real lid-close S3 suspend broke the root filesystem**: on resume
|
||||||
|
the built-in microSD reader (USB, Genesys `05e3:0747`) didn't come
|
||||||
|
back in time, the kernel logged disk I/O errors, and ext4 remounted `/`
|
||||||
|
read-only (`errors=remount-ro`). Nothing was logged because root was
|
||||||
|
read-only (the previous boot's journal just stops). After a forced
|
||||||
|
reboot, the initramfs fsck replayed the journal and reported the
|
||||||
|
filesystem clean; no lasting damage was found.
|
||||||
|
- In S3 the USB controller loses power, so the reader has to be
|
||||||
|
re-attached. USB "persist" (`power/persist=1`, the kernel feature
|
||||||
|
meant for root on USB storage) was already on and didn't save it.
|
||||||
|
There's no dependable fix, so **never set `mem_sleep_default=deep`**
|
||||||
|
while neon's root is on the microSD slot.
|
||||||
|
- `s2idle` keeps USB powered and has worked for months. **It does
|
||||||
|
reach S0ix**: in a 44s lid-close test (2026-09-28),
|
||||||
|
`/sys/kernel/debug/pmc_core/slp_s0_residency_usec` (needs root)
|
||||||
|
grew by ~42.2s, and `package_cstate_show` showed Package C10 ~42.0s.
|
||||||
|
So ~96% of the suspend was in the deepest idle state, and drain should
|
||||||
|
be close to S3's. Root stayed `rw` with no I/O errors.
|
||||||
|
- Use the `pmc_core` counter above, not
|
||||||
|
`/sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us`.
|
||||||
|
That one comes from ACPI LPIT, which isn't wired up properly under
|
||||||
|
coreboot here: it read 270µs for the same suspend.
|
||||||
|
|
||||||
|
## Suspend-then-hibernate (set up 2026-09-28)
|
||||||
|
|
||||||
|
A lid close suspends (`s2idle`, instant). If the machine is still asleep
|
||||||
|
after **3 hours**, systemd wakes it (RTC), writes a hibernation image to
|
||||||
|
disk and powers off. This protects against a dead battery without
|
||||||
|
writing a multi-GB image on every lid close, which is why this was
|
||||||
|
chosen over hybrid sleep. It doesn't need S3.
|
||||||
|
|
||||||
|
Pieces:
|
||||||
|
- **16GB `/swapfile`**, used for the hibernation image (sized to RAM for
|
||||||
|
the worst case; a typical image is ~4GB).
|
||||||
|
fstab `/swapfile none swap sw,pri=10 0 0`: priority 10 < zram's 100,
|
||||||
|
so normal swapping still goes to zram. Backup of the old fstab:
|
||||||
|
`/etc/fstab.bak-2026-09-28`.
|
||||||
|
- **Resume location**: `resume=UUID=3af45daa-b8c2-4053-9574-a958e0c3ea68
|
||||||
|
resume_offset=17674240` in `GRUB_CMDLINE_LINUX_DEFAULT` (backup
|
||||||
|
`/etc/default/grub.bak-2026-09-28`) and in
|
||||||
|
`/etc/initramfs-tools/conf.d/resume`, then `update-initramfs -u -k all`
|
||||||
|
+ `update-grub`. **If `/swapfile` is ever recreated, its offset
|
||||||
|
changes.** Get the new one with
|
||||||
|
`sudo filefrag -v /swapfile | awk '$1=="0:"{print $4}'` (strip the
|
||||||
|
trailing `..`), then update both places and rerun both commands.
|
||||||
|
- **`/etc/systemd/sleep.conf.d/50-suspend-then-hibernate.conf`**:
|
||||||
|
`HibernateDelaySec=3h`.
|
||||||
|
- **`/etc/polkit-1/rules.d/10-enable-hibernate.rules`**: Ubuntu's
|
||||||
|
`/usr/share/polkit-1/rules.d/com.ubuntu.desktop.rules` returns `NO` for
|
||||||
|
all `login1`/`upower` hibernate actions, so logind reported
|
||||||
|
`CanHibernate`/`CanSuspendThenHibernate` = `no` even though its sleep
|
||||||
|
checks passed (seen with
|
||||||
|
`systemctl service-log-level systemd-logind debug`). This rule sorts
|
||||||
|
earlier and returns `YES` for local, active users.
|
||||||
|
Ubuntu actually has *two* blockers: that JS rule and
|
||||||
|
`/var/lib/polkit-1/localauthority/10-vendor.d/com.ubuntu.desktop.pkla`
|
||||||
|
("Disable hibernate by default in logind/upower"), which is still read
|
||||||
|
because `polkitd-pkla` is installed. The `10-` rule overrides both:
|
||||||
|
`busctl call org.freedesktop.login1 /org/freedesktop/login1
|
||||||
|
org.freedesktop.login1.Manager CanSuspendThenHibernate` returns `yes`
|
||||||
|
as `matt`.
|
||||||
|
- **Plasma**: PowerDevil owns the lid, so each Power Management profile's
|
||||||
|
sleep mode is set to "Standby, then hibernate". That's `SleepMode=3`
|
||||||
|
under `[AC|Battery|LowBattery][SuspendAndShutdown]` in
|
||||||
|
`~/.config/powerdevilrc` (set 2026-09-28 for all three profiles).
|
||||||
|
**Gotcha:** PowerDevil caches hibernate support when it starts, so
|
||||||
|
after the polkit fix the option didn't appear in System Settings until
|
||||||
|
`systemctl --user restart plasma-powerdevil.service` and System
|
||||||
|
Settings was reopened. Check PowerDevil's view with
|
||||||
|
`qdbus6 org.freedesktop.PowerManagement /org/freedesktop/PowerManagement
|
||||||
|
org.freedesktop.PowerManagement.CanSuspendThenHibernate`.
|
||||||
|
|
||||||
|
Tested 2026-09-28 with `systemctl hibernate`: a 3.8GB image, powered off,
|
||||||
|
and the session resumed intact (uptime carried over, root `rw`). Right
|
||||||
|
after resume, one readahead READ failed on `sda` ("device offline or
|
||||||
|
changed") while the microSD reader was being reset. That's harmless:
|
||||||
|
readahead failures are dropped, and ext4 logged no errors and stayed
|
||||||
|
`clean`. It's the same reader re-attach that breaks S3, but a hibernate
|
||||||
|
resume is a cold boot and the reader was back in time.
|
||||||
|
|
||||||
|
## 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`) because it failed to build against the 7.x
|
||||||
|
kernels (confirmed by matt, 2026-09-27). `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.
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# 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 microSD card in the built-in slot (`sda`) that you don't want to swap to or wear
|
||||||
|
out.
|
||||||
|
|
||||||
|
## Current state (2026-09-28)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ swapon --show
|
||||||
|
NAME TYPE SIZE USED PRIO
|
||||||
|
/dev/zram0 partition 8G 582.5M 100
|
||||||
|
/swapfile file 16G 0B 10
|
||||||
|
```
|
||||||
|
|
||||||
|
zram (priority 100) takes all everyday swapping. The 16GB `/swapfile`
|
||||||
|
(priority 10) is there for the hibernation image (see
|
||||||
|
"Suspend-then-hibernate" in `NEON-CUSTOMIZATIONS.md`). It only gets
|
||||||
|
ordinary swap pages if zram fills up. 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. On 2026-09-28 a new **16GB `/swapfile`**
|
||||||
|
was added at priority 10 (below zram's 100) for hibernation. See
|
||||||
|
"Suspend-then-hibernate" in `NEON-CUSTOMIZATIONS.md`. zram is still
|
||||||
|
the everyday swap.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
# drop any old swapfile from /etc/fstab (a hibernation swapfile is set
|
||||||
|
# up separately, see NEON-CUSTOMIZATIONS.md)
|
||||||
|
# persist swappiness
|
||||||
|
echo 'vm.swappiness=100' | sudo tee /etc/sysctl.d/99-zram.conf && sudo sysctl --system
|
||||||
|
```
|
||||||
|
|
||||||
|
Check with `zramctl` and `swapon --show`.
|
||||||
Executable
+181
@@ -0,0 +1,181 @@
|
|||||||
|
#!/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 the microSD card in the built-in
|
||||||
|
# slot; its reader is USB-attached internally, so it shows as sda).
|
||||||
|
#
|
||||||
|
# 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)."
|
||||||
Reference in New Issue
Block a user