Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Confirm replacement of `power-profiles-daemon` if Pacman prompts for it. The Oma
## 3. Generate the ACPI overrides

The [ACPI builder and battery-method transformations](https://github.com/pfeifferj/archlinuxarm-proart-p16/tree/3484cc008a9ee00b34c086c729e28e935cbc3430/01-platform/acpi/payload/provenance/hardware)
come from Josephine Pfeiffer's repository. Their source is retained unchanged;
come from Josephine Pfeiffer's repository. Their source is adapted here;
this repository adds the Omarchy build/install wrappers and kernel-specific
initramfs integration. See [the retained MIT license](acpi/LICENSE).

Expand All @@ -133,17 +133,25 @@ sudo ./scripts/dump-acpi.sh
python3 scripts/build-acpi.py
```

The build uses the included ACPICA **20260408** source and checks the original tables and both generated overrides against pinned SHA-256 hashes. It rejects other firmware tables and tables that have already been patched. The dump script creates the parent `build/` directory with ownership assigned to the user who will compile the sources. It refuses to overwrite an existing `build/firmware/` directory.
The build uses the included ACPICA **20260408** source and checks the original tables and the generated ASUS SSDT override against pinned SHA-256 hashes. It rejects other firmware tables and tables that have already been patched. The dump script creates the parent `build/` directory with ownership assigned to the user who will compile the sources. It refuses to overwrite an existing `build/firmware/` directory.

If you generated ACPI files with an older version, retain `build/firmware/` and move the old output aside before rebuilding:

```bash
mv -T --no-clobber build/acpi build/acpi-before-native-dsdt
python3 scripts/build-acpi.py
```

If the backup directory already exists, choose another unused name. The builder refuses to replace different existing output bytes.

Outputs:

```text
build/acpi/kernel/firmware/acpi/DSDT.aml
build/acpi/kernel/firmware/acpi/SSDT.aml
build/acpi/manifest.json
```

The DSDT corrects SSPM enumeration; the SSDT corrects EC and battery methods. The kernel includes the ARM64 allocation fix required for these overrides. Load the tables through the included initramfs hook for the P16 kernel. This does not flash the BIOS.
The SSDT corrects EC and battery methods. The existing `acpi_osi="!Windows 2022"` boot option enables SSPM. Keep the native DSDT: its firmware-data address can move between boots on the same BIOS. A copied DSDT can then hide CPU devices and prevent CPU frequency controls from loading. The initramfs hook includes only the ASUS SSDT. This does not flash the BIOS.

## 4. Build the P16 kernel

Expand Down Expand Up @@ -459,7 +467,7 @@ They identify the inputs used here, independently of later upstream changes.

| Component | Source and treatment in this repository |
|---|---|
| ACPI builder and battery transformations | [Pfeiffer's ACPI sources](https://github.com/pfeifferj/archlinuxarm-proart-p16/tree/3484cc008a9ee00b34c086c729e28e935cbc3430/01-platform/acpi/payload/provenance/hardware): `acpi_builder.py`, `power-method-changes.json`, `power-source.sed` and `power-splice.json` are byte-identical copies under `acpi/`. Our scripts wrap them for local firmware dumps, builds and Omarchy installation. |
| ACPI builder and battery transformations | [Pfeiffer's ACPI sources](https://github.com/pfeifferj/archlinuxarm-proart-p16/tree/3484cc008a9ee00b34c086c729e28e935cbc3430/01-platform/acpi/payload/provenance/hardware): `power-method-changes.json`, `power-source.sed` and `power-splice.json` are byte-identical copies under `acpi/`. We adapted `acpi_builder.py` to retain the native DSDT. Our scripts wrap it for local firmware dumps, builds and Omarchy installation. |
| ARM64 ACPI allocation fix | [Source patch](https://github.com/pfeifferj/archlinuxarm-proart-p16/blob/3484cc008a9ee00b34c086c729e28e935cbc3430/01-platform/kernel/patches/provenance/hardware/backports/acpi/0001-arm64-bound-acpi-override-allocation.patch): copied unchanged as [kernel/arm64-acpi-allocation.patch](kernel/arm64-acpi-allocation.patch), integrated into the Omarchy kernel recipe. |
| Touchpad pressure workaround | [Source quirk](https://github.com/pfeifferj/archlinuxarm-proart-p16/blob/3484cc008a9ee00b34c086c729e28e935cbc3430/03-system/input-audio/payload/runtime/usr/share/libinput/60-h7607a-touchpad.quirks): same device IDs and pressure settings; local section name/DMI pattern and installation into `/etc/libinput/local-overrides.quirks` instead of a packaged `/usr/share/libinput/` file. |
| Speaker firmware | [Source lock](https://github.com/pfeifferj/archlinuxarm-proart-p16/blob/3484cc008a9ee00b34c086c729e28e935cbc3430/07-build/media/payload/locks/hardware.json): extraction metadata for the three `spkid0` files is retained in [config/speaker-firmware.json](config/speaker-firmware.json). Firmware is downloaded from ASUS and extracted locally; no firmware binaries are redistributed. |
Expand Down
103 changes: 37 additions & 66 deletions acpi/acpi_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Build the H7607BA.300 ACPI overrides from this P16's own firmware tables.

No firmware table, nor anything derived from one, is distributed. Dump the
DSDT and SSDTs of your own P16 into a directory and pass it as --dump:
ASUS SSDT of your own P16 into a directory and pass it as --dump:

UEFI: scripts/stage-acpi-dump.py stages the EDK2 Shell and a dump script on
a mounted FAT32 stick. Boot the P16 from it with Secure Boot disabled; the
Expand All @@ -11,17 +11,19 @@
Linux booted without h7607a-acpi.cpio, such as another distribution's live
system on this P16:
mkdir DIR
for table in /sys/firmware/acpi/tables/DSDT /sys/firmware/acpi/tables/SSDT*; do
for table in /sys/firmware/acpi/tables/SSDT*; do
sudo cat "$table" | tee "DIR/${table##*/}" > /dev/null
done

The DSDT and the ASUS SSDT (OEM ID _ASUS_, table ID ASUSSSDT) must be those of
firmware H7607BA.300. power-source.dsl is that SSDT disassembled by ACPICA iasl
20260408 and edited by power-source.sed. SSPM changes only its _STA predicate,
and six compiled ASUS methods are spliced into the original SSDT, since
compiling the whole table would rewrite unrelated AML. The two tables, the ASL
and the archive are written to their collection paths once every pinned hash
matches.
The ASUS SSDT (OEM ID _ASUS_, table ID ASUSSSDT) must be from firmware
H7607BA.300. power-source.dsl is that table disassembled by ACPICA iasl
20260408 and edited by power-source.sed. Six compiled ASUS methods are
spliced into the original SSDT. The SSDT, ASL and archive are written only
after every pinned hash matches.

The native DSDT stays in use. Its firmware-data addresses can change between
boots even on the same BIOS. The required acpi_osi="!Windows 2022" boot option
already enables SSPM without replacing that table.
"""
import argparse
import hashlib
Expand All @@ -37,15 +39,12 @@

REPO = Path(__file__).resolve().parents[2]
FIRMWARE = "H7607BA.300"
DSDT_INPUT = "0b9ed92671568cf36bade16262d068d1a4f3c85634159de46ae00bf0f727a402"
SSDT_INPUT = "46d9ed16af06093cdb849df69646683317d53d7342dfd38f24eab00795d396f0"
ASL_INPUT = "cff6e8be5665686c95bd83b5e2d73ed0abbacddecc416e57dbd29f91a746472a"
DSDT_OUTPUT = "80e0ad4a051704003d09029cb600a9814e1cd370ce774b9749dc09d2232b5aaf"
SSDT_OUTPUT = "9b28ffc14c19f8b7ea2278de88ff9ec070f5f70401107d81fef067a916aa9695"
ARCHIVE_OUTPUT = "311dc5b456f1b7a9728814cd8ca105370bdca8383facec141f23217f43eac51d"
ARCHIVE_OUTPUT = "68420c5248a237b01267bf8713867f53358252d0d3935fb09c0e66b02c39153e"
METHODS = ("RRA9", "RRA8", "WRA9", "WRA8", "RWEC", "GBST")
ASUS_SSDT = b"_ASUS_ASUSSSDT"
DSDT_PATH = "assets/hardware/uefi/n1x-dsdt-baseline.bin"
SSDT_PATH = "assets/hardware/uefi/n1x-asus-ssdt-baseline.bin"
ASL_PATH = "provenance/hardware/power-source.dsl"
ARCHIVE_PATH = "assets/hardware/acpi/h7607a-acpi.cpio"
Expand Down Expand Up @@ -97,21 +96,6 @@ def encode_length(length, width):
[(length >> (4 + i * 8)) & 255 for i in range(width - 1)])


def patch_dsdt(original):
exact(original, DSDT_INPUT, "Firmware DSDT")
table(original, b"DSDT", b"NVDA\0\0ANGUS ", 4)
start = 0x10269
predicate = b"_OSI\x0dWindows 2022\0"
require(original[start:start + len(predicate)] == predicate, "SSPM predicate changed")
modified = bytearray(original)
modified[start:start + len(predicate)] = b"\0" + b"\xa3" * (len(predicate) - 1)
struct.pack_into("<I", modified, 24, 5)
modified[9] = 0
modified[9] = -sum(modified) % 256
table(modified, b"DSDT", b"NVDA\0\0ANGUS ", 5)
return exact(bytes(modified), DSDT_OUTPUT, "SSPM override")


def compiled_method(compiled, name, digest):
matches = []
for offset in range(36, len(compiled) - 6):
Expand Down Expand Up @@ -173,23 +157,21 @@ def member(name, data, mode, inode):
return entry + bytes(-len(entry) % 4)


def archive(dsdt, ssdt):
exact(dsdt, DSDT_OUTPUT, "SSPM override")
def archive(ssdt):
exact(ssdt, SSDT_OUTPUT, "ASUS power override")
result = b"".join(member(name, b"", stat.S_IFDIR | 0o755, inode)
for inode, name in enumerate(("kernel", "kernel/firmware", "kernel/firmware/acpi"), 1))
result += member("kernel/firmware/acpi/DSDT.aml", dsdt, stat.S_IFREG | 0o644, 4)
result += member("kernel/firmware/acpi/SSDT.aml", ssdt, stat.S_IFREG | 0o644, 5)
result += member("TRAILER!!!", b"", 0, 6)
result += member("kernel/firmware/acpi/SSDT.aml", ssdt, stat.S_IFREG | 0o644, 4)
result += member("TRAILER!!!", b"", 0, 5)
result += bytes(-len(result) % 512)
return exact(result, ARCHIVE_OUTPUT, "Production ACPI archive")


def dumped_tables(dump):
"""Map b"DSDT" and b"SSDT" to {table: [file names]} for the DSDT and ASUS SSDT dumps in dump."""
"""Map ASUS SSDT bytes to their dump file names."""
dump = Path(dump)
require(dump.is_dir(), f"{dump} is not a directory")
found = {b"DSDT": {}, b"SSDT": {}}
found = {b"SSDT": {}}
for path in sorted(dump.iterdir()):
if not path.is_file():
continue
Expand Down Expand Up @@ -309,47 +291,36 @@ def pins(files):
return {name: {"bytes": len(data), "sha256": sha(data)} for name, data in files.items()}


def build(dump, *, repo=REPO, iasl="iasl", sspm_only=False):
def build(dump, *, repo=REPO, iasl="iasl"):
repo = Path(repo).resolve()
found = dumped_tables(dump)
dsdt_name, dsdt = dumped(found, b"DSDT", "DSDT", dump)
firmware(dsdt_name, dsdt, DSDT_INPUT, DSDT_OUTPUT, "DSDT")
overrides = {"DSDT.aml": patch_dsdt(dsdt)}
files = {DSDT_PATH: dsdt}
names = {"DSDT": dsdt_name}
compiler = None
if not sspm_only:
ssdt_name, ssdt = dumped(found, b"SSDT", "ASUS SSDT (OEM ID _ASUS_, table ID ASUSSSDT)", dump)
firmware(ssdt_name, ssdt, SSDT_INPUT, SSDT_OUTPUT, "ASUS SSDT")
names["SSDT"] = ssdt_name
source = repo / "provenance/hardware"
methods = json.loads((source / "power-method-changes.json").read_text())
require(tuple(row["method"] for row in methods) == METHODS, "Unexpected ASL method changes")
with tempfile.TemporaryDirectory(prefix="h7607a-acpi-") as work:
work = Path(work)
iasl = check_iasl(iasl, work)
asl = derive_asl(ssdt, (source / "power-source.sed").read_text(), iasl, work)
(work / "power.dsl").write_bytes(asl)
run_iasl(iasl, ["-p", "power", "power.dsl"], work)
compiled = (work / "power.aml").read_bytes()
compiler = "ACPICA iasl 20260408"
overrides["SSDT.aml"] = splice_ssdt(ssdt, compiled, json.loads((source / "power-splice.json").read_text()))
files.update({SSDT_PATH: ssdt, ASL_PATH: asl,
ARCHIVE_PATH: archive(overrides["DSDT.aml"], overrides["SSDT.aml"])})
ssdt_name, ssdt = dumped(found, b"SSDT", "ASUS SSDT (OEM ID _ASUS_, table ID ASUSSSDT)", dump)
firmware(ssdt_name, ssdt, SSDT_INPUT, SSDT_OUTPUT, "ASUS SSDT")
source = repo / "provenance/hardware"
methods = json.loads((source / "power-method-changes.json").read_text())
require(tuple(row["method"] for row in methods) == METHODS, "Unexpected ASL method changes")
with tempfile.TemporaryDirectory(prefix="h7607a-acpi-") as work:
work = Path(work)
iasl = check_iasl(iasl, work)
asl = derive_asl(ssdt, (source / "power-source.sed").read_text(), iasl, work)
(work / "power.dsl").write_bytes(asl)
run_iasl(iasl, ["-p", "power", "power.dsl"], work)
compiled = (work / "power.aml").read_bytes()
overrides = {"SSDT.aml": splice_ssdt(ssdt, compiled, json.loads((source / "power-splice.json").read_text()))}
files = {SSDT_PATH: ssdt, ASL_PATH: asl, ARCHIVE_PATH: archive(overrides["SSDT.aml"])}
written = publish(repo, files)
return {"schema": 2, "firmware": FIRMWARE, "compiler": compiler, "dump": names,
return {"schema": 2, "firmware": FIRMWARE, "compiler": "ACPICA iasl 20260408", "dump": {"SSDT": ssdt_name},
"overrides": pins(overrides), "outputs": pins(files), "written": written}


def cli(*, sspm_only=False):
def cli():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--dump", required=True, type=Path, metavar="DIR",
help="Directory holding this P16's DSDT and SSDT dumps")
if not sspm_only:
parser.add_argument("--iasl", default="iasl", help="ACPICA iasl 20260408 executable")
help="Directory holding this P16's original ASUS SSDT dump")
parser.add_argument("--iasl", default="iasl", help="ACPICA iasl 20260408 executable")
args = parser.parse_args()
try:
result = build(args.dump, iasl=getattr(args, "iasl", "iasl"), sspm_only=sspm_only)
result = build(args.dump, iasl=args.iasl)
except (ValueError, OSError, subprocess.SubprocessError) as error:
parser.exit(1, f"ACPI build failed: {error}\n")
print(json.dumps(result, indent=2))
3 changes: 1 addition & 2 deletions config/p16-acpi
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ build() {
error 'P16 ACPI override requires the dedicated kernel allocation fix'
return 1
}
add_file_early /etc/initcpio/p16-acpi/DSDT.aml /kernel/firmware/acpi/DSDT.aml
add_file_early /etc/initcpio/p16-acpi/SSDT.aml /kernel/firmware/acpi/SSDT.aml
}
help() {
echo 'Hash-validated H7607BA.300 SSPM and EC/battery fixes, for the dedicated P16 kernel only.'
echo 'Hash-validated H7607BA.300 ASUS EC/battery fix, for the dedicated P16 kernel only.'
}
2 changes: 1 addition & 1 deletion packages/build-local.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

PLATFORM = [
'/etc/initcpio/install/p16-acpi',
'/etc/initcpio/p16-acpi/DSDT.aml',
'/etc/initcpio/p16-acpi/SSDT.aml',
'/etc/mkinitcpio.conf.d/zz-p16-hardware-fixes.conf',
'/etc/mkinitcpio.conf.d/zz-p16-input.conf',
Expand Down Expand Up @@ -57,6 +56,7 @@
'p16-mayflower': MAYFLOWER,
}
SOURCES = {
'/etc/initcpio/install/p16-acpi': REPO / 'config/p16-acpi',
'/usr/local/sbin/p16-mkinitcpio-managed': REPO / 'scripts/p16-mkinitcpio-managed',
'/usr/local/sbin/p16-guard-preflight': REPO / 'scripts/p16-guard-preflight',
'/usr/share/p16-platform/90-mkinitcpio-install.hook': REPO / 'config/90-mkinitcpio-install.hook',
Expand Down
5 changes: 2 additions & 3 deletions scripts/build-acpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
report = module.build(build / 'firmware', repo=out, iasl=str(compiler / 'generate/unix/bin/iasl'))
(out / 'manifest.json').write_text(json.dumps(report, indent=2) + '\n')
subprocess.run(['bsdtar', '-xf', str(out / module.ARCHIVE_PATH), '-C', str(out)], check=True)
for name, expected in [('DSDT.aml', module.DSDT_OUTPUT), ('SSDT.aml', module.SSDT_OUTPUT)]:
if not hashlib.sha256((out / 'kernel/firmware/acpi' / name).read_bytes()).hexdigest() == expected:
raise ValueError('Validierung fehlgeschlagen')
if not hashlib.sha256((out / 'kernel/firmware/acpi/SSDT.aml').read_bytes()).hexdigest() == module.SSDT_OUTPUT:
raise ValueError('Validierung fehlgeschlagen')
print('ACPI-Dateien:', out / 'kernel/firmware/acpi')
6 changes: 2 additions & 4 deletions scripts/install-kernel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ python3 - "$REPO" <<'PY'
from pathlib import Path
import hashlib, sys
root = Path(sys.argv[1]) / 'build/acpi/kernel/firmware/acpi'
for name, pin in {'DSDT.aml':'80e0ad4a051704003d09029cb600a9814e1cd370ce774b9749dc09d2232b5aaf', 'SSDT.aml':'9b28ffc14c19f8b7ea2278de88ff9ec070f5f70401107d81fef067a916aa9695'}.items():
for name, pin in {'SSDT.aml':'9b28ffc14c19f8b7ea2278de88ff9ec070f5f70401107d81fef067a916aa9695'}.items():
if hashlib.sha256((root/name).read_bytes()).hexdigest() != pin:
raise ValueError('ACPI-Prüfsumme stimmt nicht: ' + name)
PY
Expand Down Expand Up @@ -83,9 +83,7 @@ done
for conf in 00-omarchy-n1x-usb4.conf 00-omarchy-n1x-gpu.conf 00-omarchy-n1x-sleep.conf; do
install -Dm644 "$REPO/config/$conf" "/etc/limine-entry-tool.d/$conf"
done
for table in DSDT.aml SSDT.aml; do
install -Dm644 "$REPO/build/acpi/kernel/firmware/acpi/$table" "/etc/initcpio/p16-acpi/$table"
done
install -Dm644 "$REPO/build/acpi/kernel/firmware/acpi/SSDT.aml" /etc/initcpio/p16-acpi/SSDT.aml
install -Dm755 "$REPO/scripts/p16-charge-limit" /usr/local/libexec/p16-charge-limit
install -Dm755 "$REPO/scripts/p16-temperatures" /usr/local/bin/p16-temperatures
for unit in p16-asus-controls.service p16-charge-limit.service; do
Expand Down
1 change: 1 addition & 0 deletions scripts/install_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'/etc/systemd/system/power-profiles-daemon.service.d/20-p16-asus.conf',
'/etc/systemd/system/multi-user.target.wants/p16-charge-limit.service',
'/usr/local/libexec/p16-charge-limit', '/usr/local/bin/p16-temperatures',
# Keep the old DSDT in checkpoints so rollback can restore earlier installs.
'/etc/initcpio/p16-acpi/DSDT.aml', '/etc/initcpio/p16-acpi/SSDT.aml',
]

Expand Down
16 changes: 10 additions & 6 deletions scripts/verify-boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import subprocess
import tempfile
from p16_release import IMAGE, PACKAGE, RELEASE
PINS = {'DSDT.aml': '80e0ad4a051704003d09029cb600a9814e1cd370ce774b9749dc09d2232b5aaf', 'SSDT.aml': '9b28ffc14c19f8b7ea2278de88ff9ec070f5f70401107d81fef067a916aa9695'}
PINS = {'SSDT.aml': '9b28ffc14c19f8b7ea2278de88ff9ec070f5f70401107d81fef067a916aa9695'}

def sections(path):
data = Path(path).read_bytes()
Expand Down Expand Up @@ -55,21 +55,25 @@ def verify_image(path):
raise ValueError('Unzulässige ACPI-Deaktivierung im UKI')
data = sec['.initrd']
offset = 0
files = {}
files = []
while data[offset:offset + 6] in (b'070701', b'070702'):
fields = [int(data[offset + 6 + i * 8:offset + 14 + i * 8], 16) for i in range(13)]
size, namesize = (fields[6], fields[11])
start = offset + 110
name = data[start:start + namesize - 1].decode().removeprefix('./')
name = data[start:start + namesize - 1].decode().removeprefix('./').rstrip('/')
start = start + namesize + 3 & ~3
if not start + size <= len(data):
raise ValueError('Abgeschnittenes frühes CPIO-Archiv')
files[name] = data[start:start + size]
if name.startswith('kernel/firmware/acpi/'):
files.append((name, data[start:start + size]))
offset = start + size + 3 & ~3
if name == 'TRAILER!!!':
break
for name, pin in PINS.items():
if not hashlib.sha256(files['kernel/firmware/acpi/' + name]).hexdigest() == pin:
expected = sorted('kernel/firmware/acpi/' + name for name in PINS)
if sorted(name for name, content in files) != expected:
raise ValueError('Only the ASUS SSDT override is allowed; use the native firmware DSDT')
for name, content in files:
if not hashlib.sha256(content).hexdigest() == PINS[name.removeprefix('kernel/firmware/acpi/')]:
raise ValueError('Falsche ACPI-Tabelle im UKI: ' + name)
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(data)
Expand Down
Loading