Skip to content

Merge aurora-wip (carries #131) with review fixes - #136

Merged
chriskearney merged 632 commits into
integration/aurorafrom
merge/aurora-wip-pr131
Oct 17, 2026
Merged

chriskearney merged 632 commits into
integration/aurorafrom
merge/aurora-wip-pr131

Conversation

@chriskearney

Copy link
Copy Markdown
Collaborator

Supersedes #131. This carries @DjDeveloperr's rebase of aurora-wip onto integration/aurora, with fixes from review on top. Authors are kept on every carried commit, and it merges with a merge commit.

The carried series

568 of #131's 569 commits, including the merge of aurora-silicon/linux#328. Compared with #131's head:

Most carried commits get new hashes, so aurora-wip needs a rebase onto integration/aurora after this merges.

user4k: built in, off unless booted with user4k=on

ARM64_USER4K_EXPERIMENTAL now defaults to y, so the release kernel includes the mixed-granule code. The feature is experimental and stays off at runtime until a user boots with user4k=on.

Without the flag:

  • the PR_AURORA_SET_*_PAGE_SIZE prctls refuse any non-native size;
  • exec never picks a non-native granule;
  • the CPU doesn't report the 4K user granule;
  • no page_ext memory is reserved.

With the flag:

  • a program gets 4K pages when it, or a parent process, asks through those prctls. The default-size setting is inherited across fork and exec;
  • page_ext takes 32 bytes per 16K page, about 0.2% of RAM.

What changes with or without the flag: the shared code is compiled in, so every process on every Mac runs DJ's restructured paths for:

  • page-table allocation and freeing, faults and PTE installs;
  • TLB flushes;
  • fork, munmap and exit;
  • rmap and reclaim walks;
  • mmap, mprotect and mremap;
  • swap and GPU buffer faults.

Compared with integration, that brings these visible changes:

  • Zero page: it grows from 16K to 64K.
  • Counters: smaps Pss for shared file pages is summed per 4K quarter; without the flag the totals are unchanged. Raw locked_vm is counted in 4K units and maxrss in kB; only BPF and debugging tools that read the kernel fields directly see this.
  • New ENOMEM cases:
    • fork and exec each allocate an MM_ID, which can fail;
    • the first swapon of a swap file sets up a permanent bio pool and rescuer, and that swapon can fail.
  • Mapping helpers: vm_map_pages() on a PFNMAP VMA returns EINVAL, and a misaligned vm_insert_pages() returns EFAULT.
  • /proc/PID/mem: reads of PFNMAP mappings go one page-table leaf at a time, and return EINVAL outside the VMA.
  • Corrupt page tables: a corrupt top-level page-table entry is logged and cleared.
  • VFIO type1:
    • it reports a 4K dirty-page size, so QEMU's legacy container treats dirty tracking as unavailable;
    • a container with no IOMMU domain reports 4K as its smallest page;
    • locked-memory refunds are no longer lost when the owner exits.

user4k has run on hardware in @DjDeveloperr's own tree on the J700 (MacBook Neo), with j700.config. This branch has only had short lab boots on an M1 MacBook Air and an M3 Pro MacBook Pro, with and without the flag (see Testing). It needs soak testing on M1, M2 and M3 before a release, both with and without the flag.

Fixes on top (64 commits)

Build

  • Restore bindgen's upstream --no-layout-tests, so the stock bindgen the release uses builds again. BINDGEN_LAYOUT_TESTS=1 keeps the layout check as an opt-in.
  • Put the tipd raw-command debugfs file behind TYPEC_TPS6598X_DEBUG_COMMANDS (default n).
  • Declare the TMPFS test dependencies (VFIO and user4k), and fix the AFK test link.
  • Add the new documents to the docs index.

Neo

  • mesa-neo-env accepts both the old and new GPU and display driver names, and still applies only on T8140.
  • The PMP report driver matches T8140 entries by compatible. The t6000 fallback is dropped from the binding and the J700 tree, so a .19 kernel booted with the new tree can't misread the report.
  • The boot NOR is read-only outside the nvram partition, and status-register, chip-erase and OTP commands are refused.

SEP

  • No module alias, so the units load the driver as before.
  • An empty or all-zero host-state file is set up again.
  • Suspend goes ahead when a capture is slow to stop, as before.
  • The T8112 DMA test harness builds again.

Display and USB-C

  • An IRQ_HPD request goes to the display route once. Unchanged port updates no longer resend it.
  • IRQ_HPD retrains the link as before, except while a relink is running.
  • A relink happens only when DCP drops a sink that still has HPD high within 10 s of the driver raising HPD (connect, DPMS wake or relink). That's at most 3 times per attachment or wake.
  • DPTX group 1 calls 3 and 4 get their reply on 13.5 firmware. 12.3 is unchanged.
  • The parser test now expects a moved tunnel to start its route.

GPU

  • The M1/M2 queue path rejects nonzero bytes in fields it doesn't support.
  • The G17 range TLBI keeps its base address in its field.

Wi-Fi (brcmfmac)

  • Flowring lookup takes the ring lock.
  • AWDL creation parameters are fixed at load.
  • AWDL is on by default, with awdl=0 as an off-switch. It's refused on interface_create v1 and v2. Only BCM4387 has been tested.
  • Queued firmware events are flushed before their worker stops.

Core kernel and user4k

  • Every user4k behaviour change in core mm, net, vfio, block, io_uring, ocfs2, tomoyo, userfaultfd, DRM shmem and prctl is gated on the Kconfig option. A build with ARM64_USER4K_EXPERIMENTAL=n gives integration's behaviour, apart from the btrfs, net, vfio, mlock and __delay items below. The release config has the option on, so these gates don't apply there.
  • Feature-on fixes:
    • a privileged exec clears the 4K preference and runs at the native granule;
    • hugetlb mappings are refused in 4K mode.
  • arch_pick_mmap_layout() uses the mm it's given instead of current->mm. With the option built in, it oopsed when called from a kernel thread, as KUnit does, with or without the flag. Exec wasn't affected.
  • perf's AUX page lookup gets its array_index_nospec back.
  • Turn the option on by default, add the user4k= boot switch, and skip the page_ext record while it's off.
  • Docs: Kconfig help, user4k.rst, user_page_size.rst, kernel-parameters and the userpages README.

What else changes on M1–M3

These run on M1–M3 with the production config, on top of the shared user4k paths above.

Display and USB-C

  • The relink described above. Integration treats that DCP drop as an unplug.
  • No bad-link report around a USB-C display's DPMS off and wake.
  • A coalesced HPD pulse from tipd becomes one IRQ_HPD request, which gets a retrain.
  • DPTX group 1 replies on 13.5 firmware.
  • The AFK ring stride now comes from the ring size.
  • DCP claims its own DMA domain.
  • One appledrm module serves every Mac, and it now needs drm_dma_helper.ko in the initramfs.
  • tipd accepts a port left in DBMa mode, and adds a read-only regs debugfs file.
  • The Type-C mux framework gains optional prepare, finish, commit and abort phases.
  • dwc3 no longer shuts the core down a second time after a failed host start.

GPU

  • The shared asahi driver chooses between the M1–M3 paths and the G17 path per device.
  • The Rust scheduler has new timeout and teardown handling.
  • Imported dma-bufs and dumb buffers become asahi GEM objects.
  • CPU faults on GEM shmem buffers take the offset-aware insert path, and mmap gets a range check.
  • GPUVM fixes.
  • The UAPI changes are append-only for M1–M3.

Touch ID

  • Sensor replies get CRC checks, re-reads and stale-frame drains.
  • SBIO exchanges run one at a time.
  • An open or start waits up to 6 s for the previous capture to stop.
  • A damaged host-state file is refused instead of erased.
  • The SEP teardown order and sensor power-line handling are stricter.

Wi-Fi (brcmfmac)

  • The AWDL vendor command is registered on every chip.
  • Detach stops the bus and event worker first.
  • A tx-status bounds check and an awdl_trace option.

Other drivers and core

  • Camera (ISP):
    • format changes are refused while buffers are allocated;
    • adding buffers past 16 gets ENOBUFS;
    • a failed boot is cleaned up.
  • AVD video decoder: up to 128 capture buffers.
  • ADMAC: synchronize waits for the channel to stop.
  • Keyboard and trackpad (dockchannel HID, M2 and M3): reports that arrive before the device is open are dropped.
  • ANE: wedge and fault-drain handling.
  • SPMI:
    • the controller polls every 10 µs instead of every 10 ms;
    • it now flushes its FIFO after a failed transfer;
    • it has only run on an M5.
  • btrfs: a fix for an EIO when the extent-map shrinker races a compressed read (8cb4cfa). It isn't upstream yet. It runs on every install, because Omarchy mounts btrfs with compress=zstd.
  • net: the skb_splice_from_iter CHECKSUM_NONE offset now matches upstream.
  • vfio type1: a lock-order fix that is only reachable with mdev.
  • mm: mlock_fixup counts pages in a long.
  • arm64: __delay reads MIDR, an M5 workaround that adds one register read per delay on M1–M3.
  • DP/HDMI audio over DCP: the changes take effect only with appledrm.hdmi_audio=1, which is off by default on M1–M3.

Testing

  • Build of 7638540: production config (user4k compiled in), W=1, LLVM=1, stock bindgen 0.73.1.

    • Exit 0, with zero new warnings compared with integration (707 against 729, compared by file and message). The module list is the same plus drm_dma_helper.ko.
    • The lab package of 7638540 was also built with bindgen 0.73.2.
  • KUnit (QEMU arm64): no failures, and no KASAN or lockdep reports.

    Suite user4k Pass Skip
    Apple not supported by config 444 2
    Neo not supported by config 62 0
    SEP not supported by config 50 1
    mm-core built in, flag off 39 0
    arm64-user4k user4k=on 107 94
    user4k mm KASAN user4k=on 154 47
    user4k data KASAN user4k=on 16 8
    user4k VFIO user4k=on 12 0
    • The Apple, Neo and SEP configs use 52-bit VA, which user4k doesn't support, so they built without it.
    • mm-core found the arch_pick_mmap_layout() oops above; it passes 39/39 with the fix.
    • The new arm64-user4k-policy suite passes 4/4 both with and without the flag.
    • The user4k skips need setups these QEMU runs don't have: a 64K-page kernel, MTE, HUGETLBFS, THP, userfaultfd, a swap disk, two CPUs, or the arm64.user4k_test=1 opt-in.
    • KUnit has no userspace, so it doesn't run ordinary 16K programs.
  • DTBs: 112 of 115 are byte-identical to integration. Only t8140-j700, t8142-j813 and t8152-j873g differ.

  • Lab boots of 7638540 (lab package build, LLVM=1, W=1, bindgen 0.73.2). These were short smoke runs, not soaks.

    • M1 MacBook Air (J313): 6 boots.
      • One hung (below). The other 5 were clean: 3 without the flag and 2 with user4k=on.
      • 3 of the clean boots were on a CalDigit TS4 Thunderbolt dock, with a 4K display over the DP tunnel, the dock's PCIe devices and its I225 Ethernet.
      • The desktop, GPU, Wi-Fi, Bluetooth, audio and the SEP service work.
      • Without the flag the 4K prctl is refused with EINVAL. With the flag the 4K workload below runs, and page_ext is 16 MB.
    • M3 Pro MacBook Pro (J516S): 2 boots, one without the flag and one with it.
      • The desktop works, with the internal panel plus an external 4K display, along with Wi-Fi, Bluetooth, audio and the SEP service.
      • With the flag the 4K workload runs, and page_ext is 75 MB.
    • The 4K workload: a program exec'd after PR_AURORA_SET_EXEC_PAGE_SIZE sees 4096-byte pages. A process started with the default set to 4K writes and hashes 256 MiB, forks 8 children and execs programs.
    • Results:
      • No oops or kernel warning splats.
      • omarchy-m-test found no regressions against the known-good kernel, except on the first M3 Pro boot: an intermittent AOP probe failure (error -62) that also takes out the ambient light sensor. It didn't recur on the next boot.
    • The M1 Air hang: the first boot hung after the old kernel shut down and before this kernel logged anything. It needed a power cycle, after which this kernel booted normally, and the next five boots were clean. The cause isn't known.
  • Still needed before the next release:

    • user4k: soak on M1, M2 and M3 without the flag (suspend/resume, swap, a GPU game, free memory after boot), then the same with user4k=on and userpages 4k. Nothing has run on an M2 or an M1 Pro/Max yet.
    • Touch ID: M1, M1 Pro/Max, M2, M2 Pro/Max, M3 Air and Neo.
    • M1–M3 peripherals:
      • USB-C and HDMI displays;
      • camera, keyboard and trackpad;
      • Wi-Fi on BCM4378, BCM4387 and BCM4388.
    • GPU: dma-buf import.
    • btrfs: a compressed-read soak.
    • Neo: boot, the nvram write path, and the rebuilt Neo Mesa.

Release notes for Neo

  • GPU UAPI: it changed, so Neo needs a rebuilt Mesa shipped with this kernel. An old Neo Mesa falls back to software rendering.
  • Render node: the mesa-neo package's udev rule now matches the renamed GPU driver, which would make the render node 0660. Drop the rule, or keep that permission on purpose.
  • Driver parameters: the separate Neo display and GPU drivers are gone. Any appledrm_neo.* or asahi_neo.* parameters become appledrm.* or asahi.*.
  • Boot NOR: the nvram partition of the boot NOR is writable on Neo. .19 kept the whole NOR read-only.
  • Release config: use the olddefconfig result. Neo display is now DRM_APPLE=m plus drm_dma_helper, which the initramfs should pick up through autodetect.

FYI:

🤖 Generated with Claude Code

Add a default-off T8140 external-controller experiment. Require the
inherited START, STOPPED and IDLE state before the standard start/wake
path and pin its DMA lifetime before any startup write. Complete this
optional preparation during platform probe so failed or deferred
controllers do not become prerequisites for the internal DRM master.

Only successfully bound, prepared controllers are included. Preserve
started-session mappings on failure and quiesce retained external
work during shutdown. Internal H17P lifecycle remains unchanged.

Hardware qualification and validated external resource mappings remain
required before this experiment can be used as a supported display path.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Describe the external display controller and four display apertures plus
its fifth clock aperture from bounded read-only ADT observations. Select
the first observed DCP mapper and the observed PIODMA stream. Add the
reciprocal rear Type-C graph while retaining disabled controller and route
status. Register names follow existing public bindings.

Mailbox, firmware handoff, routing and bandwidth integration remain
qualification requirements. This resource description is not a display
readiness claim.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Use the generic DCP fallback required by the published T8140 external binding and explicitly classify that specific compatible as an external pipeline. Otherwise the display master never discovers a successfully prepared external node. The device and route remain disabled by default.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep the external CPU supply on after the loader validates and publishes its power-domain chain. The supply remains disabled in the static tree, so a refused handoff does not register or power it. This preserves firmware ownership while allowing mailbox consumers to bind.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Separate the number of display windows from the shared firmware clock
aperture. Preserve the internal five-window profile and validate the
external four-window profile against its existing final clock resource.

Require explicit read-only experimental bandwidth offsets for the
external profile, with alignment, distinctness and aperture bounds
checks before startup. Keep both offsets unset by default and reject
missing external clock rates rather than guessing them.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Report which bounded startup wait failed so endpoint discovery, host
readiness and IOP acknowledgment failures can be distinguished without
logging message payloads. Successful handshakes remain unchanged.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external H17P trial completes endpoint setup but its normal RTKit wake
sequence times out. Use the existing early-AP wake helper for this
experimental profile, which announces host readiness before waiting for
IOP readiness. Keep the normal wake sequence for other profiles.

This ordering is a hardware qualification candidate. The previous trial
retained the internal panel but did not expose an external connector.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external H17P startup requests a shared buffer at a firmware-provided
IOVA. Its setup callback maps that buffer, but the standard RTKit ops do
not acknowledge mapped buffers. Select the existing co-processor-owned
buffer protocol for this experimental profile so RTKit acknowledges the
request and starts the crashlog endpoint before its endpoint-map reply.

Keep other display profiles on their existing ops. Restore the normal
wake ordering after the earlier AP-readiness trial also timed out, while
retaining diagnostics for subsequent qualification.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external H17P firmware announces dispext-service on the existing
IBOOT endpoint. Match that measured service name for the experimental
external profile, keeping the normal disp0-service table unchanged.
Propagate endpoint allocation and startup failures to the caller.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Report external-profile control-message types and ring readiness when
startup times out. Exclude normal receive notifications and rate-limit
control diagnostics; no payload contents or addresses are logged.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
External H17P reaches AFK transport readiness but does not announce the
DPTX port service. Its existing allocator endpoint is currently started
only later with IOMFB. Make that known endpoint available before external
service discovery, and skip its duplicate start during IOMFB startup.

Keep normal H17P ordering unchanged. This external-only ordering change
requires hardware validation; no new endpoint or message is introduced.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Limit external AFK control diagnostics to DPTX so unrelated endpoint
startup cannot consume their rate limit. Distinguish a ready transport
with no announced port service from a transport-start timeout.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The public m1n1 display startup initializes the DPTX controller endpoint
before the port endpoint. Add that existing AFK controller interface to
the experimental external H17P path, wait for its service announcement,
and then continue port discovery. Send no controller service commands.

Use a dedicated endpoint pointer and route its incoming messages through
the existing AFK worker. Cover both retained-session quiescing and normal
teardown, and never publish an allocation error as an endpoint pointer.
Normal display profiles retain their existing startup sequence.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external H17P test reaches transport readiness on the legacy DPTX
endpoint without receiving a port announcement. The public version-two
firmware implementation uses endpoint 0x2c, which this firmware advertises.
Use that endpoint for an isolated service-discovery test.

Keep a separate endpoint object with an initialization-only service table,
RX routing and lifecycle handling. This does not enable operational port
callbacks or transmit service commands; the discovered identity and
framing must be validated before extending the command path. Normal
profiles retain the legacy DPTX endpoint.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external T8140 controller announces its port on endpoint 0x2c using
version-two frames. Select that transport per endpoint, open announced
interfaces and map the single physical DCP unit to logical port zero.

Encode bounded command descriptors and validate replies against pending
owned buffers before completing them. Preserve callback tokens and limit
peer-buffer access to active allocations of the same service. Keep the
legacy transport for other display profiles.

Add host selftests for frame encoding, malformed records, response
correlation, command identifiers and the production owned-copy helper.
These pass 905 codec assertions and owned-copy cases with sanitizers;
hardware qualification of the operational transport remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Document the three bounded calibration lists used for T8140 DisplayPort
setup. Describe their register regions and ordering, and reject these
properties on other PHY variants.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The T8140 variant currently returns from AUX setup without configuring
it. The bootloader supplies three ordered DisplayPort setup lists, but
the PHY driver does not read or apply them.

Validate every list against its mapped register resource and apply the
complete bundle at AUX setup. Reject DP selection if the bundle is
missing or invalid, while keeping ordinary USB operation independent.
Other PHY generations keep their existing setup paths. Drive-level
training programming remains unchanged.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The experimental external profile uses AFKV2 for its DPTX port, but the
companion controller endpoint still uses the legacy path. Its interfaces
are discovered without receiving the version-two OPEN handshake.

Select AFKV2 for the controller before starting it, so controller
publication performs the same OPEN-before-ready sequence as the port.
Add a one-time PHY setup message to distinguish successful calibration
application from controller activation during hardware qualification.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
J700 reaches DisplayPort mode with its setup-list values present, but
readback shows AUX still clamped and both supply overrides unset. The
calibration lists do not perform the AUX power transition.

Request the small and large supplies in order, release the clamp, then
release analog shutdown. Check each masked state with a bounded poll.
Track incomplete startup so a later shutdown can unwind it, and refuse
to stop the domain if AUX cleanup fails. Leave ordinary USB operation and
other PHY generations on their existing paths.

Add host tests of the production transition helpers with register mocks,
covering phase ordering, unrelated bits and failure at every stage. These
pass with AddressSanitizer and UBSan; hardware qualification is pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The selected DP lane still reports the active power-control nibble after
its protocol changes, with both receive overrides clear. The J700 path
requires this lane to reach its quiescent state before link training.

Assert only the selected lane's receive overrides, then sequence its
power-control field into idle with bounded readback checks. Preserve the
USB lane and unrelated bits. Return failures to the mux caller instead
of publishing a completed mode transition.

Host tests of the production helpers cover both lanes, invalid indices,
unrelated-bit preservation and a failed write at every phase, under
AddressSanitizer and UBSan. Hardware qualification remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Document the firmware-provided fallback and per-rate drive tables. Each has
sixteen entries indexed by voltage swing and pre-emphasis; other PHYs do not
accept these properties.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
After correcting the crossbar route, J700 reaches SET_LINK_RATE but the
legacy PHY path waits for the wrong command-ready register. Add an isolated
T8140 main-link sequence, firmware drive presets, and cleanup ownership that
survives failed setup or shutdown. Preserve the other PMA used for USB.

Handle the version-two group-one drive command with complete bounds and
range checks before changing the PHY. Cache training values only after a
successful PHY operation and retain the legacy endpoint behavior.

Validation: 29 PHY state scenarios, 112 callback boundary cases, 100 group
routing cases, 905 AFK assertions and production owned-buffer/AUX tests under
ASan/UBSan; schema validation and strict checkpatch. Hardware qualification
remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
A drive-settings read has a short request and a larger response. Validate
both owned-buffer lengths before dispatch, allocate the response capacity,
and report its body length without extending the input read. Keep the
full response range ownership check before any callback side effect.

The 917 codec assertions now include unequal request/response lengths,
undersized buffers, limits, and invalid output pointers. Production owned
copy tests and all J700 PHY/training/group tests pass under sanitizers.
Strict checkpatch is clean.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Use the T8140 routing masks for USB3/DP and four-lane DP. The
generic sequence changes the miscellaneous lane-swap control and clears
routing state that this generation preserves. Update the low protocol
fields in order and idle only the PMAs assigned to DisplayPort.

Add opt-in callback and PHY completion diagnostics to distinguish firmware
waiting from a blocked PHY operation. No calibration values are logged.

Host checks pass: 33 PHY routing/state cases, 112 training bounds, 100
group routing cases, 917 codec assertions, owned-copy and AUX transition
tests. Strict checkpatch passes. External-display qualification remains
pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep the newer T602x DP IN clock controls for existing users, but exclude
them from the J700 physical DP output. That output uses the original
T602x gate sequence and read-clock reset location. The previous generic
path wrote the additional source/output gate registers on J700 too.

Validate the actual production mux function for every source on all three
outputs, both with and without the J700 condition: 54 select/disconnect
scenarios pass under ASan/UBSan. Existing PHY, training and AFK checks pass.
Strict checkpatch passes; external hardware qualification remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Firmware resets the active lane count to zero before setting its link
rate. Comparing the following two-lane request against that cached zero
rejected a valid activation with EINVAL and prevented link completion.

Query the PHY capacity for each request instead. Validate impossible
counts before changing state, and propagate validation failures. Cover
the zero-to-two reset sequence, unsupported counts and missing PHY.

Validation: J700 PHY/training tests, 917 AFK assertions and owned-buffer
checks pass under sanitizers; strict checkpatch is clean.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The V2 request has already passed the physical-capacity check. Restrict
the older cached-count warning to the legacy path so a valid zero-to-two
transition does not emit an incorrect error. No state transition changes.

Validation: J700 host regression tests and strict checkpatch.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Keep the separate supply request/override writes and program the link-rate
field before the final transmitter output controls. Combining those writes
and moving the rate field after output setup did not preserve the
hardware sequence.

The host test now checks the actual production register transaction order,
including supply writes. It rejects the previous implementation and passes
the corrected sequence for both PMAs and every supported rate/SSC pair.
Strict checkpatch passes.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The external firmware asks whether the connection is a virtual display.
Return a bounded, zero-initialized physical-device answer instead of
unsupported, matching the public m1n1 V2 DPTX protocol. Keep other unknown
commands rejected and retain the callback group restrictions.

Validation: query size/group checks, J700 training tests, 54 crossbar
scenarios, 917 codec assertions and owned-buffer tests; checkpatch
passes. Hardware qualification remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Move the firmware DP calibration bundle into the tunable-application
phase, before the common supply/clamp sequence. Applying it later from
AUX enable preserved the final register values but not the required
initialization order. Full DP uses its own common bundle; shared USB/DP
keeps the USB lane calibration and then applies the DP bundle.

Existing tunable operations are reused. Actual production-dispatch tests
cover full/shared/reversed DP, the legacy path and fixed USB2. Existing
PHY, codec and crossbar tests pass. Strict checkpatch passes. Hardware
qualification remains pending.

Generated-by: LLM
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
chriskearney and others added 25 commits October 17, 2026 01:09
msg_zerocopy_realloc() now allocates a fresh completion record for a
stream socket whose tail skb carries another kind of ubuf_info,
instead of returning NULL. This is needed when a retained-subpage
splice owner sits on the skb, but it also changed kernels without
ARM64_USER4K_EXPERIMENTAL: a MSG_ZEROCOPY send on a TCP socket whose
last skb belongs to another zerocopy user, such as io_uring send
zerocopy, used to fail with ENOBUFS and now succeeds on a new segment.

Only take the new path with CONFIG_MM_SUBPAGE, which is the only
configuration that creates subpage splice owners.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The four PR_AURORA_{GET,SET}_{EXEC,DEFAULT}_PAGE_SIZE prctl cases were
compiled into every kernel. On a kernel without
CONFIG_ARCH_HAS_USER_PAGE_SIZE they returned -EINVAL directly, which
skipped the unknown-option path that every other unsupported prctl
takes, so the task_prctl_unknown tracepoint no longer fired for those
option numbers.

Compile the cases and their helpers only when the architecture
supports selecting the user page size. Kernels without it handle the
option numbers exactly as before: the default case emits the
tracepoint and returns -EINVAL. Kernels with the feature are
unchanged.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ocfs2_truncate_file() was switched to truncate_pagecache() so that the
mixed-granule tail invalidation runs for small userspace leaves.
truncate_pagecache() also repeats unmap_mapping_range() after the
pagecache truncation, so kernels built without
ARM64_USER4K_EXPERIMENTAL got an extra unmap pass that the .19 kernel
and upstream ocfs2 do not perform.

Use truncate_pagecache() only when CONFIG_MM_SUBPAGE is enabled and keep
the original unmap_mapping_range() + truncate_inode_pages() sequence
otherwise, so ocfs2 truncation is unchanged in production builds.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bio_map_user_iov() now sizes its bio with bio_iov_vecs_to_alloc(), which
returns 0 for bvec-backed iterators and only differs from
iov_iter_npages() so that user4k can count user fragments. Kernels
built without ARM64_USER4K_EXPERIMENTAL therefore allocated passthrough
bios differently from .19 for bvec iterators (for example io_uring
fixed buffers on NVMe passthrough).

Use bio_iov_vecs_to_alloc() only with CONFIG_MM_SUBPAGE and keep
iov_iter_npages() otherwise.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
headpage_already_acct() was changed to skip registered buffers whose
release callback is not io_release_ubuf(), so that fragment-backed
buffers with their own charge lifetime are not matched. Without
ARM64_USER4K_EXPERIMENTAL this also skipped kernel-registered bvec
buffers, changing how a compound head page already present in such a
buffer affects locked-memory accounting compared with .19.

Apply the skip only when CONFIG_MM_SUBPAGE is enabled.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixed-granule work changed the shmem GEM helper in two places that
also run on kernels built without ARM64_USER4K_EXPERIMENTAL:

- single-page faults go through vmf_insert_pfn_prot_mkwrite_offset()
  with a run-time computed sub-page offset instead of
  vmf_insert_pfn_mkwrite();
- drm_gem_shmem_mmap() gained an extra byte-range check that can return
  -EINVAL for callers whose vm_pgoff does not carry the GEM fake offset.

These are the CPU mapping paths of every shmem-backed GPU buffer. Build
both only with CONFIG_MM_SUBPAGE and keep the .19 code otherwise, so the
production fault and mmap paths are unchanged.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UFFDIO_COPY now validates the source range against current->mm instead
of the registered mm, which matters only when the pager and target can
use different user granules. On kernels built without
ARM64_USER4K_EXPERIMENTAL it changed which task size bounds the source
range when the uffd is driven from another process (for example a
compat monitor).

Validate the source against current->mm only with CONFIG_MM_SUBPAGE and
keep the original validate_unaligned_range(ctx->mm, ...) otherwise.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tomoyo_dump_page() was rewritten to copy exec arguments through
access_remote_vm() with a byte-position cache, so that small user
leaves are read correctly. Kernels built without
ARM64_USER4K_EXPERIMENTAL do not need this and got a different lookup,
locking and caching path from .19.

Keep the rewritten helper and its position-based cache under
CONFIG_MM_SUBPAGE and restore the page-reference implementation
otherwise.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PAGEMAP_SCAN keeps the target mm's leaf size in pagemap_scan_private so
mixed-granule address spaces are walked correctly. Without
ARM64_USER4K_EXPERIMENTAL that field always holds PAGE_SIZE, but it is
loaded at run time, so the walk uses divides and multiplies instead of
shifts and the private structure grows.

Keep the field only with CONFIG_MM_SUBPAGE and read it through a helper
that returns PAGE_SIZE otherwise. No functional change; production
builds compile the scan paths as before.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixed-granule work added branches on vfio_dma::fragments to the
unpin, unmap, map, replay, re-account, owner-change and dma_rw paths.
Without CONFIG_MM_SUBPAGE no fragment provider is ever installed, so
those branches are dead at run time but are still compiled into every
kernel.

Test them through vfio_dma_has_fragments(), which folds to false when
MM_SUBPAGE is off, so a native-only build carries only the previous
code paths. No functional change.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The external pin/unpin rewrite for retained fragments also changed
kernels without mixed user granules:

 - vfio_iommu_type1_pin_pages() and _unpin_pages() round the start
   IOVA down to a page instead of using the caller's IOVA;
 - pinning a page that is already pinned now sets its dirty bit again;
 - dirty bits are set as length >> pgshift, which is zero when the
   smallest IOMMU page is larger than PAGE_SIZE, so pinned pages can
   be missing from the dirty bitmap, both at pin time and when the
   bitmap is repopulated.

Build the fragment-aware pin_pages()/unpin_pages() and their helpers
only with CONFIG_MM_SUBPAGE and keep the previous native
implementations otherwise. Repopulate the dirty bitmap with one bit per
pinned page as before. Drop the vfio_fragments_external() stub, which
no longer has a caller without MM_SUBPAGE.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vfio_change_dma_owner() now calls capable(CAP_IPC_LOCK) for
VFIO_DMA_MAP_FLAG_VADDR updates of native-page mappings, including
updates issued by the process that already owns the mapping.
capable() has side effects: it marks the task as having used a
privilege and can emit LSM audit records. The previous code returned
before the check when the mm did not change.

Return early in that case again. The result is unchanged: moving the
charge of an unchanged owner onto itself was already a no-op.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The map and unmap paths warned when the smallest IOMMU page size is
larger than PAGE_SIZE, a layout that the native pinning and dirty
tracking code does not handle. The mixed-granule work removed both
warnings for every kernel. Keep them when CONFIG_MM_SUBPAGE is off.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixed-granule work changed locked_vm accounting for every kernel:
refunds take the owner's mmap_lock without allowing a fatal signal to
interrupt them, refunds are applied to an mm whose users have all
exited, a zero-page charge no longer takes the lock, and a DMA owner
change refunds the old mm even after it has exited and undoes the new
charge if that refund fails. These keep vfio_dma::locked_vm consistent
with the pins that were actually released, which retained fragments
rely on, but they also change kernels without mixed user granules.

Keep the previous rules when CONFIG_MM_SUBPAGE is off, and build the
accounting KUnit suite, which checks the new rules, only with
MM_SUBPAGE.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The experimental exec page-size preference is set by an unprivileged
prctl and survives exec: PR_AURORA_SET_EXEC_PAGE_SIZE selects the
granule for the next exec and PR_AURORA_SET_DEFAULT_PAGE_SIZE for all
later ones. Nothing cleared it for a set-user-ID or set-group-ID
binary, so an unprivileged user could start a privileged program with
a 4K user address space and code paths it was never tested with, and
the preference was also inherited by everything that program executed.

Treat the preference like the personality flags in PER_CLEAR_ON_SETID.
arm64_bprm_prepare_mm() now also records the granule the image gets
without a preference (native for AArch64, 4K for AArch32 when
available). Once begin_new_exec() has computed the new credentials, an
exec that gains privilege (set-user-ID, set-group-ID, file
capabilities or an LSM secure-exec transition) is restaged at that
granule before the point of no return, and both the one-shot and the
default preference are cleared for the new image and its children.

Add KUnit cases that stage arguments in a 4K mm and check that a
privileged exec is restaged at the native granule with its arguments
intact, that an unprivileged one keeps 4K, and that the arch hook
records the preference-free granule for AArch64 and AArch32.

Only affects kernels built with ARM64_USER4K_EXPERIMENTAL.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HugeTLB mappings are built with the native page table geometry, and
PMD tables can be shared between processes mapping the same file
(huge_pmd_share() and pt_share_count). An mm with 4K user tables on a
16K kernel uses different table formats, and its table descriptors
reuse the same ptdesc word for fragment counts, but nothing stopped a
4K-mode process from mapping hugetlbfs, MAP_HUGETLB, SHM_HUGETLB or
MFD_HUGETLB memory.

Refuse such mappings with -EINVAL in do_mmap() when the mm's user
granule is not the native page size. Every HugeTLB VMA is created
through do_mmap(), and fork and exec cannot hand one to such an mm, so
this covers all entry points. Applications that probe for huge pages
fall back as they do when none are configured.

Add a KUnit case that maps a hugetlbfs file and MAP_HUGETLB memory into
a 4K mm (refused) and the same file into a native mm (accepted).

Only affects kernels built with ARM64_USER4K_EXPERIMENTAL.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixed-granule rework split vmf_insert_pfn_prot_mkwrite() into a
static __vmf_insert_pfn_prot_mkwrite() helper and a thin exported
wrapper, but left the kernel-doc comment on the helper. kernel-doc now
warns that the comment describes the wrong prototype and that the
helper's offset parameter is undocumented, and the exported function
lost its documentation.

Move the comment back onto vmf_insert_pfn_prot_mkwrite(). No code
change: the prod-config mm/memory.o is identical before and after.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the mmap path was converted to byte offsets for mixed user
granules, the AUX page lookup moved from perf_mmap_to_page() into
map_range(), and the array_index_nospec() clamp on the AUX page index
was dropped. The index is still steered by userspace: map_range() walks
every page of the AUX mapping, whose length the caller chooses, and only
a conditional branch keeps pagenum below rb->aux_nr_pages before
rb->aux_pages[] is read. Perf events are built into every kernel, so
this weakened the Spectre-v1 hardening on all configurations.

Clamp the index with array_index_nospec() again, as perf_mmap_to_page()
did before. The architectural behaviour is unchanged; map_range() again
contains the cmp/ngc/csdb/and mask sequence that the .19 kernel had in
perf_mmap_to_page().

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several ARM64_USER4K_KUNIT_TEST cases create shmem files with
shmem_file_setup() and fill them with kernel_write(): the shared
cache-file fixture used by many file-mapping cases, the 64K-granule
shmem fault allocation-failure case and the 4K shmem partial-hole case.
Without CONFIG_TMPFS shmem files have no write_iter, so those writes
fail and the cases fail at run time. Nothing in Kconfig asked for
TMPFS, so a kunitconfig that enables the tests but not TMPFS builds a
kernel whose tests fail.

Make ARM64_USER4K_KUNIT_TEST depend on TMPFS, as was done for the VFIO
type1 harness options. The in-tree arm64_user4k kunitconfigs already
set TMPFS=y and configure as before; without TMPFS, kunit.py now
reports the test option as unsatisfiable.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Turn ARM64_USER4K_EXPERIMENTAL on by default, so release kernels
configured with olddefconfig include it. A process that doesn't use the
PR_AURORA_SET_EXEC_PAGE_SIZE prctl keeps the native granule. The
mixed-granule paths in core mm, net, vfio, block and io_uring are then
built in, though, and every process runs through them.

So far it has run on the J700 (MacBook Neo) with j700.config, and in the
arm64_user4k KUnit configurations under QEMU. It has not been run on an
M1, M2 or M3 machine.

Replace the help text's advice to say N, which no longer matches the
default.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit made ARM64_USER4K_EXPERIMENTAL default to y, so
the configuration section no longer matches when it says the option
defaults off.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With ARM64_USER4K_EXPERIMENTAL, arch_pick_mmap_layout() takes the
mmap randomisation and the mmap_base alignment from current->mm, not
from the mm it was given. Exec only calls it once the new mm is
installed, so the two are the same there. KUnit's kunit_vm_mmap() also
calls it, from a kernel thread with no mm, and every usercopy test then
oopses on a NULL pointer dereference in arch_pick_mmap_layout().

Pass the mm through to the randomisation and mmap_base helpers.
arch_mmap_rnd() keeps using current->mm for its other callers.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ARM64_USER4K_EXPERIMENTAL is now built into the release kernel by
default. With it, any local user can ask for 4K pages through the
private PR_AURORA_SET_{EXEC,DEFAULT}_PAGE_SIZE prctls on every CPU
that supports the 4K translation granule, M1-M3 included. The
feature's hardware testing so far is DJ's J700 (MacBook Neo) only; it
has never run on an M1, M2 or M3.

Make it an experimental opt-in. The code stays compiled in, but 4K
userspace is now off unless the kernel is booted with user4k=on
(parsed with kstrtobool, so 1/0/y/n work too). user4k_enabled()
reports the choice from a __ro_after_init bool set by an early
parameter, so it is fixed before CPU capabilities are detected.

While it is off:

- arch_exec_page_size_shift(), the validator behind both SET prctls,
  refuses every non-native size with EINVAL. A native or zero size
  still succeeds, and the GET prctls report the native default.
- arm64_bprm_prepare_mm() ignores any exec preference and no longer
  gives AArch32 images 4K, so every ELF exec, and its fallback when
  the exec gains privilege, gets the native granule.
- arch_mm_init_exec() never builds an alternative-granule mm, so no
  other exec path can create one either.
- The 4K and 16K user granule capabilities are not detected, so
  nothing reports 4K support and the boot log has no "4K userspace
  translation granule" line.

Fork copies the mm and both preferences, and neither can be set while
off, so nothing non-native is inherited. The shared MM paths that
every process runs stay compiled in and do not change.

Add an arm64-user4k-policy KUnit suite that runs with or without the
parameter. It checks the request policy for both states, and that the
real prctls, exec preparation and exec mm allocation follow the boot
switch. The other user4k suites now need user4k=on. Document the
parameter in kernel-parameters.txt, the Kconfig help and user4k.rst.

Tested under QEMU KUnit: the policy suite passes 3/3 without the
parameter and 3/3 with it, and with user4k=on the arm64_user4k config
gives the same per-test results as before (103 pass, 94 skip) plus
the new cases.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MM_SUBPAGE registers a per-page 4K ownership record in page_ext whose
need() callback always returned true. A kernel with user4k built in
therefore reserved 32 bytes for every native page at boot (2 MiB per
GiB of RAM with 16K pages, about 0.2%), although 4K userspace is now
off unless the kernel is booted with user4k=on and no 4K mm can exist
without it. The feature's hardware testing so far is DJ's J700 only.

Add arch_mm_alt_granule_enabled(), which arm64 maps to
user4k_enabled(), and return it from need(). Early parameters are
parsed in setup_arch(), before mm_core_init() and
page_alloc_init_late() size page_ext, so the answer is known in time.

Without the record, page_ext can still be allocated for another user
such as page_owner or page_table_check, and mm_subpage_ext_ops then
has no offset of its own: reading it would alias that user's data.
Route every lookup in user-subpage.c through helpers that return no
record while user4k is off. Callers that run for all memory already
treat a missing record as "no 4K owner", which is the right answer
then. The shmem userfaultfd mask helpers report no missing 4K units
and ignore updates, and the smaps/pagemap mapcount helper returns zero
counts without warning.

Add a KUnit case for the lookups on a native page in both states.

Verified under QEMU KUnit (1 GiB guest): the arm64_user4k config
allocates 4 MiB of page_ext with user4k=on and 2 MiB (page_table_check
only) without it; the mm-core config allocates 2 MiB with it and none
without it, and passes 39/39 both ways. With user4k=on the user4k
configs match the earlier per-test results plus the new cases:
arm64_user4k 107 pass/94 skip, mm_kasan 154/47, data_kasan 16/8,
VFIO 12/12. Without it the policy suite passes 4/4. A production
W=1 build (Image, modules, dtbs) has no new warnings.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4K userspace is now off unless the kernel is booted with user4k=on,
and without it the kernel refuses a 4K request with EINVAL. The
launcher then only printed "Invalid argument", which does not say
what is missing.

Say in the README that the kernel must be booted with user4k=on, and
print a hint when a smaller-than-native size is refused with EINVAL.
errno is saved before the first message is printed.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants