A/B on a single-slot device¶
How to give a device that shipped with one slot a second one, so GlassineOS and the vendor system can live side by side and either can be booted. The partition numbers and sizes for each device are on its device page (for example the Note Air layout).
Verified on: Onyx Boox Note Air (Snapdragon 636, Qualcomm ABL, eMMC).
This rewrites the partition table and wipes the data partition
Have a full backup and a working way into EDL that does not depend on the device booting (see Qualcomm boot chain and EDL) before you start. The How to undo section only works with those in hand.
Context¶
What A/B needs¶
On an A/B device the bootloader picks boot_a or boot_b, and the system finds the
partitions of the same slot (system_a, vendor_a, …). A single-slot device has
boot, system and vendor and a bootloader that never looks for suffixes. Three
things have to change together:
| Part | Single slot | A/B |
|---|---|---|
| Partition table | boot, system, vendor |
boot_a/boot_b, system_a/system_b, vendor_a/vendor_b, with slot attributes |
| Bootloader | boots boot |
picks a slot, tells the kernel which one, boots recovery when asked |
| Each slot's system | mounts partitions by plain name | mounts the partitions of its own slot |
Carve, don't move¶
The safe layout keeps every existing partition where it is:
- Rename
boot,systemandvendorto_a, at the same position. - Shrink the data partition at its end and carve
boot_b,system_bandvendor_bfrom the freed space, each the size of its_apartner, aligned like the stock partitions (1024 sectors on the Note Air). _bpartitions take the type GUID of their_apartner and a new unique GUID.- Leave
recoveryunsuffixed: it then stays reachable from both slots. - The partition entry array usually has to grow (48 → 64 entries on the Note Air). Check that it still fits in the space the table already reserves before the first partition.
Only the partition table and the data partition change; no partition content moves.
Slot attributes¶
Qualcomm's bootloader keeps slot state in the attribute bits of boot_a and boot_b:
| Bits | Meaning |
|---|---|
| 48–49 | priority (0–3) |
| 50 | active |
| 51–53 | retries left (0–7) |
| 54 | booted successfully |
| 55 | unbootable |
The bootloader boots the active slot with the highest priority and does not look at
the unbootable bit when choosing. A slot without the successful bit uses up its retries
and then falls back to the other slot. If nothing on the device marks a boot successful,
pin the slot you want: active, priority 3, successful, 7 retries (0x007F in the top
16 bits).
What the bootloader has to do¶
A Qualcomm ABL already contains the A/B code. Check these in its source before you rely on them:
- It switches to A/B mode only when it finds at least two
boot_*partitions. - It adds
androidboot.slot_suffix=_a(or_b) to the kernel command line. - The system partition and its verity device must follow the slot. The ABL build we started from hard-wired slot A's system partition; slot B then booted with slot A's system.
recoverymust boot on an A/B layout. Stock A/B bootloaders expect recovery insideboot_<slot>and ignore a separaterecoverypartition.
What each slot's system has to do¶
Anything that mounts system or vendor by plain name breaks after the rename. Android
mounts vendor in its first stage from an fstab in the kernel's device tree; adding the
slotselect flag there makes it follow the slot. Such a boot image no longer boots on a
single-slot table, because the bootloader adds no slot suffix there.
A GlassineOS kernel finds its root by partition name and has no such problem. Recovery
images that mount system or vendor need the same change.
Steps¶
1. Back up and save the current table¶
Read every partition and both copies of the partition table (start and end of the disk) to the host. Keep the old table as a file: writing it back is the way out (see below).
2. Build the new table on the host¶
Generate both copies (primary: protective MBR + header + entries; backup: entries +
header at the end of the disk) with a script, and check them with an independent tool
(parted, sgdisk -v) on a disk image of the same size. Check that every partition
except the renamed ones and the data partition is byte-for-byte unchanged.
3. Prove the safety net on the device¶
Before the real write, test on the device what happens if a write is interrupted:
- Damaged primary, valid backup (zero the primary header): the device should boot from the backup, and Linux should repair the primary.
- Valid primary, different backup: the valid primary should win, and Linux should rewrite the backup from it.
On the Note Air both held, so a write interrupted at any point still boots.
4. Prove the system boots with renamed partitions¶
Without touching the data partition: write a table that only renames boot, system
and vendor to _a and adds a small dummy boot_b (just enough for the bootloader to
switch to A/B mode). Slot A must boot normally, including recovery. If it does not,
write the saved table back and fix the bootloader or the fstab first.
5. Clone slot A to slot B¶
From a running system with root (not from EDL: EDL's sector-size problems make
multi-step raw writes unsafe), write the new _b regions at their sector offsets on the
whole-disk device, then compare each one with its source by hash.
Use 512-byte sectors and, with toybox dd, conv=notrunc (without it dd seek= fails
and writes nothing).
6. Write the new table¶
Backup copy first, then the primary, one right after the other. Read both back and compare them with the files. From here the old data partition is gone.
7. Recreate the data partition¶
The data partition now has a new size and its old file system is no longer valid. Format it with the system's own tool (Android's or TWRP's "Format Data"): a file system that fills the whole partition leaves no room for Android's encryption footer, and encryption then fails.
If the device then boots into recovery on every start, a failed first boot left a
recovery request in misc. Clearing that request (zeroing the first 2 KiB of misc)
ends the loop.
8. Test the slots¶
Boot slot A, then switch to slot B and boot it. Then test the fallback: mark the slot under test unbootable but keep it active, with the other slot marked successful; the device should boot the other slot. (A slot that is merely inactive proves nothing, because the bootloader never picks it.) Finish by pinning the slot you want.
How to undo¶
- Before step 6: the partition table is unchanged, so the device boots as before.
The
_bwrites went to the end of the old data partition and may have damaged files stored there: check them, or restore from the backup of step 1. - After step 6: write the saved primary table back (from the running system or over
EDL). The bootloader reads it, and Linux rewrites the backup to match. All
_apartitions are where they were. The data partition's content does not come back: restore it from the backup of step 1. - The device does not boot at all: EDL, then write the saved table and the backed-up partitions.
Verified on¶
| Device | What | Date |
|---|---|---|
| Onyx Boox Note Air | interrupted-write tests (step 3) | 2026-06-01 |
| Onyx Boox Note Air | rename-only table with a dummy boot_b, slot A boots, data kept |
2026-06-02 |
| Onyx Boox Note Air | full A/B table, clone, data partition recreated | 2026-06-02 |
| Onyx Boox Note Air | slot B boots, fallback, pinning | 2026-06-03 |