Some checks failed
build image / build (push) Failing after 3h12m41s
The account the image creates was fedora/fedora; make it grenade/grenade and say in both docs that config/device.env decides it. Nothing else referenced the name — sudo comes from wheel membership, not a per-user sudoers file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011XgGF5wfxLDAybVnNz6eNQ
195 lines
9.0 KiB
Markdown
195 lines
9.0 KiB
Markdown
# Fedora for the Lenovo Yoga C630 13Q50
|
|
|
|
Builds a ready-to-write Fedora aarch64 disk image for the Lenovo Yoga C630
|
|
(model 81JL, Qualcomm SDM850), using Gitea Actions.
|
|
|
|
The approach is borrowed from [aarch64-laptops/build][aal], but the heavy
|
|
lifting that project had to do in 2019 is now unnecessary: mainline Linux has
|
|
carried `sdm850-lenovo-yoga-c630.dts` since 5.5, and Fedora ships it in
|
|
`kernel-core`. There is no kernel to patch and no GRUB to compile. What is left
|
|
is assembling a disk image that boots on hardware whose firmware hands Linux no
|
|
device tree.
|
|
|
|
[aal]: https://github.com/aarch64-laptops/build
|
|
|
|
## What you get
|
|
|
|
`output/fedora-44-<variant>-lenovo-yoga-c630-<date>-<ref>.img.zst` — a GPT disk
|
|
image with an ESP, a `/boot` partition and an ext4 root. Decompress, write it to
|
|
a USB stick or microSD card, and boot. The root filesystem grows to fill the
|
|
medium on first boot.
|
|
|
|
Two variants:
|
|
|
|
| Variant | Size | Contents |
|
|
|---------------|-------|----------------------------------------------|
|
|
| `minimal` | 8 GiB | Console, sshd, and enough tools to debug the machine |
|
|
| `workstation` | 16 GiB| GNOME desktop |
|
|
|
|
Default login is `grenade` / `grenade`, and the password must be changed at
|
|
first login. Root is locked. Both are set in `config/device.env`
|
|
(`DEFAULT_USER`, `DEFAULT_PASSWORD`) if you want a different account.
|
|
|
|
## Hardware status
|
|
|
|
| Works | Works once you supply firmware | Does not work |
|
|
|---|---|---|
|
|
| UFS storage, USB, keyboard, touchpad, touchscreen | Adreno 630 initialises — see mesa note below | Onboard WiFi — loaded by the modem, which does not stay up |
|
|
| Display (unaccelerated), battery, charging | Sensor hub — accelerometer, auto-rotate | LTE modem — crashes at RF init; see docs/firmware.md for what has been ruled out |
|
|
| USB WiFi dongles, USB phone tethering | Hardware video decode (venus, `/dev/video0-3`) | |
|
|
| Bluetooth — the controller has no burned-in address; `c630-bt-addr.service` reads the factory one off the DPP partition at boot | Audio — needs `qcadsp850.mbn` plus one kernel module Fedora does not build; see docs/firmware.md | |
|
|
|
|
Verified on hardware. `docs/firmware.md` has the evidence for each, including
|
|
the things that look like they should help and do not.
|
|
|
|
**Graphics comes with a caveat.** With `qcdxkmsuc850.mbn` in place the GPU
|
|
genuinely initialises — `gpu-initialized: 1`, revision 630, executing
|
|
submissions. But mesa's freedreno EGL path then segfaults in
|
|
`dri2_drm_swap_buffers`, taking gnome-shell down in a login loop. Until that is
|
|
fixed upstream, the desktop needs software rendering:
|
|
|
|
```sh
|
|
echo 'MESA_LOADER_DRIVER_OVERRIDE=kms_swrast' | sudo tee /etc/environment
|
|
```
|
|
|
|
Note `LIBGL_ALWAYS_SOFTWARE=1` does *not* work here — mutter uses EGL, which
|
|
ignores it.
|
|
|
|
Getting the firmware: see
|
|
[docs/firmware.md](docs/firmware.md), which compares the two projects that
|
|
publish it — WOA-Project and aarch64-laptops — and what each one covers.
|
|
`sudo c630-firmware` works if the Windows partition survives.
|
|
|
|
## Building
|
|
|
|
CI does this on every push to `main`. To run it yourself:
|
|
|
|
```sh
|
|
./build/build-image.sh --variant minimal
|
|
```
|
|
|
|
You need `podman` and, on an x86_64 host, aarch64 emulation:
|
|
|
|
```sh
|
|
sudo dnf install -y qemu-user-static-aarch64
|
|
sudo systemctl restart systemd-binfmt
|
|
```
|
|
|
|
`build/build-image.sh` runs on the host and only sets up the container.
|
|
`build/stage2.sh` runs inside an aarch64 Fedora container and does everything
|
|
else: `dnf --installroot`, the overlay, dracut, and the disk assembly. It builds
|
|
filesystems from directory trees with `mke2fs -d` and `mcopy` rather than
|
|
mounting loop devices, so it does not need `/dev/loop-control` — which CI
|
|
runners generally will not hand out.
|
|
|
|
### Iterating
|
|
|
|
Every aarch64 binary runs under emulation, and the `dnf` transaction is
|
|
essentially all of the cost — 500-odd packages' worth of rpm scriptlets for
|
|
`minimal`, four times that for `workstation`. Everything after it takes
|
|
minutes. Since getting this machine to boot will take a few attempts, the build
|
|
is arranged so you only pay that once:
|
|
|
|
- The post-`dnf` root filesystem is staged under `<work>/base`, keyed on a hash
|
|
of the package lists, release and variant. Editing `config/device.env`,
|
|
`overlay/`, or the bootloader config reuses it. Editing `config/packages/`
|
|
invalidates it automatically.
|
|
- The working copy is made with `cp --reflink=auto`, so on btrfs or xfs it is
|
|
a copy-on-write clone rather than a real copy.
|
|
- Downloaded rpms live in `.cache/dnf`, outside the staged tree, so even
|
|
`--fresh` re-runs the scriptlets without re-downloading.
|
|
|
|
In practice a kernel-command-line change rebuilds in a few minutes.
|
|
|
|
```sh
|
|
./build/build-image.sh --variant minimal # reuses the staged base
|
|
./build/build-image.sh --variant minimal --fresh # forces a reinstall
|
|
./build/build-image.sh --variant minimal --keep-rootfs # keep the tree to poke at
|
|
```
|
|
|
|
CI points `--work` and `--cache` at `/var/tmp/c630-build` so both survive
|
|
between jobs. That is per-runner, so the first build on a given runner is cold.
|
|
|
|
The build container defaults to `git.lair.cafe/gongfoo/build-fedora-44-aarch64`,
|
|
which ships the assembly tooling so it does not have to be installed under
|
|
emulation on every run. If that image is not reachable the build falls back to
|
|
stock Fedora and installs the tooling itself — slower, but it works.
|
|
|
|
## Repository layout
|
|
|
|
```
|
|
config/device.env C630 parameters: DTB path, kernel command line, geometry
|
|
config/packages/*.pkgs Package lists — base plus one file per variant
|
|
overlay/ Files copied into the root filesystem (*.in are templated)
|
|
build/build-image.sh Host driver: emulation checks, podman invocation
|
|
build/stage2.sh The actual build, inside an aarch64 container
|
|
firmware/local/ Optional drop-in for firmware you extracted yourself (gitignored)
|
|
.gitea/workflows/ CI
|
|
docs/ Installation, firmware, runner setup
|
|
```
|
|
|
|
## Documentation
|
|
|
|
- [docs/install.md](docs/install.md) — writing the image and booting the laptop
|
|
- [docs/firmware.md](docs/firmware.md) — the per-model blobs: what they enable, where to get them, and what they do not fix
|
|
- [docs/runner-setup.md](docs/runner-setup.md) — one-time Gitea runner preparation
|
|
|
|
## Boot status
|
|
|
|
Confirmed on hardware, from a USB stick:
|
|
|
|
- GRUB loads the kernel and the device tree
|
|
- The kernel comes up and probes the SDM850 — UFS controller, display
|
|
subsystem, WiFi and IPA all appear as platform devices, so
|
|
`DEVICE_CMDLINE` and the DTB are right
|
|
- The framebuffer console works (`simple-framebuffer`, 240x67)
|
|
- The root filesystem mounts, systemd starts, and it reaches a login prompt
|
|
- Networking works over a USB WiFi dongle, and sshd is reachable
|
|
- `build/install-to-disk.sh` copies it onto the internal UFS, and it boots from
|
|
there with the stick removed — 18s to multi-user, no failed units
|
|
|
|
The RTC cannot be set on this machine and reads 1970 at every boot, so `chrony`
|
|
is installed and corrects the clock once the network is up. Until it does, rpm
|
|
rejects package signatures dated after the (wrong) current time and reports
|
|
installed packages as missing — see [docs/install.md](docs/install.md).
|
|
|
|
With the per-model firmware supplied, the GPU initialises, the ADSP, CDSP and
|
|
SLPI all run, sensors appear as IIO devices and venus registers `/dev/video0-3`.
|
|
Audio additionally needs one kernel module Fedora does not build
|
|
(`CONFIG_SND_SOC_WSA881X`); with it compiled out of tree, the sound card
|
|
registers and the speakers play. Onboard WiFi remains broken: the modem that
|
|
hosts the WLAN firmware dies at RF init, and firmware versions, userspace
|
|
services and kernel 6.12-vs-7.1 have all been eliminated as the cause — see
|
|
[docs/firmware.md](docs/firmware.md), which records the evidence and the many
|
|
plausible fixes that turn out not to work.
|
|
|
|
The internal drive needs `build/install-to-disk.sh` rather than `dd`: its UFS
|
|
uses 4096-byte logical sectors, which the 512-byte image geometry cannot be
|
|
written onto directly. See [docs/install.md](docs/install.md).
|
|
|
|
SELinux is shipped permissive — see below.
|
|
|
|
### SELinux
|
|
|
|
The build labels the filesystem offline with `setfiles`, so enforcing should
|
|
work. It ships **permissive** anyway, because the failure mode is unusually
|
|
punishing: an unlabelled root takes PID 1 down with `Failed to allocate manager
|
|
object` before anything can be logged into and repaired, and `/.autorelabel`
|
|
cannot save it because nothing survives long enough to act on the flag.
|
|
|
|
Once the machine is up and you are happy with it:
|
|
|
|
```sh
|
|
sudo setenforce 1 # try it for this boot
|
|
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
|
|
```
|
|
|
|
Or set `SELINUX_MODE=enforcing` in `config/device.env` and rebuild.
|
|
|
|
## Caveats
|
|
|
|
The kernel command line and firmware layout are taken from Fedora's Snapdragon
|
|
WoA documentation and the aarch64-laptops project; the parts specific to the
|
|
C630's older SDM850 are reasoned from those rather than derived from
|
|
documentation for this machine. Findings belong in this README.
|