中文说明见 README.zh-CN.md。
Tooling to unpack a Tesla infotainment ECU ("ICE") firmware/eMMC dump, optionally patch its root filesystem for research, and boot it under QEMU for offline analysis — built from a full reverse-engineering pass over a real Model 3 Intel Elkhart Lake ("ICE-MRB") firmware image.
Repository: https://github.com/cn0xroot/Tesla_OS_On_QEMU
This repository ships tooling and documentation only. No firmware image, extracted filesystem, key material, packet capture, or log file is included or downloaded by anything here — see What's not in this repo below. You must supply your own firmware dump.
Tesla's Intel-based infotainment ECU stores its root filesystem as a
squashfs image protected by dm-verity, spread across GPT partitions using
a private dm-linear layout that the stock verity-init reconstructs at
boot time. Getting a working, inspectable copy of that filesystem — and
being able to boot it in an emulator instead of on real hardware — takes
several non-obvious steps (documented in full, including the debugging
dead-ends, in docs/firmware_unpacking_reproduction.md if you keep a
local copy of that analysis).
scripts/tesla_fw.py is a single CLI that wraps the whole pipeline:
| Stage | What it does |
|---|---|
unpack |
Partition table → extract kernel from the iasImage container → reconstruct the dm-linear-spliced rootfs → unsquashfs it |
patch |
Build a minimal dm-verity-bypass init + initrd, and repack an edited rootfs directory back into squashfs |
run |
Boot the (optionally patched) firmware in QEMU — headless, interactive GUI, GDB-attached, or a full accelerated graphics stack |
ssh / focus / capture |
Day-to-day helpers for a running QEMU guest: SSH in, fix host input focus, capture traffic |
- Linux host, Python 3.8+
qemu-system-x86_64,qemu-img(with KVM available for reasonable boot speed)squashfs-tools(unsquashfs,mksquashfs)gcc(to statically build the bypassinit),cpio,gzipfdisk/sfdisk(partition inspection/extraction)openssh-client(for thessh/capturehelpers)
git clone https://github.com/cn0xroot/Tesla_OS_On_QEMU
cd Tesla_OS_On_QEMU
# 1. Unpack a firmware dump you already have (not provided by this repo)
python3 scripts/tesla_fw.py unpack /path/to/tesla_ROM1_*.bin ./extracted both \
--ias-image ./extracted/boot/bank_a.iasImage --bzimage-len 0x832fa0 \
--make-overlay
# 2. (optional) Modify extracted/rootfs_reconstructed/squashfs-root-a/...,
# then repack it and build a verity-bypass boot path:
python3 scripts/tesla_fw.py patch repack \
extracted/rootfs_reconstructed/squashfs-root-a rootfs_edited.squashfs
python3 scripts/tesla_fw.py patch build-init ./patched_boot
# 3. Boot it
python3 scripts/tesla_fw.py run --mode headless --image /path/to/tesla_ROM1_*.bin --duration 180
python3 scripts/tesla_fw.py run --mode ui --rootfs rootfs_edited.squashfs
# 4. While it's running
python3 scripts/tesla_fw.py ssh
python3 scripts/tesla_fw.py capture eth0 30Every subcommand has its own --help with the full option list.
tesla_fw.py unpack <image.bin> <outdir> [a|b|both]
--ias-image PATH boot-partition bankX.iasImage (also extracts the kernel)
--bzimage-len HEX exact bzImage length read from the device's own bootlog.0
--skip-unsquashfs stop after reconstructing the raw image
--make-overlay also create a QEMU qcow2 copy-on-write overlay
--p2-off/--p3-off/--seg1-len/--p4-off/--p4-end-incl
override the GPT offsets if your image's
partition table differs from the default
The reconstruction step matters: on this firmware family, a single GPT
partition is too small to hold the full squashfs + dm-verity metadata.
The real root device is a dm-linear splice of that partition plus a
1GiB slice borrowed from the tail of the LVM data partition — the exact
offsets are derived from the vendor's own verity-init binary. Getting a
truncation/rounding detail in that formula wrong produces a rootfs that
looks corrupt (squashfs table pointers point past EOF) when it's really
just misaligned by a few thousand bytes; tesla_fw.py unpack reproduces
the corrected formula.
tesla_fw.py patch build-init <outdir> # compiles scripts/custom_init.c,
# packs it as a 1-file initrd
tesla_fw.py patch repack <rootfs_dir> <out.squashfs> [--comp lz4] [--block-size 131072]
tesla_fw.py patch legacy-tables <squashfs> # deprecated/archival only, see --help
The stock rootfs is dm-verity protected: touch a single byte and the
vendor's verity-init will refuse to mount it. patch build-init
compiles a small replacement PID 1 (scripts/custom_init.c) that mirrors
the vendor init's mount/switch-root sequence but skips the RSA signature
check, so any structurally valid squashfs — edited or not — can boot.
This does not, and cannot, make edited content pass Tesla's real
signature verification; it's for offline emulator research only.
tesla_fw.py run --mode headless --image IMG --duration 180 [--gui]
tesla_fw.py run --mode ui --rootfs squashfs [--kernel ...] [--initrd ...]
tesla_fw.py run --mode gdb --rootfs squashfs
tesla_fw.py run --mode glamor
--mode selects between the four QEMU launch profiles already present
under scripts/ (run_qemu.sh, run_qemu_ui.sh, run_qemu_gdb.sh,
run_v62_glamor.sh) — a fixed-duration headless boot, a long-running
interactive GUI session, a session with kernel- and userspace-GDB stubs
attached, and a full accelerated 2D graphics stack respectively. See each
script's header comment for the details of what it configures.
What actually works once you're booted, and what still doesn't:
| Subsystem | Status | Fix | Reproducible from this repo? |
|---|---|---|---|
| Touch input | Working | Replaced touch-proxy (wrong coordinate space) with x11-input-proxy, which injects clicks via X11 XTest directly in screen coordinates (v63) |
Partially — scripts/x11-input-proxy.c is tracked; the rootfs-side wiring that runs it isn't |
Browser — Apple Music web view (chromium-app) |
Working (renders, stable) | Forced software rendering (--disable-gpu --disable-gpu-compositing) plus sandbox netns/bind-mount fixes; the streaming backend itself is unreachable from an emulator, which isn't a guest bug |
No — baked into the rootfs only |
| Browser — built-in in-car Browser | Partially working | Panel opens after a boot-race workaround, but page content renders as a black screen: no GPU acceleration and no swiftshader software-GL fallback compiled in — an open, unresolved hard limit | No — open issue |
| Audio / sound card | Working (host playback) | The OEM kernel ships no PCI/USB sound driver at all; a custom AC97 kernel module plus QEMU -audiodev pa,... streams guest audio out to the host's PulseAudio/PipeWire. A prior crash loop was fixed separately by pre-creating /var/lib/alsa and /var/lib/audiod |
Partially — the QEMU-side -audiodev wiring is in run_qemu_ui.sh/run_v62_glamor.sh; the kernel module and rootfs directory fix aren't |
| Network (app connectivity + stable SSH, simultaneously) | Working | v68's dual-NIC design: eth0 stays under connman's static IP for app connectivity, eth1 gets its own static IP outside connman's reach for SSH, so neither interferes with the other. A separate earlier fix stops connman from picking a factory-only config that drops the whole connection |
Yes — tesla_fw.py patch network-fix (see below) |
| UI rendering (2D) | Working | Pure 2D scanout (-device virtio-vga, gl=on left off) — the real Tesla UI fully renders and keeps running (Factory Net label, map grid, loading cards, bottom taskbar all visible and animating) |
No — QEMU display flags only, no rootfs change needed |
| 3D hardware acceleration | Not available on this host | Tried virtio-vga-gl + gl=on (virgl) to fix both the sluggish 2D refresh rate and the mame black screen below — QEMU segfaults inside libGLX_nvidia.so around the 35s mark when the guest starts creating a GL context; forcing Mesa software GLX avoids that crash but QEMU still silently dies at the same point. Permanently disabled; kept as an experimental GL=on switch in run_qemu_ui.sh only |
N/A — host GPU/driver limitation, not something this repo's tooling can fix |
| Maps / navigation | Working | Needed a kernel rebuild (CONFIG_NETFILTER_XT_TARGET_REDIRECT, CONFIG_NF_NAT_REDIRECT) so a transparent SOCKS proxy can REDIRECT the map/streaming traffic that ignores http_proxy, plus raising CONFIG_NR_CPUS from 4 to 8. This is what "mapfix" in the STABLE rootfs filename refers to. Confirmed with real Google map tiles and a live supercharger list |
No — baked into the custom bzImage_redirect_smp8 kernel build, not a rootfs file |
| Built-in games | Mixed | Native Toybox mini-games (Light Show, Sketchpad, etc.) work — they're rendered by QtCar itself, no separate GL process needed. The Arcade app opens, but its downloadable titles (Beach Buggy Racing 2, etc.) can't install — Tesla's CDN backend is unreachable from an emulator, an environment limit rather than a bug. Classic mame titles (Missile Command, Asteroids, ...) don't render at all: no GPU, no software SDL renderer compiled in, and Mesa's software EGL-on-X11 path doesn't work in this build — the same hard limit as 3D acceleration above | Partially — the audio-gate workaround (a synthetic gameaudio-ready runit service) is a rootfs change, not reproducible from this repo yet; the mame/Arcade limitations aren't fixable from software at all |
A few things worth being explicit about:
-
Touch, Chromium, audio, maps, and the games audio-gate workaround are "working" only in the pre-built
..._STABLE.squashfsimages referenced byrun_qemu_ui.sh/run_v62_glamor.shby default — those images aren't distributed with this repo (see What's not in this repo), so a rootfs you build yourself from scratch viaunpack+patch repackwon't have those fixes unless you reconstruct them yourself. The graphics/audio/touch/maps fixes involve binary patches, a rebuilt kernel, and a custom kernel module that aren't reproduced as standalone tooling here yet. -
The network fix is the one exception: it's fully reproducible from this repo. Run it against an already-booted guest (one started with
run --mode uior--mode glamor, which already carry the second NIC):python3 scripts/tesla_fw.py patch network-fix
This applies the exact
connmanctlsequence documented in Part 2 to configureeth0, and a standard-tooling equivalent (not the original file, which wasn't preserved) to bring up a static IP oneth1. It only affects the currently running guest — it edits runtime network state, not/etc/runit/1itself, so it doesn't survive a reboot. See the script's own header comment (scripts/apply_network_fix.sh) for the full detail on what's verified-exact versus reconstructed.
The full research write-up — methodology, dead ends, and the findings this toolchain distills — is published in two parts on the author's blog:
- Part 1: Unpacking
— GPT partition analysis, reverse-engineering the
iasImagecontainer from the device's ownbootlog.0, disassemblingverity-initto recover the exactdm-linearsplice formula, and the ±2MB exhaustive search that traced a "corrupted" rootfs down to one missed(p4_bytes >> 12) << 3truncation. - Part 2: Debugging + fixing
— the boot-chain fixes (kernel module version pinning, RCU stall
tuning, a LUKS/quota reformat-on-every-boot loop), the graphics stack
port (DRM driver selection, Mesa ABI mismatches, the
virtio-gpu-gl→virtio-vgafallback), the touch-input protocol translation, and two security findings worth flagging on their own:sshd_config's dev/production split gated byis-fused(), and a missingclock_gettimeentry inQtCarDvServer.kafel's seccomp allowlist that only surfaces because this build's glibc skips the VDSO fast path.
.gitignore is a strict allowlist: everything is ignored by default, and
only the tool sources (scripts/*.py, *.sh, *.c, *.patch) and this
README are tracked. In particular, none of the following are ever
committed:
- The firmware dump itself, or anything derived from it (extracted
partitions, reconstructed rootfs images,
.squashfs/.qcow2files) - SSH keys (
scripts/ssh_key/) - Packet captures, serial console logs, screenshots of the booted UI
- Any file under
extracted/,build/,docs/, or similar working directories your own local checkout may accumulate
If you fork this for your own research, keep that boundary: the tooling is safe to share, the artifacts it produces from a real device usually are not.
This project exists to make Tesla's own infotainment firmware inspectable
and bootable offline, in an emulator, for security research and
defensive analysis — not to defeat vehicle security controls on a live,
in-service car. The dm-verity bypass in patch build-init only helps you
boot a modified image inside QEMU; it has no effect on, and provides no
path to, a real vehicle's own verity/TPM chain. Use this against firmware
you're authorized to analyze (your own hardware, or under a disclosed
research/bug-bounty engagement), and follow responsible disclosure for
anything you find.
Thanks to @vessial (GitHub) for guidance on the firmware
unpacking approach — the dm-linear reconstruction work in unpack
benefited directly from that input (credited in Part 2
of the write-up above).
The accelerated graphics stack used by run --mode glamor
(scripts/run_v62_glamor.sh) ports the Ubuntu Xorg + modesetting +
glamor (llvmpipe) approach pioneered by
denysvitali/tesla-qemu onto
this project's real Tesla 4.14 rootfs, including its vblank-wait DRM
patch. scripts/touch-proxy.c is likewise based on that project's
tools/touch-proxy.c. Credit to @denysvitali
for the original QEMU-graphics groundwork this builds on.
Tesla's own public source releases were essential reference material for identifying and cross-checking the platform this firmware targets:
- teslamotors/linux — the vendor
kernel source, used to cross-reference the extracted
bzImage(version,Tesla Model3 hardware core supportstring, driver selection) against a known-authentic build. - teslamotors/coreboot —
Tesla's coreboot sources, relevant to the early Intel Elkhart Lake
boot chain that hands off into the
iasImage/verity-initsequence this project'sunpack/patchtooling targets. - teslamotors/buildroot —
confirmed the
ice-mrb("In-Car Entertainment") platform naming and general userspace layout (runit,connman, package set) match this firmware, and gave a version baseline to diff the extracted rootfs against.
This project is licensed under the GNU General Public License v3.0 —
see LICENSE for the full text.
One exception: scripts/touch-proxy.c is a derivative of
denysvitali/tesla-qemu's
tools/touch-proxy.c, and that upstream project does not declare a
license of its own. That single file is therefore not covered by this
repository's GPL-3.0 grant — see the note at the top of the file.
Everything else in this repository (including scripts/tesla_fw.py,
scripts/custom_init.c, and this documentation) is original work
licensed under GPL-3.0.