Generalize documentation for Meta Quest headsets

This commit is contained in:
yusuf committed 2026-09-26 21:45:28 +03:00
1 parent 157e6182e6
commit b8cea4e97e
4 files changed
+205 -139

No files matched your search

+50 -29
View File
@@ -1,9 +1,12 @@
# Build
# Build guide
There are two kernel builds:
> This project currently has tested settings only for Meta Quest Pro.
- **Android kernel:** used to compile `quest_kexec.ko`
- **Target kernel:** the patched kernel booted by kexec
Two kernel trees are used:
1. The **Android kernel tree** builds `quest_kexec.ko` for the kernel currently
running on the headset.
2. The **target kernel tree** builds the custom kernel entered by kexec.
## Requirements
@@ -11,59 +14,70 @@ Install:
- Clang and LLD
- `aarch64-linux-gnu-` GCC/binutils
- `adb`, Python 3, `dtc`, `fdtput`, `curl`, `make`
- `adb`
- Python 3
- `dtc` and `fdtput`
- `curl`, `make` and standard build tools
## 1. Build the Android kernel tree
## Android kernel and loader module
The loader must match the kernel reported by:
Check the running version:
```sh
adb shell uname -r
```
For OS build `51503870024400340`, use Meta kernel commit:
```text
fa2e480a85c6adbcc73dfc4f0ab0728781e14584
```
Save the running config:
Use matching source and save the device config:
```sh
mkdir -p android-out
adb exec-out 'su -c "zcat /proc/config.gz"' > android-out/.config
```
Disable the automatic git suffix, then build `Image modules`. A full modules
build is needed for a correct `Module.symvers`.
For the tested Quest Pro release, the source commit is:
```text
fa2e480a85c6adbcc73dfc4f0ab0728781e14584
```
Build the kernel and modules so that `Module.symvers` contains the correct
symbol CRCs:
```sh
SRC=/path/to/android-kernel-source
OUT=$PWD/android-out
LOCAL=-g49638c7a8637
$SRC/scripts/config --file "$OUT/.config" --disable LOCALVERSION_AUTO
make -C "$SRC" O="$OUT" ARCH=arm64 CC=clang LD=ld.lld HOSTCC=clang \
CROSS_COMPILE=aarch64-linux-gnu- CLANG_TRIPLE=aarch64-linux-gnu- \
LOCALVERSION=-g49638c7a8637 olddefconfig
LOCALVERSION="$LOCAL" olddefconfig
make -C "$SRC" O="$OUT" ARCH=arm64 CC=clang LD=ld.lld HOSTCC=clang \
CROSS_COMPILE=aarch64-linux-gnu- CLANG_TRIPLE=aarch64-linux-gnu- \
LOCALVERSION=-g49638c7a8637 KCFLAGS=-I$SRC/drivers/pinctrl \
LOCALVERSION="$LOCAL" KCFLAGS=-I$SRC/drivers/pinctrl \
-j"$(nproc)" Image modules
```
## 2. Build the kexec and log modules
Build the kexec and log modules:
```sh
make module KDIR="$OUT" LOCALVERSION=-g49638c7a8637
modinfo -F vermagic module/quest_kexec.ko
make module KDIR="$OUT" LOCALVERSION="$LOCAL"
```
The vermagic must start with the output of `adb shell uname -r`.
Verify the result:
## 3. Build the target kernel
```sh
modinfo -F vermagic module/quest_kexec.ko
adb shell uname -r
```
Use the patched kernel fork:
The release strings must match.
## Target kernel
For the tested Quest Pro kernel:
```sh
git clone https://github.com/YusufOzmen01/oculus-linux-kernel
@@ -71,7 +85,7 @@ cd oculus-linux-kernel
git checkout oculus-quest-pro-kernel-master
```
Build it using the Quest Pro config. The target needs at least:
Build a raw ARM64 `Image` using the headset config. Required options include:
```text
CONFIG_LOCALVERSION_AUTO=n
@@ -82,13 +96,20 @@ CONFIG_PRINTK_TIME=y
CONFIG_LOG_BUF_SHIFT=20
```
The result must be a raw ARM64 `Image`.
For another Quest model or kernel release, see
[HOW_TO_PORT.md](HOW_TO_PORT.md).
## 4. Build the initramfs
## Initramfs
From this repository:
```sh
make initramfs
```
This downloads and verifies BusyBox 1.36.1, builds it statically for ARM64 and
creates `out/initramfs.gz`.
This downloads and verifies BusyBox 1.36.1, cross-builds it statically for
ARM64 and writes:
```text
out/initramfs.gz
```
+42 -54
View File
@@ -1,68 +1,56 @@
# Port a custom Quest Pro kernel
# Porting to another Meta Quest kernel
This project is for Quest Pro only. Porting here means adapting another Quest
Pro 4.19 kernel release, not another headset or SoC.
This implementation has only been tested on Meta Quest Pro. Other Quest models
use different SoCs, device trees, memory maps and hardware handoff states.
## 1. Start from the matching Meta source
## Same headset, newer kernel
Find the Meta source commit for the headset OS version. Keep a clean copy for
building the Android loader module.
1. Get the source matching the new Android release.
2. Build `quest_kexec.ko` against its exact config, local version and
`Module.symvers`.
3. Apply the patches under `kernel/patches/` to the new target source.
4. Resolve conflicts carefully in the affected drivers.
5. Capture a fresh runtime device tree with `tools/capture.sh`.
6. Test first with the supplied RAM-only initramfs.
## 2. Apply the target patches
Important target areas include:
The maintained kernel fork already contains the changes. For another source
revision, apply the patches in order:
- ARM64 entry and CPU startup
- GIC and SMMU inherited state
- RPMh and interconnect voting
- display clocks and power domains
- USB controller and PHY handoff
- AOP/QMP mailbox adoption
- SPMI and charger state
- retained logging and watchdog recovery
```sh
git am /path/to/quest-pro-kexec/kernel/patches/*.patch
```
## Another Quest headset
Resolve conflicts by behavior, not by blindly choosing one side. The important
areas are:
Do not reuse the Quest Pro physical addresses or device names without checking
them. At minimum, determine:
- ARM64 entry and SMP/GIC state
- apps SMMU and msm_bus/RPMh
- display clocks/GDSCs/SDE
- DWC3 and USB PHY
- AOP QMP adoption
- SPMI stale IRQ cleanup
- camera CPAS/CDM
- SMB5 charger handoff
- retained log and boot watchdog
- target Image, initramfs and DTB staging addresses
- safe retained-log RAM
- target kernel text offset and maximum image size
- CPU, GIC and SMMU setup
- USB controller and UDC name
- watchdog behavior
- platform-device names used by loader shutdown callbacks
- AOP/mailbox protocol used by that SoC
## 3. Keep the required target command line
Update `module/loader.c`, `module/transition.S`, `tools/prepare.py` and the target
kernel patches for the new board.
`tools/prep.sh` adds:
## First test
```text
rdinit=/init nokaslr qkx_qmp_adopt=1
initcall_blacklist=virtual_sensor_driver_init
```
Keep the first target small:
Do not use loader-side QMP disconnect. Taking the AP/AOP link down caused a PMIC
reset. The target must adopt the inherited link instead.
- static BusyBox initramfs
- no block-device mounts
- USB Ethernet only
- retained kernel log enabled
## 4. Keep the staging layout
Verify CPUs, USB and basic device probing before adding a larger userspace.
```text
Image 0x90080000
initramfs 0x93000000
DTB 0x93200000
retained log 0x9ba80000 (64 KiB)
```
The Image memory size must be at most `0x2f80000`. Initramfs and DTB must each
fit in 2 MiB.
## 5. Test in small steps
First boot with the supplied RAM-only initramfs. Check:
```sh
getconf _NPROCESSORS_ONLN
ls /dev/dri
ip addr show usb0
dmesg | grep -E 'qkxqmp|QPNP SMB5|cam_hw_cdm_init'
```
Only move to a larger userspace after the basic target is repeatable.
Do not disconnect the AOP QMP link on Quest Pro. The tested implementation
adopts the inherited link with `qkx_qmp_adopt=1`.
+39 -23
View File
@@ -1,64 +1,83 @@
# Use
# Usage guide
## 1. Capture the running device state
> These steps are tested on Meta Quest Pro with rooted Android.
Boot rooted Android and connect ADB:
## Prepare
Build the modules, target `Image` and initramfs first. See
[HOW_TO_BUILD.md](HOW_TO_BUILD.md).
Boot Android, enable ADB and confirm root:
```sh
adb shell 'su -c id'
```
Capture the current device information:
```sh
tools/capture.sh out/captured
```
This reads the runtime device tree, iomem, command line and kernel config. It
does not modify the device.
This is read-only. It captures the runtime device tree, iomem, command line and
kernel configuration.
Use the runtime tree from `/proc/device-tree`. The original
`/sys/firmware/fdt` blob is not safe for this handoff.
## 2. Prepare the payload
## Create the payload
```sh
tools/prep.sh out/captured /path/to/target/Image \
out/initramfs.gz out/payload
```
## 3. Boot it
## Boot
```sh
tools/run.sh out/payload
```
The script freshly pushes and verifies the module, Image, initramfs and DTB.
All device files are placed under `/data/local/tmp`.
The script copies the module, Image, initramfs and DTB to `/data/local/tmp`,
runs `sync`, verifies their hashes and starts kexec.
## 4. Connect to the shell
## Connect to BusyBox
Find the new Ethernet interface with `ip link`. Then:
The initramfs exposes USB Ethernet with:
- headset: `10.42.0.2`
- host: `10.42.0.1`
Find the new interface:
```sh
ip link
```
NetworkManager can normally obtain the address from the headset:
```sh
nmcli device connect <interface>
tools/shell.sh
```
If DHCP is unavailable:
If DHCP does not work:
```sh
sudo ip addr add 10.42.0.1/24 dev <interface>
tools/shell.sh
```
Run one command without telnet:
Run one command without an interactive shell:
```sh
python3 tools/qkx_sh.py 'uname -a'
```
## 5. Return to Android
## Return to Android
```sh
tools/return-android.sh
```
## Logs
## Read logs
After Android returns:
@@ -66,13 +85,10 @@ After Android returns:
tools/read-log.sh
```
This loads `marker_read.ko` and prints the target's retained RAM log.
To capture Android kernel messages over the existing USB gadget before kexec:
To capture Android kernel logs over its existing USB gadget before a run:
```sh
tools/load-usb-log.sh 60
```
`usb_log.ko` hooks the active gadget's EP0 callback and intentionally cannot be
unloaded safely. Reboot Android to remove it.
`usb_log.ko` intentionally remains installed until Android reboots.
+74 -33
View File
@@ -1,61 +1,102 @@
# Quest Pro kexec
# Meta Quest kexec
A RAM-only ARM64 kexec loader for Meta Quest Pro.
An experimental ARM64 kexec implementation for Meta Quest headsets.
It boots a patched Linux kernel from rooted Android without flashing a
partition. The included initramfs starts a BusyBox shell over USB Ethernet.
It starts a custom Linux kernel from rooted Android without flashing a
partition. The included RAM-only initramfs provides a BusyBox shell over USB
Ethernet.
## Current status
> **Note:** This project has only been developed and tested on Meta Quest Pro.
> Other Quest headsets require device-specific kernel and loader work.
Tested on Quest Pro OS build `51503870024400340`.
Working:
## What works on Quest Pro
- all 8 CPUs
- DRM and both panels
- USB Ethernet
- USB Ethernet shell
- AOP/QMP handoff
- syncboss
- camera CPAS/CDM
- SPMI and SMB5 charging
The patched kernel is in the `oculus-quest-pro-kernel-master` branch of:
The tested kernel changes are available in:
https://github.com/YusufOzmen01/oculus-linux-kernel
The same changes are also included as patch files in `kernel/patches/`.
Patch copies are also included under `kernel/patches/`.
## Layout
## Basic build
- `module/` — kexec module and log modules
- `initramfs/` — BusyBox USB-network initramfs
- `tools/` — capture, prepare, run and shell scripts
- `kernel/patches/` — Quest Pro target-kernel changes
- `HOW_TO_BUILD.md` — build instructions
- `HOW_TO_USE.md` — boot and shell instructions
- `HOW_TO_PORT.md` — adapting another Quest Pro kernel
- `TROUBLESHOOTING.md` — common failures
Install Clang/LLD, an `aarch64-linux-gnu-` toolchain, Python 3, `adb`, `dtc`,
`fdtput`, `curl` and `make`.
## Network shell
Build the kexec modules against the exact Android kernel running on the headset:
The initramfs exposes one ECM USB function:
```sh
make module KDIR=/path/to/android-kernel-out \
LOCALVERSION=-g49638c7a8637
```
- headset: `10.42.0.2`
- host: `10.42.0.1`
- shell: `telnet 10.42.0.2`
Build the BusyBox USB-network initramfs:
The target runs DHCP, so NetworkManager can normally configure the host
interface automatically.
```sh
make initramfs
```
See [HOW_TO_BUILD.md](HOW_TO_BUILD.md) for preparing the Android and target
kernel trees.
## Basic usage
With rooted Android running:
```sh
# Capture the current runtime device tree and kernel information
tools/capture.sh out/captured
# Prepare the target payload
tools/prep.sh out/captured /path/to/target/Image \
out/initramfs.gz out/payload
# Push, verify and boot it
tools/run.sh out/payload
```
Connect to the shell after USB Ethernet appears:
```sh
nmcli device connect <interface>
tools/shell.sh
```
Return to Android:
```sh
tools/return-android.sh
```
See [HOW_TO_USE.md](HOW_TO_USE.md) for more information.
## Logs
Read a retained target-kernel log after Android returns:
```sh
tools/read-log.sh
```
Capture Android kernel logs through its current USB gadget:
```sh
tools/load-usb-log.sh 60
```
## Safety
This is experimental. A bad handoff can reboot or lock the headset.
A failed handoff can reboot or lock the headset. The supplied scripts only copy
files to `/data/local/tmp`; they do not flash, format or mount partitions.
The included scripts only copy files to `/data/local/tmp`. They do not flash,
format or mount partitions.
Always build `quest_kexec.ko` against the exact Android kernel currently running
on the headset. Never force-load a module with the wrong ABI.
Never force-load a module built for a different Android kernel.
## License