The opening article of the series. We wrote an OS from scratch in Rust and booted it on a production Samsung Galaxy Tab S6 tablet (Snapdragon 855). What follows is not about Rust and not about the tablet. It is about who else lives inside this device, what rights each of them holds, and why our OS is far from the most privileged tenant. The rest of the series is about how we found this out — one discovery at a time.
1. The naive picture
When you write an OS for a microcontroller, the picture is simple: there is a processor, there is memory, there are peripherals. You are the only code that runs. Whatever you did not write does not exist. All privileges are yours, and so is all responsibility.
We brought that picture to the tablet. The bootloader hands control to our image at EL1, and from there on it is our memory, our MMU, our drivers. Or so it seemed.
It took about ten silent reboots of the entire chip — no panic, no message, no interrupt — for the picture to be replaced with an honest one: at least eight executing entities live in this device, seven of them were written by someone else, and almost every one of them can kill us, while we can kill none of them.
2. The tenants
Here they are, from the most privileged down to us. For each: where it runs, whose code it is, what power it holds over us, and how we found out (usually through a reset).
| Tenant | Where | Whose code | What it holds over us | How we found out |
|---|---|---|---|---|
| TrustZone (secure world, EL3) | the same cores, secure mode | Qualcomm (+ Samsung) | Loads and verifies the signatures of the other tenants' firmware (PAS). Puts XPU fences on memory: one byte read past a fence by us — silent reset. Owns the modem registers (QDSP6SS): a read is a bus error. Decides who owns which RAM region (assign_mem). Its own log is encrypted and wiped on reset | resets #1, #2, #9; every memory handover to the modem goes through it |
| Hypervisor (EL2) | the same cores, one level above us | Samsung | Owns the SMMU (qcom,skip-init — the global configuration is his): stage‑2 translation of all DMA masters is his, stage‑1 is left to us. Vetoes writes of S2CR type bypass — they come back as fault. Forwards our smc calls to TrustZone. His memory hyp_mem is no-map: a speculative CPU read into it is a reset | attempting to configure the SMMU for Wi‑Fi; reset #10 (preventively) |
| AOP (Always‑On Processor) | a separate Cortex‑M core inside the SoC | Qualcomm | Controls power and clocks through RPMh: every rail is a vote by name from a command database (cmd-db) that he also stores. The copy of the database in his message RAM is closed: a read stops the processor. Without his load_state modem on the modem does not load; without the qdss clock, Wi‑Fi does not start. Our wrong vote vote(0) for cx.lvl powered down the digital logic of the whole SoC | reset #3, reset #8 |
| S‑Boot (bootloader) | before us; stays in two slots on disk | Samsung | Decides what boots at all: boot or recovery, by the reboot‑reason byte in the PMIC. Picks the device tree by msm-id, enters EL1 "wherever it likes" (our image is PIE). Leaves behind rail votes on behalf of APPS and a framebuffer with a logo, which we hijack as our display. A dying recovery is no longer booted — by itself | the experiment with the recovery reason that led into Download mode |
| Modem (MPSS, Hexagon) | a separate DSP, 160 MiB carve‑out | Qualcomm, 80 MB of signed firmware | Inside — three domains: cellular, GNSS, WLAN. Demands four servers from us (rmtfs, tqftp, pd‑mapper, memshare), shared SMEM memory, GLINK/QRTR/QMI queues. Without EFS it dies in 40 s; on a refusal to open fsg — in 23 ms. Its own radio, its own uplink | resets #4, #5, #6, #7; all of Wi‑Fi stages 3–4 |
| WLAN copy engines | 12 DMA channels in WCSS | execute descriptors written by the modem firmware | DMA into physical memory — stream 0x640 goes through the SMMU without stage‑1 translation. The only fences are the hypervisor's and the XPU's, not ours | SMMU analysis; docs/wifi-threat-model.md |
| Display controller (MDSS) | DMA master, stream 0x800 | hardware + bootloader setup | Scans the framebuffer memory through the same SMMU. We never brought up the display — we replaced the contents of what it was already showing | milestone 0: stripes and colour cycles, from which we guessed the stride and byte order |
| PMIC (PM8150) over SPMI | a separate chip | Qualcomm | Reboot reasons (PON), the byte for the bootloader, and SDAM — the only non‑volatile memory where we can write a word before a dangerous step and read it back after a reset. Our only witness | the silent‑reset methodology |
| Us (EL1) | APPS cores, a ~85 MiB heap between the image and the first reserve | our Rust | We vote for rails (with the AOP's permission), ask TrustZone to load the modem, serve the modem, program stage‑1 of the SMMU (not yet), write the word into SDAM. The second core is our watchdog, which names the step on which the first one hung | — |
Read the table like this: above us there are three (TrustZone, the hypervisor, the AOP); beside us — two with DMA into our memory (the modem via copy engines, and the display); before us — one who decides whether we run at all. All seven are someone else's code.
2a. The map of relations
The same table as a graph. This is the shared diagram of the whole series: each individual article shows its own fragment of this diagram, using the same node names.
How to read it: red is foreign code; green is ours; blue is memory that is contested; dashed means "can, if it wants to" (a reset, a read, DMA outside the window). Every arrow out of our block is a request, a vote, an answer or a translation; every arrow into it is power.
3. What "owning" means
Not every privilege is equal. Let us break it down by what each tenant can do to us — and what we can do to it.
| Read our memory | Write into it | Power off / reset | Decide whether we boot | What we can do | |
|---|---|---|---|---|---|
| TrustZone | all of it | all of it | yes (XPU → reset) | indirectly (verifies signatures) | ask (smc) |
| Hypervisor | all of it | all of it | yes | no | ask (smc goes through him); accept his veto |
| AOP | no | no | yes (power for the whole SoC) | no | vote by his rules |
| S‑Boot | before us | before us | no | yes | leave a reason byte in the PMIC |
| Modem via CE | all of it (bypass) | all of it (bypass) | indirectly (reset via TZ on a refusal) | no | serve it while isolating transparently; lock stage‑1 |
| Modem on its own | its regions + SMEM | the same | no | no | validate everything it wrote |
| Display | framebuffer | no | no | no | draw into the right place |
| PMIC | no | no | on command | keeps the byte for S‑Boot | write the witness word |
Note the asymmetry: the "what we can do" column contains only ask, vote, serve, validate and lock. Not a single "forbid", not a single "verify their code". That is normal for a modern SoC. What is not normal is not knowing it.
4. Three discoveries the series grew out of
4.1 The chip punishes not with an error, but with a reset
On a microcontroller an error is a HardFault with an address in a register. Here an error in front of a TrustZone fence is a reboot from the secure world, indistinguishable from a regular SYSTEM_RESET: the reason registers in the PMIC are clean, the TrustZone log is encrypted, there is no interrupt. The only way to learn on which step we died is to write a word about the step into non‑volatile memory before the step and read it back afterwards. That is how the "witness" in SDAM appeared, and how a dozen resets were turned into a table of "symptom → cause → proof → fix".
4.2 The memory map is a security boundary
One cacheable megabyte in a window we lent to the modem cost a reset: the modem read the empty headers of its file system through our cache, "restored" it, and crashed. A speculative CPU read into hypervisor memory — a reset. Metadata for TrustZone in a heap we later reused — a reset six seconds later, "while idle". Hence the invariants checked by the compiler: everything shared is non‑cacheable, everything foreign is Device, every window is on a 2 MiB boundary.
→ The Megabyte That Killed the Modem
4.3 The radio is not a peripheral but a tenant
The biggest discovery. On the STM32 + ESP32 breadboard, Wi‑Fi was a module behind a UART: 950 lines of driver, a wire, a text protocol. On the tablet "the same thing" took 32,000 lines, because the Wi‑Fi firmware runs on the cellular modem's processor, demands files, disk, memory and IPC from the host, and walks into our RAM by physical addresses. This concerns more than one tablet: every Android on a Snapdragon, laptops with Qualcomm Wi‑Fi under Windows, iPhones with a Qualcomm modem.
→ "The Parasite in the Tablet" — the central article: anatomy, threat model, Android/Windows/Apple/ESP32, what it opens up to intelligence agencies, how to check your own device.
Four narrower articles grew out of it:
- 23 Milliseconds to Lie — why the answer "no such file" crashes the modem while the answer "here it is, empty" does not; why isolation must be transparent: an answer correct in form, empty in substance.
- The Hypervisor Said No — what a veto on bypass looks like, why Android's "bypass" is a translate bank with translation switched off, and what it takes to lock the firmware's DMA into its window.
- The Password Is Ours, the Key Is the Modem's — what a supplicant on the host protects (the network password), what it does not (the traffic: the session key still leaves for the firmware), and why it is like this for everyone.
- Trust the Wire? — the honest state of TLS on small boards, why "trust the wire" turns the radio from an observer into a MITM, and what is needed to close that.
5. What became OS rules
Every discovery left a rule in the code base. Not in a document — in the code, where it cannot be forgotten.
- Read nothing without a request. The first
smcand the first SMEM read happen only when an agent asked. Boot must not hang on a question nobody asked. - Every step that can hang or kill is marked. A call into the secure world, the first read of shared memory, every step of the handshake with the modem — under a mark that is read by the watchdog core or by the next boot from SDAM. The answer is "hung on
scm detect" or "reset onwlfw fw_ready wait" — instead of "died". - The memory map is constants with
constchecks. 2 MiB windows, everything shared non‑cacheable, allno‑map— Device, metadata for TrustZone — in its own window outside the heap. - Everything the other side wrote is untrusted input. The FIFO base, the element offset, the length, the address in QMI, the buffer address in HTT — checked against the window before dereferencing. Parsers are safe Rust: a bug is a panic, not a write past the buffer.
- Transparent isolation. The modem gets an answer to everything it asks — formally correct, essentially empty: a shadow instead of the disk, scratch in RAM instead of files, named regions instead of arbitrary ones,
NOT_SUPPORTEDinstead of silence. - Vote, do not command. Rails and clocks are votes with a floor, not absolute values: our vote is one of several, and the bootloader has already left its own.
- An honest list of "we cannot". A threat model in which everything is closed is not a threat model. The hypervisor, the XPU, the modem firmware, the air and the cellular uplink are not ours; this is written down, not hidden.
6. What is next
- Stage‑1 for stream
0x640— lock the copy engines into their window. The only piece of work that requires a live run with the risk of a reset; mainline Linux on this SoC already walks this path, so the answer is known. - TLS with a pinned root — turn the radio from a MITM into an observer of metadata.
- Fuzzing the parsers of QMI/QRTR/GLINK/HTT on the deterministic fakes that already exist for every server.
- Level votes for rails as a general kernel model (maximum across voters) rather than a floor in a single constant.
7. Reading order
If you read one thing — "The Parasite in the Tablet". If you read everything — in this order:
- This article — the map of tenants.
- A Reset Without Witnesses — the method without which there would be nothing else.
- The Megabyte That Killed the Modem — the memory map as a boundary.
- "The Parasite in the Tablet" — the radio as a tenant.
- 23 Milliseconds to Lie — how the tenant tests the landlord.
- The Hypervisor Said No — who owns the SMMU.
- The Password Is Ours, the Key Is the Modem's — where the WPA boundary lies.
- Trust the Wire? — where the confidentiality boundary lies.
The technical primary sources are docs/wifi-sm8150.md (hardware, memory map, all resets with causes, methodology) and docs/wifi-threat-model.md (threat model by channel, residual capabilities, comparison with Linux).
The answer to the question in the title: the computer belongs to whoever holds the keys, owns the fences and decides what boots. On the tablet that is TrustZone, the hypervisor, the AOP and the bootloader — Qualcomm and Samsung. We are a tenant at EL1 who has learned not to step on the fences, to serve a neighbour with DMA so that it gets nothing, and to honestly write down what we cannot reach. That is less than we wanted. It is more than any OS that does not know about it has.
VPN Chrome extension: what it protects and what it doesn'tA Chrome VPN extension is a proxy for browser traffic only. What it really protects, where it leaks, and when you need a full VPN app instead.
Turnkey smart homes on our own OS: 0xd-os and 0xd-uiWe build turnkey smart homes on components we designed ourselves. At the core is 0xd-os — one operating system that runs identically on a flagship tablet and on a device with 512 KB of memory.
Why security agents need a sandboxAn agent that can 'just run tools' on a security review is a second attacker — unless every call has a grant. AGI Core isolates tool execution; Forge keeps the skill list in UIDE.