Repository navigation
os: fans and power profile inside the image, on the boards we know - #550
Conversation
|
Preparing review... |
|
Review (orchestrator): DMI-gated, driver-only, no byte written on an unknown board, GPL/LGPL sources shipped with the image, fans row with the dead-fan FAIL case; host checks against a fake EC are the right call while the lane is busy. Decisions: ship the SMU tool pinned (version + source commit recorded in os/, not 'if the builder has it') so the 90 W cap is deterministic; default power file on the known board = the dev box profile. Converted to draft until the module build + VMTEST (QEMU reaches the SKIP path, EC_MODULE=skip image still boots) run at 'lane shared'; the hardware boot + release check is the owner's step after 'leaderboard done'. |
PR Reviewer Guide 🔍(Review updated until commit f49e7fe)Here are some key observations to aid the review process:
|
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
|
Persistent review updated to latest commit 96bb144 |
|
Preparing review... |
The appliance had no fan or power control at all: on this board class the firmware's own fan curve is conservative, and without a driver for the board's embedded controller the image shows no fan readings and cannot set the EC power mode or the SMU limits. This brings all of it to the boards we know and writes nothing at all anywhere else. - os/mini/board.sh (installed as /usr/sbin/1bit-board): the EC fan curve, a predictive boost loop (0.5 s poll on package power, GPU busy, Tctl and its rise; level 4 on load, back to the curve after 20 s quiet, level 5 only above 82 C) and the power profile (EC power mode first, then the SMU limits through ryzenadj when the image carries it, then the fabric/SoC clock pins), with overrides read from /etc/1bit/power. Driver only: no debug-register access, ever. - os/mini/mkimage.sh: builds ec_su_axb35 for the image kernel from the source this repository ships (os/licenses/ec_su_axb35, GPL-2.0, verbatim upstream commit f62c2c22), installs the module plus its source and licence into the image, installs board.sh and the /etc/1bit/power template, and ships ryzenadj (LGPL-3.0, licence included) when the build machine has it. - os/mini/init.sh: on a board we know, load the EC driver, apply the curve and the power profile and start the boost loop; report fans, power profile and the two temperatures in the boot banner and in the 1bit.check self-test. On any other board, one line saying the firmware curve stays. - os/mini/release-check.sh: a fans row - PASS with the fan rpms at level 4, SKIP when this board has no EC we drive, FAIL when the driver loaded but a fan still reads 0 rpm at level 4 for 5 s (the fault mode we have seen on real hardware). - os/LICENSES.md: the board driver (GPL-2.0, source shipped) and the SMU tool (LGPL-3.0). Image boot on hardware pending: the module build and the VM boot test run once the build lane opens (EC_MODULE=skip builds an image without the driver). No behaviour change on machines without this board. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
…ile when the cap cannot be written Three things the review asked for, plus the two Secure Boot traps that would have bitten the image: - **ryzenadj is pinned, not conditional.** `os/licenses/ryzenadj` carries LGPL-3.0, a SOURCE naming tag v0.19.0 (commit ea71739b) with its build recipe, and that tag's source verbatim. `mkimage.sh` now builds it from that source (cmake; needs cmake, a C compiler and libpci-dev) instead of copying whatever the build machine happens to have, installs `/opt/1bit/ryzenadj`, and puts the source in the image next to the binary. The 90 W cap is deterministic. - **The board driver is signed with the module-signing MOK.** An out-of-tree module has to be signed by a key the kernel trusts; it must be the MOK, because the "Secure Boot Kernel" cert carries no module-signing OID (verified: amdxdna.ko and ec_su_axb35.ko on the development box are both signed by "strixhalo Secure Boot MOK"). `mkimage.sh` signs with MOK.priv/MOK.der through the kernel's sign-file, overridable with MODULE_SIGN_KEY/MODULE_SIGN_CERT, and warns loudly when it cannot. - **The SMU cap has a defined fallback.** If the limits cannot be written - lockdown refuses /dev/mem, a kernel without it, or no tool - `1bit-board power` falls back to the EC's own `balanced` profile (85 W sustained, 120 W burst) instead of leaving the board in `performance` with no cap, and the boot banner says which cap is in force (`power: … cap=90/90/105W …` or `… fallback=balanced …`). `FALLBACK_MODE` is in `/etc/1bit/power`. - `/etc/1bit/power` now documents that its defaults are the development box's profile (performance, 90/90/105 W, level-5 trip at 82 C unchanged) and marks the fan trip points as the first thing to revisit on a hotter unit. - `tools/copyright.py` knows `*.example` (a '#'-comment file; the template ships as /etc/1bit/power). Image boot on hardware and the VM test still pending (build lane). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
…t through the MOK
The first real image build found three things the PR could not have known without building:
- the image kernel is clang-built (`CONFIG_CC_IS_CLANG`), so `gcc` rejects its flags
(`-mretpoline-external-thunk`); the module now builds with `LLVM=1` and the matching
`/usr/lib/llvm-<major>/bin` on PATH, because `LLVM=1` also wants `ld.lld`, `llvm-ar` and friends -
a bare `clang` is not enough (`ld.lld: not found`);
- the MOK private key is 0600 root, so an unprivileged build cannot read it: the module is signed
through `sudo -n` when needed, and always from the scratch copy, never from the file already inside
the image tree (a root-written module there broke the later chmod/cpio);
- `$out/logs` is created before the build logs are opened.
Also documents that MODULE_SIGN_KEY/MODULE_SIGN_CERT are deliberately not MOK_KEY/MOK_CERT: a module
needs the MOK that carries the module-signing OID, the EFI image needs the kernel-signing key.
Built and verified: image 2.3 GB (EFI 97 MB, initramfs 86 MB), `ec_su_axb35.ko` 33,846 bytes signed by
`MOK.der` ("strixhalo Secure Boot MOK"), ryzenadj v0.19.0 built from the pinned source (68,184 bytes,
6 libs), `1bit-board` and `/etc/1bit/power` in the initramfs, both licence trees with their source.
`vmtest-release.sh` in QEMU: RESULT PASS (16 checks, no failures) for the image with the driver and for
the `EC_MODULE=skip` image, with `SKIP fans : this board has no EC we drive` in both.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
caf49ea to
f49e7fe
Compare
|
Preparing review... |
|
Persistent review updated to latest commit f49e7fe |
|
Review (orchestrator): VMTEST PASS (16 checks) for both the driver image and the EC_MODULE=skip image, fans row SKIP in the VM as designed, module signed with the MOK that carries the module-signing attribute, SMU tool pinned and built from vendored source with the EC balanced-mode fallback when lockdown or the tool is missing. Merging; the hardware boot + 1bit-release-check (fans row PASS with live rpm, [power] cap=90/90/105W in the banner) is the owner's step after the leaderboard. |
|
Persistent review updated to latest commit f49e7fe |
The appliance had no fan or power control at all (
grep -rn 'fan|power_mode' os/was empty), so on aboard with an embedded controller it ran on the firmware's fan curve, which is conservative, and reported no
fan readings or temperatures of its own. This brings the development box's setup into the image for the
boards it knows, and writes nothing anywhere else.
os/mini/board.sh(installed as/usr/sbin/1bit-board, BusyBox sh, no Python, no systemd):mode=curveplus the ramp-up/ramp-down trip points, which the EC applies on its own sensorwith hysteresis);
3 °C rise per poll → the CPU fans at level 4; back to the curve after 20 s quiet; level 5 only above 82 °C,
held to 76 °C;
ryzenadj, then the fabric/SoC clock pins — every value overridable from
/etc/1bit/power,SKIP_POWER=1toleave power alone;
Board match is DMI based (
BeyondMax Series/AXB35-02). On any other board every command exits withoutwriting a byte,
statussaysfans: not controlled (this board is not one we know; the firmware curve stays),and the boot banner says the same.
Deterministic by construction.
os/mini/mkimage.shbuildsec_su_axb35for the image kernel from thesource this repository ships (
os/licenses/ec_su_axb35, GPL-2.0, verbatim upstreamf62c2c22, built in a tempcopy so the tree stays clean) and builds ryzenadj from the pinned source in
os/licenses/ryzenadj(LGPL-3.0, tag v0.19.0, commit
ea71739b) rather than depending on what the build machine has. Both sourcetrees and licences are installed into the image under
/usr/share/licenses/1bit-os/, so every binary travelswith its corresponding source. A failed build fails the image build loudly;
EC_MODULE=skipships without thedriver.
Two Secure Boot traps, both handled:
MOK: the "Secure Boot Kernel" certificate carries no module-signing OID (verified on the development box —
amdxdna.koandec_su_axb35.koare both signed by strixhalo Secure Boot MOK, and onlyMOK.derhas1.3.6.1.4.1.2312.16.1.2).mkimage.shnow signs the module withMOK.priv/MOK.derthrough the kernel'sscripts/sign-file(MODULE_SIGN_KEY/MODULE_SIGN_CERToverride) and warns loudly if it cannot.ryzenadjwrites the SMU mailbox through/dev/mem, which lockdown(integrity) refuses. On the development box that never bites because the kernel does not enter lockdown from
Secure Boot:
/sys/kernel/security/lockdownreads[none]andCONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOTis notset in the shipped kernel config — the same kernel the image carries. If a machine ever does refuse the write
(lockdown forced, no
/dev/mem, or no tool),1bit-board powernow falls back to the EC's ownbalancedprofile (85 W sustained, 120 W burst) instead of leaving the board in
performancewith no cap, and boththe banner and
power-statussay which cap is in force (cap=90/90/105W …vsfallback=balanced …).os/mini/init.sh: on a known board, load the EC driver, apply the curve and the power profile, start theboost loop, and report
[fans] …,[power] …andtemps: Tctl …C EC …Cin the banner and in the1bit.checkself-test. Unknown board → one line, nothing written.
os/mini/release-check.sh: afansrow —The FAIL case is deliberate: it is exactly how a fan failed on our own hardware.
/etc/1bit/powerships as a commented template whose defaults are the development box's profile(performance mode, 90/90/105 W, level-5 trip at 82 °C unchanged — the appliance is the same board and pad class),
with the fan trip points marked as the first thing to revisit on a hotter unit.
os/LICENSES.md: the board driver (GPL-2.0) and the SMU tool (LGPL-3.0, pinned) with corresponding-sourceentries.
Verified on the development box (same board, same kernel):
known,curve(identical trip points),statuswith live rpms,
boostraising the fans on load and releasing to the curve on TERM,powerexercised end toend against a fake EC — including the fallback path (
ryzenadjforced to fail → EC mode becomesbalanced,banner says so) — so the real profile was never touched; the
/etc/1bit/powerparser (comments, spaces,SKIP_POWER=1,FALLBACK_MODE);tools/copyright.py --checkpasses;sh -nclean on all four scripts.Draft on purpose — image boot on hardware pending: the module build, the ryzenadj build and the VM boot test
run once the build lane opens (
EC_MODULE=skipbuilds an image without the driver). No behaviour change onmachines without this board.
Verified by building the image and booting it in QEMU (24 Oct, build lane)
os/mini/mkimage.shon this board, under a build hold (gpu-hold -l "build pi osfans" -t 30,nice -n 19 taskset -c 0-3), against~/wt/release-rc/work/build:ec_su_axb35.ko33,846 bytes, in/lib/modules/<kver>/updates/, signed byMOK.der—modinfo -F signersaysstrixhalo Secure Boot MOKryzenadjv0.19.0 built fromos/licenses/ryzenadj/src(68,184 bytes, 6 libs linked) →/opt/1bit/ryzenadjusr/sbin/1bit-board,etc/1bit/power, the signed.ko,opt/1bit/ryzenadj, and both licence trees with their full source (…/1bit-os/ec_su_axb35/src31 files,…/1bit-os/ryzenadj/src47 files)1bit-os.img2.3 GB,1bit-os.efi97 MB,initramfs.img86 MBos/mini/vmtest-release.sh(QEMU, KVM, OVMF, the real release check over SSH):i.e. the SKIP path is exercised exactly as intended (no EC in a VM), the
EC_MODULE=skipbuild still bootsand passes, and nothing else regressed (
chat,bench,sealed,licencesall PASS;chat-hrx,chat-npu,identitySKIP for the documented VM reasons).Three things the build lane taught this PR, all fixed here: the image kernel is clang-built, so the module
must build with
LLVM=1and the matching/usr/lib/llvm-<major>/binonPATH(gccchokes on-mretpoline-external-thunk, andLLVM=1alone dies onld.lld: not found); the MOK private key is0600 root, so signing goes through
sudo -nwhen needed and always from a scratch copy (a root-ownedmodule inside the image tree broke the later
chmod/cpio);$out/logshas to exist before the build logsopen.
Still pending on hardware: a boot on the real board with
1bit-release-check(thefansrow should PASSwith live rpms there) — that is the owner's step, after the leaderboard is done.