Heimdall

From postmarketOS Wiki

Heimdall is a reverse-engineered, open-source, version of Samsung's flashing software Odin for Galaxy S devices.

Getting into download mode

Heimdall, and Odin, can flash devices that are in "download mode". It is usually possibly to reboot into download-mode by pressing and holding Power + Volume Down + Home. It is also possible to make a special usb cable (similar to a serial debugging cable), where the ID pin in a micro-usb male header is connected to GND with (usually, check kernel driver for mfd/muic sources to be sure) a 150 kOhm resistor. When attached the device automatically boots into download mode.

Known issues - flashing rootfs fails

Heimdall and Odin are unable to flash postmarketOS's rootfs image. The transfer always fails on the second chunk, which indicates that the bootloader tries to verify the first package and then rejects it. Looking at the format of Samsung's system.img from stock android, and comparing with what is created with postmarketOS, LineageOS or even AOSP reveals that Samsung seem to have made some changes to the sparse image format. Both the file header and each chunk header contains an extra 32bit entry, and exactly what these entries mean is unknown, for some devices it seem to work to just use 0x0 everywhere though.

The sparse image format

The format specification in AOSP specifies three different type of chunks:

  • Raw chunk, containing data as it is (cannot be shrinked)
  • Fill chunk, if image contains regions with a single repeated byte (typically lots of 0x0 or 0xF's), it can be shrinked into just a chunk header and information about which byte to later fill into the entire memory region.
  • Don't care chunk, data region that is not important for some reason (not quite sure what it means, maybe a cache or /tmp region?), bootloader can just skip over it when writing image to memory.

Generating a sparse image with a tool like img2simg generally shrinks an image by using these types of chunks. However, The fill chunk is not supported by Samsung's bootloaders (mentioned here), and hence the img2simg tool cannot be used to create a sparse image suitable for flashing with Heimdall or Odin. img2simg also cannot create don't care chunks, due to it not being smart enough to analyse the compressed file system. This means that even if img2simg would be patched to no longer create fill chunks, then it would only contain raw chunks, and should hence be slightly larger than the original image. Other tools, like mke2fs, are better suited to create images in this case.

To complicate things further, different devices seem to use different versions/formats of these sparse images:

  • In old devices like Galaxy S3 there are no extra entries header entries, the sparse format seem to be identical to the one in AOSP.
  • In the android 5 firmwares for Galaxy S6, the extra entries are there and the file header entry is 0000 0000, while the chunk entry seem to be counting up with the block offset, possibly keeping track of the size of the expanded image.
  • In android 7 firmwares for Galaxy S6, and in all other devices up to, and including, Galaxy S8, the extra entries are there. The file header has 0000 0000 and each chunk has a number that is the constant for most (but not always all) of the chunks.
  • In Galaxy S9 and tab S6 lite (and supposedly other devices), the extra entries are gone, and the format seem to be identical to AOSP's images again.

Possible solutions for postmarketOS

pmbootstrap can generate standard Android sparse images by adding the following deviceinfo option:

deviceinfo_flash_sparse="true"

For device with non-standard Samsung sparse images, sm_sparse_image_tool.py is used to patch the image so that the Odin loader accepts it. Adding this option to the deviceinfo in conjuction with deviceinfo_flash_sparse will do it automatically:

deviceinfo_flash_sparse_samsung_format="1"

This has been tested only with Samsung Galaxy Note 10.1 (2014) at time of writing. The "1" refers to the strategy used by sm-sparse-image-tool to patch the vanilla sparse image; currently this it the only existing strategy and it does the following:

  1. extend the sparse image header and the header of every chunk with some null padding bytes
  2. translate fill chunks as dontcare chunks only if it is a zero fill, otherwise expand the fill chunk into a raw chunk

See also

  • simg_dump.py useful tool for analysing sparse image format (the one in the linked commit can handle Samsung's manipulations)
  • sgs4ext4fs also has a --list option for showing image format
  • libsparse patch to make pre-android 10 libsparse compatible with Samsung's images (changes for android 10 and newer are similar)
  • Original Heimdall repository (no longer maintained)
  • Heimdall fork (maintained)
  • sm_sparse_image_tool.py Script inspired by samsungify.d and simg_dump.py, meant to analyze and patch sparse images to conform to Samsung's sparse image format