Skip to content
Closed
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
92 changes: 92 additions & 0 deletions tools/aurora-apif/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
.. SPDX-License-Identifier: GPL-2.0-only

Native APIF guest preparation
=============================

Apply ``arch/arm64/configs/aurora-apif.config`` after the existing Asahi
fragment, then run ``olddefconfig``. It enables the native transport, DART,
SART, ANS, PCIe and PMP providers. Both SART components must be built in:
``APPLE_SART_APIF`` requires ``APPLE_SART=y``. These drivers use the native
Linux build workflow and need no external build VM or wrapper.

The boot producer owns physical addresses, grants, calibration and the stopped
handoff. Linux does not reconstruct them from a machine name. A generated
version-1 description can be migrated on a copy with Python libfdt::

tools/aurora-apif/prepare_guest.py producer.dtb native-apif.dtb \
--pcie-layout t6050-gen3-x1

The output must be a new file. Validation failure writes nothing. This tool
does not install, boot or modify hardware. Use the result only with the matching
Aura version-2 monitor at CPU IPA ``0x61ff0000``. The version-1 transport remains
in the tree for other consumers; its sixteen alias pages do not overlap v2.

DART unit IDs, stream counts, native force masks, DMA apertures and retained
SIDs are preserved. Provider phandles remain stable, legacy unit-address names
are removed, and standard aliases/symbol paths are repaired. The final-page
allocation guard formerly hidden in the PMP consumer match becomes explicit
provider metadata. PMP status is preserved unless ``--enable-pmp`` is supplied.
Unrelated device nodes and opaque data are preserved.

ANS requires four distinct 16 KiB ``no-map`` queue reservations with the
native addresses already pinned by firmware, in ``admin-sq``, ``admin-cq``,
``io-sq``, ``io-cq`` order. The producer must declare coherent memory and
``asahi,rtkit-quiesced`` after actually stopping both RTKit peers and controller
DMA. The converter refuses live or incomplete handoffs; it never allocates
replacement pages or invents ownership. The native driver independently
checks translations and requires an identity DMA aperture.

A preboot PCIe cycle additionally requires the explicitly selected T6050
Gen3 x1 layout, five named resources, stopped ``apple,preinitialized`` state
and existing ``apple,rid-to-sid`` tuples matching ``iommu-map``. MSI address
and AIC range remain producer-supplied. There is no automatic board/layout
inference or N1 radio driver activation in this migration.

CPU resource overlap is checked for root and identity CPU buses. Translation
through nonempty bus ranges and PCI BAR encodings remains the producer's
responsibility. The tool does not prove firmware state, native permissions,
thermal policy or hardware qualification. Old live-session m5 trees lacking
queue reservations cannot be converted into a safe stopped handoff merely by
setting a boolean. Update the producer first, retain its original DTB, and
qualify boot, storage durability, MSI, USB DMA, PMP and teardown separately.

The matched T6050 Abbey RTC is migrated to ``apple,abbey-pmu-rtc`` only
when its five named cells describe the counter, offset, alarm, control
and interrupt mask. This leaves the separate Baku RTC binding unchanged.

T6050 right-port USB DART must name its ATC2_USB power domain independently
of the DWC3 consumer. Its DART gate has no local power-state register and
requires the shared controller parent before native OPEN reads DART registers.
The migration rejects an enabled T6050 USB controller using native DART16
unless both nodes carry the producer-supplied shared power-domain dependency.
It preserves that dependency on the renamed APIF IOMMU node; platform PM
attachment therefore precedes IOMMU probe and native access. This does not
replace physical hotplug/DMA qualification.

The T6050 GPU keeps its separate compatibility-operation page at
``0x61f3c000`` with register name ``x1n1-uat``. The generic APIF v2 page
does not replace that GPU ABI. Migration preserves the GPU apertures,
CPU release offsets, guest-device mask and QSPI/NVRAM description.
It accepts the legacy ANS ``nvmmu`` resource only to remove that mediated
window after validating the stopped handoff. The explicitly selected
T6050 PCIe layout normalizes its historical ``config,port,phy,intr2axi,perst``
resource order to ``ecam,port,phy,perst,intr2axi`` without changing extents.

Kernel configuration check
--------------------------

T6050 boot NOR uses ``CONFIG_SPI_APPLE_QSPI``, not ``CONFIG_SPI_APPLE``.
The APIF fragment explicitly enables QSPIMC, SPI memory, MTD, SPI-NOR and
fixed-partition support. Apply it before ``olddefconfig``, then check the
resolved configuration against the enabled producer nodes::

tools/aurora-apif/check_guest_config.py native-apif.dtb build/.config

The checker covers the known M5 guest peripherals and their required build
options. It follows disabled parent nodes, accepts built-in or modular
drivers by default, and reports unmatched compatibles as unaudited rather
than unsupported. ``--built-in`` requires every matched option to be ``y``;
otherwise the corresponding modules must be supplied by the deployment.
This checks configuration coverage, not hardware behavior or module contents.
Migration can also take ``--kernel-config build/.config``; missing known
driver options then reject the migration before writing its output.
125 changes: 125 additions & 0 deletions tools/aurora-apif/check_guest_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
"""Check enabled guest peripheral drivers against a resolved kernel config."""
import argparse
import json
from pathlib import Path
import re
import libfdt

# This is a reviewed mapping of guest consumers, not a replacement for Kconfig.
# An unlisted compatible is reported as unaudited, never as unsupported.
DRIVERS = {
"aurora,apif-mmio": ("AURORA_APIF_MMIO",),
"apple,dart-apif": ("APPLE_DART_APIF", "AURORA_APIF_MMIO"),
"apple,sart-apif": ("APPLE_SART", "APPLE_SART_APIF", "AURORA_APIF_MMIO"),
"apple,nvme-apif": ("NVME_APPLE", "AURORA_APIF_MMIO"),
"apple,apif-pcie": ("PCIE_APPLE_APIF", "PCI", "PCI_MSI"),
"apple,t8140-qspi": ("SPI", "SPI_MEM", "SPI_APPLE_QSPI"),
"apple,t6050-qspi": ("SPI", "SPI_MEM", "SPI_APPLE_QSPI"),
"jedec,spi-nor": ("MTD", "MTD_SPI_NOR", "SPI_MEM"),
"fixed-partitions": ("MTD", "MTD_OF_PARTS"),
"fixed-clock": ("COMMON_CLK",),
"syscon": ("MFD_SYSCON",),
"apple,asc-mailbox-v4": ("APPLE_MAILBOX",),
"apple,t6050-ascwrap-v6": ("APPLE_MAILBOX",),
"apple,t6050-x1n1-ascwrap-v6": ("APPLE_MAILBOX",),
"apple,t6050-pmp-v2": ("APPLE_PMP",),
"apple,t6050-dwc3": ("USB_DWC3", "USB_DWC3_APPLE", "USB_XHCI_HCD", "USB_XHCI_PLATFORM", "USB_ROLE_SWITCH"),
"apple,t6050-spmi": ("SPMI", "SPMI_APPLE"),
"apple,sn201202x": ("TYPEC", "TYPEC_SN201202X", "TYPEC_TPS6598X_CORE"),
"apple,t8142-display-crossbar": ("MULTIPLEXER", "MUX_APPLE_DPXBAR"),
"apple,t6050-combo-phy": ("PHY_APPLE_T6050_DP", "TYPEC", "APPLE_RTKIT", "RESET_CONTROLLER"),
"apple,t6050-dp-phy": ("PHY_APPLE_T6050_DP",),
"apple,t6050-dcp": ("DRM", "DRM_APPLE", "APPLE_RTKIT"),
"apple,t6050-dcpext": ("DRM", "DRM_APPLE", "APPLE_RTKIT"),
"apple,display-subsystem": ("DRM_APPLE",),
"apple,pinctrl": ("PINCTRL", "PINCTRL_APPLE_GPIO"),
"apple,pmgr-pwrstate": ("APPLE_PMGR_PWRSTATE", "PM_GENERIC_DOMAINS"),
"apple,t8103-pmgr-pwrstate": ("APPLE_PMGR_PWRSTATE", "PM_GENERIC_DOMAINS"),
"apple,t8103-pmgr": ("APPLE_PMGR_MISC",),
"apple,dockchannel": ("APPLE_DOCKCHANNEL",),
"apple,dockchannel-hid": ("HID_DOCKCHANNEL",),
"apple,rtk-helper-asc4": ("APPLE_RTKIT_HELPER",),
"apple,s5l-fpwm": ("PWM", "PWM_APPLE"),
"pwm-leds": ("LEDS_CLASS", "LEDS_PWM", "PWM"),
"apple,wdt": ("WATCHDOG", "APPLE_WATCHDOG"),
"apple,t6050-cluster-cpufreq": ("CPU_FREQ", "ARM_APPLE_SOC_CPUFREQ"),
"apple,t6050-smc": ("MFD_MACSMC",),
"apple,smc-pcio-gpio": ("GPIO_MACSMC",),
"apple,smc-hwmon": ("SENSORS_MACSMC_HWMON",),
"apple,t8122-aic3": ("APPLE_AIC",),
"arm,armv8-timer": ("ARM_ARCH_TIMER",),
"apple,s5l-uart": ("SERIAL_SAMSUNG", "SERIAL_SAMSUNG_CONSOLE"),
"pci106b,1903": ("BT", "BT_HCICENTAURI", "APPLE_CENTAURI"),
"pci106b,1902": ("APPLE_CENTAURI_WIFI", "APPLE_CENTAURI"),
"gpu,t6050": ("DRM_ASAHI", "AURORA_APIF_GPU"),
"apple,agx-t6050": ("DRM_ASAHI", "AURORA_APIF_GPU"),
}
# Declarative/container nodes whose compatible does not imply another driver.
METADATA = {"fixed-clock", "simple-bus", "simple-mfd", "syscon", "operating-points-v2",
"arm,armv8", "framebuffer", "usb-c-connector", "apple,panel-mini-led"}


def parse_config(text):
return dict(re.findall(r"^(CONFIG_[A-Za-z0-9_]+)=(.+)$", text, re.M))


def strings(tree, node, name):
value = tree.getprop(node, name, quiet=(libfdt.NOTFOUND,))
return [] if isinstance(value, int) else bytes(value).rstrip(b"\0").decode().split("\0")


def audit(blob, config, builtin=False):
tree = libfdt.Fdt(blob)
values = parse_config(config)
result = {"matched": [], "missing": [], "unaudited": []}
accepted = ("y",) if builtin else ("y", "m")

def visit(node, parent_enabled=True):
status = strings(tree, node, "status")
enabled = parent_enabled and (not status or status in (["okay"], ["ok"]))
if enabled:
compatibles = strings(tree, node, "compatible")
matched = next((compatible for compatible in compatibles if compatible in DRIVERS), None)
path = tree.get_path(node)
if matched:
symbols = {"CONFIG_" + symbol: values.get("CONFIG_" + symbol, "n")
for symbol in DRIVERS[matched]}
result["matched"].append({"node": path, "compatible": matched, "symbols": symbols})
for symbol, value in symbols.items():
if value not in accepted:
result["missing"].append({"node": path, "symbol": symbol, "value": value})
elif node and compatibles and not any(c in METADATA for c in compatibles):
result["unaudited"].append({"node": path, "compatible": compatibles})
child = tree.first_subnode(node, quiet=(libfdt.NOTFOUND,))
while child >= 0:
visit(child, enabled)
child = tree.next_subnode(child, quiet=(libfdt.NOTFOUND,))

visit(0)
return result


def validate(blob, config, builtin=False):
result = audit(blob, config, builtin)
if result["missing"]:
missing = "; ".join(f"{entry['node']}: {entry['symbol']}={entry['value']}"
for entry in result["missing"])
raise ValueError("enabled guest peripherals lack kernel support: " + missing)
return result


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("dtb", type=Path)
parser.add_argument("config", type=Path)
parser.add_argument("--built-in", action="store_true", help="require y rather than accepting loadable modules")
args = parser.parse_args()
result = audit(args.dtb.read_bytes(), args.config.read_text(), args.built_in)
print(json.dumps(result, indent=2))
return bool(result["missing"])


if __name__ == "__main__":
raise SystemExit(main())
Loading