Android dynamic partitions

From postmarketOS

Devices running Android 10 and later versions implement the dynamic partitioning scheme. With dynamic partitioning, multiple logical partitions are defined in a single "super" partition. They behave very much like Linux LVM, being flexible and easy to change around. However, dynamic partitioning follows a different format from LVM.

The standard installation process for internal storage would be to flash a root filesystem to a GPT-based partition, by default system. On devices with dynamic partitions, either there isn't a GPT system partition, or in the case of retrofit, the flashing interface prevents flashing to the GPT system partition without command line options like --force.

Devices with dynamic partitions either need to have a different GPT partition flashed (like super or userdata) or a way to understand the dynamic partitions and map them to logical block devices so they can be mounted. PostmarketOS depends on the configuration of the device specific package to find and map the dynamic partitions so there might be no support for dynamic partitions on some devices.

Before Android dynamic partitions, Android_AB_Slots is the main infrastructure of android partition after android 8. Android super partition is one of the newer version of Project Treble requirements while AB Slot is the older version.

Different options of installation

There are a few ways to install the rootfs to a device with dynamic partitions. While some device pages don't document both, the first two methods do not need device-specific modifications.

Flashing to userdata

One location for the rootfs is the userdata partition. As the name implies, this will overwrite the existing data (things like apps, photos, videos, etc.) on your device like a factory reset. If you have any important data on your device and haven't done so already, you should definitely back up important data at this point. It also lets you make use of most of your device's storage.

This has been used by the SHIFT6mq.

Flashing to userdata is documented here.

Flashing to super

Another option is overwriting the super partition, or the partition that stores and defines the dynamic partitions. Depending on whether your device launched with at least Android 10 (non-retrofit) or not (retrofit), you might find your device unusable with Android unless you back up the super partition.

This is used by the OnePlus 8T (oneplus-kebab), OnePlus 9 & 9 Pro (oneplus-lemonade & oneplus-lemonadep), Xiaomi Mi Pad 5 Pro (xiaomi-elish), Xiaomi Poco M2 (xiaomi-shiva), Volla Phone 22 (volla-mimameid). Some devices are configured to automatically do this.

From the oneplus-kebab device page, back up your super partition like so (if your device is retrofit, replace "super" with "system"):

$ adb pull /dev/block/by-name/super super.img

Then, you can either flash to super if your device is non-retrofit:

$ pmbootstrap flasher flash_rootfs --partition super

or force flash if your device is retrofit:

$ pmbootstrap export
$ fastboot flash system --force /tmp/postmarketOS-export/(device codename).img

where (device codename) is the codename of the device.

To go back to Android, you can flash the backed up super partition (again, if your device is retrofit, replace super with system):

$ fastboot flash super super.img

fastbootd

Another method is to flash to the dynamic partitions in fastbootd. The disadvantages of this are that it might be a source of issues because of the extra code needed to support dynamic partitions, and that fastbootd is needed for a flashing interface (which does not support booting even if the bootloader might, and might get erased when flashing).

Dynamic partitions are only supposed to be flashed in fastbootd, as opposed to the bootloader fastboot. To get to fastbootd from the bootloader fastboot, run:

$ fastboot reboot fastboot

Afterwards, you can flash the rootfs as normal.

Preserving fastbootd

Since fastbootd is in userspace, you should have a recovery (custom or Android stock recovery) ready, either on the target device, or on the device responsible for flashing. If your device stores the recovery on the boot partition, then fastbootd will be overwritten when you flash the pmOS kernel. You could try to preserve the boot partition of one of the slots on the device or flash a recovery image every time you want to modify the dynamic partitions.

One way to do this is to keep an Android(-like) installation on one slot, if your device has A/B partitions:

$ fastboot set_active b # Either slot is fine if they both have Android 10+
$ fastboot reboot fastboot
$ fastboot set_active a # Avoid overwriting fastbootd by switching to the inactive slot
$ pmbootstrap flasher flash_rootfs
$ pmbootstrap flasher flash_kernel

Adding support for dynamic partitions to your port

There are a few changes you need to make to the device package:

  • add make-dynpart-mappings to the dependencies in the APKBUILD
  • set deviceinfo_super_partitions in deviceinfo to a certain value
  • regenerate the checksums
  • rebuild the device package and everything related to updating it

There is no single correct value for deviceinfo_super_partitions, hence the need for it as a configuration value. Below is a guide to determine the appropriate value for deviceinfo_super_partitions.

Retrofit or single super partition?

The super partitions are different for devices that launched before Android 10, called retrofit devices, and devices launching with Android 10 or higher.

Retrofit Devices

For retrofit devices, old A/B partitions are re-purposed into a single "virtual" super partition, usually the system_a, system_b, vendor_a, vendor_p and product partitions, but it can vary between devices. Which partitions are used is specified in the BOARD_SUPER_PARTITION_BLOCK_DEVICES variable in the devices BoardConfig*.mk but you only need to specify the partitions with the metadata (see below).

Special cases

On retrofit devices, the device manufacturer has the option to specify different super partitions. If your device has a https://github.com/LineageOS/android_device_* repository, you can look for the value of BOARD_SUPER_PARTITION_METADATA_DEVICE in the board configuration (i.e. BoardConfig*.mk). If it is unspecified, then it is probably "system", otherwise you can use the value you found. It can also be found on the kernel command line, as the argument to the option "androidboot.super_partition". You may need to add both slot suffixes and find each physical partition (e.g. "system_a" and "system_b").

Devices launching with Android 10+

Devices launching with Android 10 (or later) have a single super partition called "super". If your device has A/B seamless update, or you are not sure, then you should specify the same super partition twice.

Finding the device nodes

On Android, there are symlinks to the appropriate device nodes in /dev/block/by-name/. You can use the adb root shell to find the device node names of your super partitions:

$ adb root
$ adb shell
(device hostname):/# readlink /dev/block/by-name/super | basename
mmcblk0p127
(device hostname):/# readlink /dev/block/by-name/system_a | basename
mmcblk0p68
(device hostname):/# readlink /dev/block/by-name/system_b | basename
mmcblk0p69
(device hostname):/# exit

The same information is available in postmarketOS under /dev/disk/by-partlabel/.

You can then use the output to determine the appropriate value of deviceinfo_super_partitions by prepending /dev/ to each partition:

# for a retrofit device
deviceinfo_super_partitions="/dev/mmcblk0p68 /dev/mmcblk0p69"
# for a device with the single super partition
deviceinfo_super_partitions="/dev/mmcblk0p127 /dev/mmcblk0p127"

See also