Google Pixel 7a (google-lynx)
| This device is marked as not booting. |
|
Google Pixel 7a | |
| Manufacturer | |
|---|---|
| Name | Pixel 7a |
| Codename | google-lynx |
| Released | 2023 |
| Type | handset |
| Hardware | |
| Chipset | Google Tensor G2 |
| CPU |
Octa-core (2x2.85 GHz Cortex-X1 2x2.35 GHz Cortex-A78 4x1.80 GHz Cortex-A55) |
| GPU | Mali-G710 MP7 |
| Display | FHD+ OLED 2400×1080 90Hz |
| Storage | 128 GB |
| Memory | 8 GB |
| Architecture | aarch64 |
| Software | |
Original software The software and version the device was shipped with. |
Android 13 |
Extended version The most recent supported version from the manufacturer. |
Android |
| postmarketOS | |
Mainline Instead of a Linux kernel fork, it is possible to run (Close to) Mainline. |
no |
pmOS kernel The kernel version that runs on the device's port. |
6.x.x |
Unixbench score Unixbench Whetstone/Dhrystone score. See Unixbench. |
0.0 |
The Google Pixel 7a is Google's 2023 mid-range smartphone, leveraging the capabilities of Google's in-house Tensor G2 chipset.
Contributors
Users owning this device
- Alexander (Notes: Daily driver, GrapheneOS)
- Angelthorns (Notes: GrapheneOS)
- Bac0n (Notes: Main smartphone)
- Diogotavc (Notes: Daily driver with GrapheneOS)
- Flysqu (Notes: LineageOS)
- Giopan (Notes: Lineage OS)
- Jacoblightning3 (Notes: Running GrapheneOS)
- Julian (Notes: Daily driver, LineageOS)
- Little-miss-synth (Notes: running grapheneOS - Main phone)
- Omar mahboubi (Notes: Trying to port pmOS)
- Omgmog
- Segfault (Notes: Daily driver)
- Soopyc
- ZenTheFox
How to Unlock Bootloader
WARNING: Doing this will wipe ALL data on your device. Please backup all your personal data before continuing!
- In
Settings>About Phone, scroll down and tapBuild Number7 times. - In
Settings>System>Developer Options, enableUSB Debuggingand tap OK. - In
Developer Options, enableOEM Unlocking. You will be prompted for your PIN (if applicable) and warned about the risks of OEM unlocking. Tap Enable. - Backup any important data.
- Connect your device to a PC and open a terminal.
- Confirm your device is detected by typing
adb devices. - Authorize the device with the onscreen dialogue if it's listed as Unauthorized.
- Reboot to the bootloader with
adb reboot bootloader. - Unlock the bootloader with
fastboot flashing unlock. - Press continue on your device's screen.
- Reboot
How to enter flash mode
- Power off the device.
- Press and hold and until
Fastboot Modeappears.
You are now ready to flash!
Installation
TBD
Prerequisites
$ sudo apt install -y fastboot android-sdk-platform-tools python3-pip pmbootstrap
$ pip install payload_dumper mkbootimg
Initialize pmbootstrap
$ pmbootstrap init
Answer the prompts:
- Work path: Press Enter (default)
- pmaports path: Press Enter (default)
- Channel:
edge - Vendor:
google - Device codename:
lynx - Username:
user - Provider (audio): Press Enter (default)
- Provider (wifi): Press Enter (default)
Device Package
Create the device package directory:
$ mkdir -p ~/.local/var/pmaports/device/testing/device-google-lynx
deviceinfo
Create ~/.local/var/pmaports/device/testing/device-google-lynx/deviceinfo:
deviceinfo_format_version="0" deviceinfo_name="Google Pixel 7a" deviceinfo_manufacturer="Google" deviceinfo_codename="google-lynx" deviceinfo_year="2023" deviceinfo_arch="aarch64" deviceinfo_gpu_accelerated="true" deviceinfo_chassis="handset" deviceinfo_keyboard="false" deviceinfo_external_storage="false" deviceinfo_screen_width="1080" deviceinfo_screen_height="2400" deviceinfo_flash_method="fastboot" deviceinfo_kernel_cmdline="earlycon=exynos4210,0x10A00000 console=ttySAC0,115200 androidboot.console=ttySAC0 printk.devkmsg=on cma_sysfs.experimental=Y cgroup_disable=memory rcupdate.rcu_expedited=1 rcu_nocbs=all rcutree.enable_rcu_lazy page_pinner=on swiotlb=1024 cgroup.memory=nokmem rodata=on at24.write_timeout=100 log_buf_len=1024K android_arch_task_struct_size=512" deviceinfo_generate_bootimg="true" deviceinfo_bootimg_qcdt="false" deviceinfo_bootimg_dtb_second="false" deviceinfo_flash_offset_base="0x00000000" deviceinfo_flash_offset_kernel="0x00008000" deviceinfo_flash_offset_ramdisk="0x01000000" deviceinfo_flash_offset_second="0x00000000" deviceinfo_flash_offset_tags="0x00000100" deviceinfo_flash_pagesize="2048" deviceinfo_flash_sparse="true" deviceinfo_bootimg_header_version="4" deviceinfo_bootconfig="androidboot.load_modules_parallel=true androidboot.boot_devices=14700000.ufs" deviceinfo_ab_partitions="true" deviceinfo_ab_partition_suffix="_a" deviceinfo_ab_partition_list="boot dtbo vendor_boot vendor_kernel_boot system system_ext product vendor vendor_dlkm" deviceinfo_usb_idVendor="0x18d1" deviceinfo_usb_idProduct="0x4ee3" deviceinfo_flash_kernel_on_update="true" deviceinfo_super_partitions="system system_ext product vendor vendor_dlkm" deviceinfo_dtb="google/gs201-lynx" deviceinfo_append_dtb="true" deviceinfo_rootfs_image_sector_size="4096" deviceinfo_generate_vendor_bootimg="true" deviceinfo_generate_vendor_kernel_bootimg="true" deviceinfo_flash_fastboot_partition_vendor_boot="true" deviceinfo_flash_fastboot_partition_dtbo="true"
Critical settings explained:
deviceinfo_flash_pagesize="2048"— Must be 2048 for Pixel 7a, not 4096deviceinfo_bootimg_header_version="4"— GKI v4 boot header requireddeviceinfo_rootfs_image_sector_size="4096"— UFS storage uses 4KB sectorsdeviceinfo_append_dtb="true"— DTB is embedded in vendor_kernel_boot
modules-initfs
Create ~/.local/var/pmaports/device/testing/device-google-lynx/modules-initfs:
# Storage - UFS ufs-exynos-gs ufs-pixel-fips140 # Samsung Exynos platform essentials exynos-pmu-if pinctrl-exynos-gs exynos-cpupm exynos-pm exynos-pd exynos-pd_el3 exynos-pd_hsi0 # I2C and GPIO i2c-exynos5 i2c-acpm # Serial console exynos_tty # Clock and timer clk_exynos_gs exynos_mct # USB dwc3-exynos-usb xhci-exynos phy-exynos-usbdrd-super # USB gadget and networking usbnet cdc_ether cdc_eem cdc_ncm cdc-acm # USB Type-C tcpci_max77759 # DMA pl330 samsung-dma # Thermal gs_thermal # GPU (Mali G710) mali_kbase # PMIC / regulator s2mpg12-mfd s2mpg12-regulator s2mpg13-mfd s2mpg13-regulator max77779_pmic max77779_pmic_irq pmic_class # Power management exynos_devfreq google-bms google-charger google-battery
Note: The watchdog module (s3c2410_wdt) is intentionally excluded.
Loading it without petting the watchdog causes a 60-second reboot loop.
The LOS kernel has a hardware watchdog that must be handled by the initramfs.
APKBUILD
Create ~/.local/var/pmaports/device/testing/device-google-lynx/APKBUILD:
pkgname=device-google-lynx
pkgdesc="Google Pixel 7a"
pkgver=1
pkgrel=1
url="https://postmarketos.org"
license="MIT"
arch="aarch64"
options="!check !archcheck"
depends="
linux-postmarketos-gs201-6.1
mkbootimg
postmarketos-base
firmware-google-lynx
"
makedepends="devicepkg-dev"
source="
deviceinfo
modules-initfs
"
build() {
devicepkg_build $startdir $pkgname
}
package() {
devicepkg_package $startdir $pkgname
}
Generate checksums:
$ cd ~/.local/var/pmaports/device/testing/device-google-lynx
$ pmbootstrap checksum device-google-lynx
Kernel Package
The Pixel 7a uses a prebuilt 6.1 kernel extracted from LineageOS 23.2. Building from source requires Google's Bazel build system and the full LOS kernel manifest. For porting, the prebuilt kernel is used.
Create the kernel package directory:
$ mkdir -p ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1
APKBUILD
Create ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1/APKBUILD:
_flavor="postmarketos-gs201-6.1"
pkgname=linux-$_flavor
pkgver=6.1.145
pkgrel=0
_krel="6.1.145-android14-11-gec45f20f38ea-aba4c29fc158"
pkgdesc="Prebuilt 6.1.145 kernel for Google Tensor G2 (gs201)"
arch="aarch64"
url="https://github.com/LineageOS/android_kernel_google_gs-6.1_manifest"
license="GPL-2.0-only"
options="!check !strip !archcheck !tracedeps pmb:cross-native"
source="
get-kernel.sh
vmlinuz
gs201-lynx.dtb
kernel.release
modules.tar.gz
"
builddir="$srcdir"
package() {
install -Dm644 "$srcdir"/vmlinuz "$pkgdir"/boot/vmlinuz
install -Dm644 "$srcdir"/gs201-lynx.dtb "$pkgdir"/boot/dtbs/google/gs201-lynx.dtb
mkdir -p "$pkgdir"/lib/modules
tar xzf "$srcdir"/modules.tar.gz -C "$pkgdir"/lib/modules
sed -i "s|/vendor/lib/modules/|/lib/modules/$_krel/|g" "$pkgdir"/lib/modules/"$_krel"/modules.dep
install -D "$srcdir/kernel.release" "$pkgdir/usr/share/kernel/$_flavor/kernel.release"
}
get-kernel.sh
Create ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1/get-kernel.sh:
#!/bin/sh
# Downloads latest LineageOS build and extracts kernel + modules + DTB
set -e
KERNEL_DIR="$(dirname "$(readlink -f "$0")")"
pip install --break-system-packages payload_dumper mkbootimg 2>/dev/null || true
echo "Looking up latest LineageOS build for lynx..."
URL=$(curl -s "https://download.lineageos.org/api/v1/lynx/nightly/1" | \
python3 -c "import sys,json; print(json.load(sys.stdin)['response'][0]['url'])")
ZIP="$KERNEL_DIR/$(basename "$URL")"
if [ ! -f "$ZIP" ]; then
echo "Downloading $URL ..."
curl -L -o "$ZIP" "$URL"
else
echo "Already downloaded: $ZIP"
fi
WORKDIR="$KERNEL_DIR/.work"
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"
echo "Extracting payload from zip..."
unzip -o "$ZIP" payload.bin -d "$WORKDIR" >/dev/null
echo "Extracting partition images..."
python3 -m payload_dumper "$WORKDIR/payload.bin" \
--partitions boot,vendor_kernel_boot,vendor_dlkm \
--out "$WORKDIR/images"
echo "Extracting kernel..."
BOOT_DIR="$WORKDIR/boot_unpacked"
mkdir -p "$BOOT_DIR"
unpack_bootimg --boot_img "$WORKDIR/images/boot.img" --out "$BOOT_DIR" 2>/dev/null
mkdir -p "$KERNEL_DIR/boot"
cp "$BOOT_DIR/kernel" "$KERNEL_DIR/boot/vmlinuz" 2>/dev/null || \
cp "$BOOT_DIR/Image" "$KERNEL_DIR/boot/vmlinuz" 2>/dev/null
echo "Extracting DTB..."
VK_DIR="$WORKDIR/vk_unpacked"
mkdir -p "$VK_DIR"
unpack_bootimg --boot_img "$WORKDIR/images/vendor_kernel_boot.img" --out "$VK_DIR" 2>/dev/null
mkdir -p "$KERNEL_DIR/boot/dtbs/google"
cp "$VK_DIR/dtb" "$KERNEL_DIR/boot/dtbs/google/gs201-lynx.dtb" 2>/dev/null || \
cp "$BOOT_DIR/dtb" "$KERNEL_DIR/boot/dtbs/google/gs201-lynx.dtb" 2>/dev/null
echo "Extracting modules..."
MODULES_OUT="$KERNEL_DIR/lib/modules"
mkdir -p "$MODULES_OUT"
TMP_MNT="$WORKDIR/vendor_dlkm_mnt"
mkdir -p "$TMP_MNT"
sudo mount -t erofs -o ro,loop "$WORKDIR/images/vendor_dlkm.img" "$TMP_MNT" 2>/dev/null || \
sudo mount -t ext4 -o ro,loop "$WORKDIR/images/vendor_dlkm.img" "$TMP_MNT" 2>/dev/null || true
if mountpoint -q "$TMP_MNT" 2>/dev/null; then
cp -a "$TMP_MNT/lib/modules/"* "$MODULES_OUT/" 2>/dev/null || true
sudo umount "$TMP_MNT"
fi
rmdir "$TMP_MNT" 2>/dev/null
cd "$MODULES_OUT"
tar czf "$KERNEL_DIR/modules.tar.gz" .
RELEASE=$(ls "$MODULES_OUT" 2>/dev/null | head -1)
echo "$RELEASE" > "$KERNEL_DIR/kernel.release"
rm -rf "$WORKDIR"
echo "Done! Kernel + DTB + modules extracted to $KERNEL_DIR"
Make executable and run:
$ chmod +x ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1/get-kernel.sh
$ cd ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1
$ ./get-kernel.sh
$ pmbootstrap checksum linux-postmarketos-gs201-6.1
Build Rootfs
$ pmbootstrap install --password 147147
The rootfs image is created at:
~/.local/var/pmbootstrap/chroot_native/home/pmos/rootfs/google-lynx.img
Flashing
Important: The Pixel 7a bootloader enforces Android Verified Boot (AVB). The LineageOS vbmeta chain must be used. Custom-signed boot images do not pass verification. Only the vendor_kernel_boot partition can be customized.
Extract all LOS partition images:
$ cd ~/.local/var/pmaports/device/testing/linux-postmarketos-gs201-6.1
$ python3 -m payload_dumper .work/payload.bin --out /tmp/los_images
Flash everything:
# Put phone in fastboot mode (Volume Down + Power)
# Flash rootfs
$ fastboot flash userdata ~/.local/var/pmbootstrap/chroot_native/home/pmos/rootfs/google-lynx.img
# Flash LOS boot chain
$ fastboot flash boot_a /tmp/los_images/boot.img
$ fastboot flash init_boot_a /tmp/los_images/init_boot.img
$ fastboot flash vendor_kernel_boot_a /tmp/los_images/vendor_kernel_boot.img
$ fastboot flash dtbo_a /tmp/los_images/dtbo.img
# Flash vbmeta (both slots)
$ fastboot flash vbmeta_a /tmp/los_images/vbmeta.img
$ fastboot flash vbmeta_b /tmp/los_images/vbmeta.img
$ fastboot flash vbmeta_system_a /tmp/los_images/vbmeta_system.img
$ fastboot flash vbmeta_system_b /tmp/los_images/vbmeta_system.img
$ fastboot flash vbmeta_vendor_a /tmp/los_images/vbmeta_vendor.img
$ fastboot flash vbmeta_vendor_b /tmp/los_images/vbmeta_vendor.img
$ fastboot set_active a
Booting
The device cannot boot pmOS normally. It must boot to LineageOS recovery, then pmOS is accessed via ADB and chroot:
# Boot to recovery
$ fastboot reboot recovery
# Tap "Allow" on the phone for ADB authorization
# Mount pmOS and enter chroot
$ adb shell
# mount -t ext4 /dev/block/by-name/userdata /data
# mount --bind /dev /data/dev
# mount --bind /proc /data/proc
# mount --bind /sys /data/sys
# chroot /data /bin/ash
To resize the rootfs to use all available userdata space:
# resize2fs /dev/block/by-name/userdata
Technical Details
Boot Chain
The Pixel 7a uses GKI v4 boot image headers with this partition layout:
| Partition | Contents | Size | AVB |
|---|---|---|---|
| boot | Kernel Image (LZ4 compressed), no ramdisk | 64 MB | Chained to LOS key |
| init_boot | Android init binary (recovery) | 8 MB | Chained to LOS key |
| vendor_kernel_boot | DTB + kernel modules (210 .ko files) | 64 MB | Hash descriptor |
| dtbo | Board-specific device tree overlay | 16 MB | Hash descriptor |
| vbmeta | AVB chain root (flags=3, verification disabled) | 8 KB | Signed LOS key |
Ramdisk Concatenation Order
The bootloader concatenates ramdisks in this order:
boot (empty) -> init_boot (Android init) -> vendor_kernel_boot
The Android init binary from init_boot claims PID 1 before any code from vendor_kernel_boot can execute. This is why pmOS cannot boot without recovery mode.
Why Normal Boot Fails
- The Android init (PID 1) starts and cannot find the Android system partition
- After ~39 seconds, Android init calls
reboot - The device returns to fastboot
The init= kernel parameter is ignored by this kernel.
Custom boot.img with ramdisk causes an instant kernel panic.
The init_boot partition cannot be replaced (AVB chain requires LOS key).
USB Networking
The kernel has these USB gadget drivers built-in:
CONFIG_USB_CONFIGFS_NCM=y CONFIG_USB_CONFIGFS_ECM=y CONFIG_USB_CONFIGFS_EEM=y CONFIG_USB_F_NCM=y CONFIG_USB_F_ECM=y
NCM functions can be created via configfs and activated on the UDC
(11210000.dwc3), but the USB host never re-enumerates.
The PID stays at 4ee0 (fastboot) instead of switching to
4ee3 (network). Recovery mode uses FunctionFS (FFS) for ADB
which works because the Android init stack handles enumeration.
Super Partition
The super partition (8.53 GB) contains logical partitions:
| Partition | Size |
|---|---|
| system | 1.2 GB |
| system_ext | 534 MB |
| product | 2.1 GB |
| vendor | 801 MB |
| vendor_dlkm | 63 MB |
The lpmake tool can create a super image, but the bootloader
does not create logical partitions from it on this device. The LOS OTA
mechanism via recovery sideload is required for proper super partition setup.
Path to Normal Boot
- Clone the full LOS kernel manifest:
git clone https://github.com/LineageOS/android_kernel_google_gs-6.1_manifest - Build with Bazel, adding
CONFIG_INITRAMFS_SOURCEpointing to a pmOS initramfs cpio - Enable vendor USB drivers (exynos DWC3, PHY) from the separate vendor repos
- Embed the pmOS initramfs into the kernel binary
A custom kernel without vendor drivers was built and confirmed to boot: the USB PID changed from 4ee0 to a different value, proving the kernel with embedded initramfs executes. The vendor driver source is in separate repos under the LOS manifest.