Audio: Fedora has never built CONFIG_SND_SOC_WSA881X, the speaker-amp driver the in-tree DTS requires; with it built out of tree the card registers and plays. The din-ports message is a harmless warning. Also record the S24_LE format defect and the sink-stealing traps (AirPlay, Bluetooth, phantom headphone jack) that masqueraded as broken audio. WiFi: every published modem firmware build (five distinct across nine WOA versions) and kernel 6.12 all fail identically at QLINK, the link to the SDR845 RF transceiver. This unit is an LTE SKU with working modem history under Windows, fsg/fsc are blank, and the modem asks rmtfs for unknown fsg_oem partitions. Fedora's tqftpserv also serves different paths than the old aarch64-laptops one — the earlier instructions here were wrong for it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011XgGF5wfxLDAybVnNz6eNQ
194 lines
8.9 KiB
Markdown
194 lines
8.9 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 `fedora` / `fedora`, and the password must be changed at first
|
|
login. Root is locked.
|
|
|
|
## 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.
|