DEV Community

Cover image for Debian on a Microsoft Lumia 950 XL: An Honest Status
Matt Miller
Matt Miller

Posted on

Debian on a Microsoft Lumia 950 XL: An Honest Status

From win11 arm to Debian..

After a few posts about resurrecting hardware nobody supports anymore, here is another one in that spirit: a Microsoft Lumia 950 XL that shipped as a Windows phone, now running Debian. This is the "where are we, actually" write-up — what works, what doesn't, and the honest reasoning behind both. There are no miracles here, but there is a real, usable outcome, and one wall I couldn't climb.

The device, and why bother

The Lumia 950 XL is a 2015 flagship with a Snapdragon 810 (MSM8994), 3 GB RAM, and a 1080×1920 AMOLED. Its OS is long dead. The reason it's interesting at all is that the community already did the hard part years ago: the WOA-Project wrote an EDK2/UEFI firmware for it, which is how people boot Windows 10/11 ARM on it today.

And a UEFI that can boot Windows can boot a Linux kernel the same way — vmlinuz + a device tree + GRUB off the EFI System Partition. So the plan was short: get into the storage, lay down a Debian arm64 rootfs and a kernel, boot it. Each of those steps had a trapdoor under it.

Getting in: the phone fights back

First lesson, learned immediately: plug the phone (running Windows) into a PC and nothing happens. No new device, no dmesg line, nothing.

That's because full desktop Windows on ARM makes the phone a USB host, not a device. Two hosts do nothing for each other. The way in is the firmware itself. Power off, hold Volume Down, tap Power, and you reach a menu:

windows 10
dummy, please ignore
Developer Menu
Enter fullscreen mode Exit fullscreen mode

Developer Menu → Mass Storage Mode exposes the whole eMMC to the PC as a plain USB disk — 29 GB across 43 partitions. Most of those are sacred Qualcomm firmware (SBL1, TZ, RPM, the MODEM_* and BACKUP_* regions); touching one bricks the phone. The ones that mattered were the leftover Windows partitions:

# Name Size What it is
41 SYSTEM 100 M the active EFI System Partition
43 Windows 17.9 G the Windows 11 ARM install

I chose to replace Windows outright: partition 43 becomes the Debian root, GRUB lives on the ESP.

Debian onto the eMMC

The build itself is ordinary arm64 cross-bootstrapping — debootstrap a Bookworm rootfs, add the community empyreal96 prebuilt 5.7 kernel and its msm8994 device tree, reformat partition 43 as ext4, rsync the rootfs in, install GRUB on the ESP. Then two non-obvious boot failures cost real time.

The UEFI ignores the fallback bootloader. It boots its saved "Windows" entry, which points at \EFI\Microsoft\Boot\bootmgfw.efi, not the generic \EFI\Boot\bootaa64.efi where I'd put GRUB. The fix is to install GRUB as bootmgfw.efi, so the "windows 10" menu entry launches GRUB instead.

console=efifb is fatal. A cmdline I copied from a guide included console=efifb. There is no console device by that name, so the kernel couldn't open /dev/console, init exited, and the boot ended in:

Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000200
Enter fullscreen mode Exit fullscreen mode

The fix was almost embarrassingly small — drop console=efifb, leaving root=LABEL=mobian-root rw acpi=no.

After that:

Debian GNU/Linux 12 lumia950xl tty1
lumia950xl login: root (automatic login)
Linux lumia950xl 5.7.0-lumia-950xl-uefi #1 SMP PREEMPT aarch64
root@lumia950xl:~#
Enter fullscreen mode Exit fullscreen mode

Debian, booting on a Windows phone.

A shell I couldn't type into

The catch arrived right after the win: I had a root shell on the screen and no way to type into it. No physical keyboard, and worse — USB is completely absent. Not just gadget mode; the prebuilt kernel's device tree has no USB node at all, so neither a USB keyboard (host) nor SSH-over-USB (gadget) is possible. No WiFi, no Ethernet either.

So I configured the thing the only way left: offline. Mass Storage Mode exposes the rootfs, so I could mount it on the PC and edit anything — even chroot in via qemu-aarch64-static and apt install packages using the PC's network. A boot-time diagnostic, dumped to a file and read back offline, told me what the hardware actually does:

Synaptics S3708AR  ->  /dev/input/event0   (multitouch, works)
/dev/fb0  1080x1920   (efifb framebuffer, works)
USB controllers: none
Enter fullscreen mode Exit fullscreen mode

Touch works. That's the whole game.

A touch desktop, built offline

No GPU and no DRM/KMS — just the EFI framebuffer — so the desktop is plain fbdev X11: xserver-xorg-video-fbdev, matchbox-window-manager, xterm, and an on-screen keyboard. All of it installed by chroot-ing into the phone's rootfs from the PC, so the device never needed a network to get a working desktop.

The first on-screen keyboard (matchbox-keyboard) rendered fingernail-sized keys on a ~385 DPI panel. I switched to onboard, which came up filling the entire screen and ignoring every size setting I gave it. The cause was a good one: the gsettings command-line tool wasn't installed (libglib2.0-bin), so every gsettings set I ran was silently a no-op and onboard ran on pure defaults. One package later, a properly docked keyboard with finger-sized keys.

A shell over Bluetooth

Driving a tablet entirely through an on-screen keyboard gets old fast, so I wanted a real shell from my PC. With no WiFi and no USB, that left exactly one radio: the QCA6174's Bluetooth half works (it's on UART, independent of the WiFi half), and Bluetooth can carry IP over PAN/BNEP.

The phone side was straightforward — run an auto-accept pairing agent, a NAP server bridged to a pan1 interface, and a small dnsmasq for DHCP. The PC side was a fight:

  • bt-network -c connected and then immediately tore the link down.
  • NetworkManager failed differently — udev renames bnep0 to enxdc21… mid-activation, NM loses track of it, and the link flaps.
  • And my first subnet choice, 172.20.0.0/24, collided with a Docker bridge on the PC, so a "successful" ping was the PC happily talking to itself.

What finally worked was to take NetworkManager out of the loop, connect via bluez's D-Bus interface directly, and statically address whatever the interface had been renamed to, on a subnet Docker wasn't squatting on:

nmcli device set "$MAC" managed no
dbus-send --system --dest=org.bluez "$DEVPATH" org.bluez.Network1.Connect string:nap
ip addr add 10.13.37.2/24 dev enxdc21...   # the renamed bnep iface
ssh root@10.13.37.1
Enter fullscreen mode Exit fullscreen mode
64 bytes from 10.13.37.1: icmp_seq=1 ttl=64 time=52.2 ms
Enter fullscreen mode Exit fullscreen mode

A root shell on the phone, over Bluetooth. No more Mass-Storage shuffle for every change.

Internet over Bluetooth

If the phone reaches the PC over Bluetooth and the PC has internet, the rest is NAT:

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.13.37.0/24 -o wlo1 -j MASQUERADE
Enter fullscreen mode Exit fullscreen mode

Add a default route and DNS on the phone, and apt works on the device, its packets routed out through the PC over Bluetooth. It's the closest thing to "the phone is online" I'd manage — which is the natural place to talk about the part that doesn't work.

The WiFi wall, and why

The phone's WiFi is a QCA6174, the WiFi half of that same combo chip, attached over PCIe. The ath10k_pci driver is built and the firmware is a trivial download, so this should be easy. It is not:

$ ls /sys/bus/pci/devices/
(empty)
Enter fullscreen mode Exit fullscreen mode

No PCI devices at all. Peeling it apart, layer by layer:

  1. The PCIe controller never initializes. Its platform device is unbound and isn't even in the deferred-probe list — the qcom-pcie driver simply doesn't match the qcom,pcie-msm8994 compatible.
  2. It doesn't match because mainline Linux has no MSM8994 PCIe support. The earliest Qualcomm DesignWare PCIe in-tree is msm8996.
  3. The prebuilt device tree also has no regulators (only a dummy), so even the controller's PHY had no power to come up.
  4. The "rampatch/TLV firmware" the forums mention for this chip is the Bluetooth firmware, not WiFi — a red herring I chased for a bit.

The WOA documentation hints at a shortcut: the UEFI firmware-initializes the PCIe root port, so in theory a generic pci-host-ecam-generic node could just enumerate the already-trained bus. But a DesignWare PCIe core needs iATU programming for config-space access; generic ECAM can't drive it, and the WOA notes themselves say ath10k crashes the system that way.

So the only real route was to build a kernel that actually supports the SoC. Down the rabbit hole.

Mainline 6.16 does boot

This is the part that surprised me. Mainline's MSM8994 clock driver does define the PCIe clocks, and the mainline cityman device tree carries the full PMIC regulators the prebuilt one lacked. So I cross-compiled mainline 6.16 with the postmarketOS MSM8994 config and pushed the kernel and device tree to the phone over Bluetooth.

It booted. Eight penguins (all eight cores), the framebuffer up at 1080×1920, the eMMC enumerated, and — for the first time — the PMIC regulators powering on. (A small trap: on 6.16 the eMMC comes up as mmcblk1, not mmcblk0.)

And then it hung. It gets through the regulators s1s7, l1l25, and stops dead. The RPM regulator bring-up blocks deterministically — on these WOA devices the UEFI hands the RPM coprocessor over in a state mainline's regulator/SMD path waits on forever. That's a genuine low-level bring-up bug, not a config I missed.

And even past it, the original wall is still standing: there is no MSM8994 PCIe driver to attach the WiFi to. That's not a setting; it's unwritten SoC support. Two hard, uncertain kernel problems stacked on top of each other, so I called it and reverted to the working 5.7 build.

What works today

  • Boots Debian 12 from the eMMC through the WOA UEFI and GRUB.
  • Display — the 1080×1920 panel via the EFI framebuffer.
  • Touch — the Synaptics digitizer, multitouch.
  • A touch desktop — fbdev X11 with a docked on-screen keyboard, terminal, file manager.
  • Root SSH over Bluetooth, and internet tethered over that same Bluetooth link (so apt works on the device).
  • Bluetooth generally (it's how everything above is driven).

What doesn't — and why

  • Onboard WiFi. PCIe-attached QCA6174, and no kernel — mainline or otherwise — has a working MSM8994 PCIe controller driver. Tethering over Bluetooth is the stand-in.
  • USB. The prebuilt kernel's device tree describes no USB controller, so no host keyboards and no gadget mode. (Mainline, with real regulators, might change this — if it got past the regulator hang.)
  • Cellular, cameras, sensors, audio, battery reporting. Standard for this class of port; none are wired up.
  • Clean reboot/poweroff is unreliable — MSM8994's PSCI is incomplete, so you hold the power button.

The honest framing is "doesn't work right now." Mainline booting at all means the ceiling is higher than this 5.7 build; it's the regulator hang and the missing PCIe driver standing between here and more.

What comes next

If anyone wants to push this further, the frontier is clear and the entry cost is low (it already boots): the msm8994-mainline effort needs the RPM/SMD regulator hang worked out, and then an MSM8994 PCIe controller driver written — the MSM8996 DesignWare path plus the in-tree MSM8994 PCIe clocks is the obvious starting point. With a Bluetooth shell for iteration, the cycle is faster than you'd expect.

The sticky-note version

  • Lumia 950 XL, Windows 11 ARM → Debian 12, touch desktop, on-screen keyboard.
  • You get in via the WOA UEFI's Mass Storage Mode; the phone won't talk to a PC from Windows because it's a USB host.
  • Two boot traps: install GRUB as bootmgfw.efi, and never pass console=efifb.
  • No USB and no WiFi, so the lifeline is SSH over Bluetooth, with internet via NAT on top.
  • WiFi needs an unwritten MSM8994 PCIe driver; mainline 6.16 boots but hangs in regulator init.

From "an abandoned Windows phone" to "a touch Debian tablet you can SSH into over Bluetooth" is a real outcome, even with WiFi unsolved. And finding out mainline boots on it — eight penguins on a 2015 Lumia — was worth the detour on its own.


If you'd like to support this kind of hardware-resurrection writing, you can buy me a coffee. It genuinely helps, and it's appreciated.


Enjoying the content? If you'd like to support my work and keep the ideas flowing, consider buying me a coffee! Your support means the world to me!

Buy Me A Coffee

Top comments (0)