Как создать два рекавери на OragePi Zero2



1. Эксперимент по замене ядра на TWRP


В любом устройстве на базе Android имеется рекавери (Recоvery), которое представляет собой простейшую операционную систему, предназначенную для сервисных и отладочных целей. Очень часто стандартное рекавери, имеющее достаточно примитивный интерфейс и очень ограниченный функционал, заменяют на так называемое "кастомное рекавери", обычно это TWRP (по имени проекта). Такая замена выполняется путем записи образа TWRP в раздел Recovery загрузочного носителя устройства, при этом стандартный рекавери, естественно, уничтожается.

Возникает вопрос: а нельзя ли сделать так, чтобы в устройстве было два рекавери: стандартное и TWRP ? То есть изменить логику: OR ( ИЛИ ) на AND ( И ). Если это осуществимо, то появляется возможность повысить надежность и живучесть устройства, особенно в тех случаях, когда работоспособность TWRP не гарантирована и требует проверки на реальном устройстве.

Для ответа на этот вопрос был проведен эксперимент на устройстве OrangePi Zero 2, являющемся прототипом многих ТВ-боксов, использующих SoC Allwinner H616 (поэтому описанные ниже результаты вполне применимы к любому аналогичному ТВ-боксу).

После распаковки утилитой imgrepacker.exe стокового образа Android (версии V1.1) образуется папка с именем IMG.DUMP, содержащая набор файлов, участвующих в записи прошивки на карту памяти.

Теперь удаляем в этой папке файл образа ядра boot.fex, а вместо него записываем образ кастомного рекавери TWRP. Это файла twrp.img, но переименованный в boot.fex. Остальные файлы остаются без изменения.

После этого делаем утилитой imgrepacker.exe обратную упаковку этой папки в файл прошивочного образа с именем test.img. Затем записываем утилитой PhoenixCard этот файл на SD-карту в режиме startup и включаем питание устройства со вставленной картой. В результате наблюдаем на экране монитора загрузку операционной системы, которая представляет собой не что иное, как TWRP. Это означает, что простой заменой одного файла в образе прошивки достигается загрузка TWRP в качестве операционной системы.

Но обычное при этом стандартное рекавери тоже осталось на своем месте, т.е. в разделе Recovery. Чтобы в этом убедиться, достаточно выполнить команду его загрузки, находясь в интерфейсе TWRP: "Перезагрузка -> Рекавери". Для управления используется мышка.

После произошедшей перезагрузки на экране монитора наблюдается интерфейс обычного стандартного рекавери. Здесь мышкой управлять сложно, удобнее стрелками физической клавиатуры (или ИК-пульта, если к устройству подключен ИК-приемник). Теперь для обратного перехода в TWRP достаточно в этом рекавери выполнить команду: "Reboot system now".

Проведенный эксперимент показал, что загрузчик U-Boot может в автоматическом режиме (без вмешательства в его работу по UART) выполнять загрузку системы из двух разделов:

  • boot
  • recovery

    При этом он не проверяет содержимое раздела boot (или recovery), а просто загржает его в память и стартует.

    Для того, чтобы убедиться в этом, проверим процесс загрузки в ручном режиме. Если в момент старта U-Bоот нажать пробел, то мы попадаем в командный интерфейс U-Bоот. В котором можно выполнять загрузку системы вручную командами его интерфейса.

    Как их узнать ? Если посмотреть содержимое массива переменных окружения Env, то мы увидим все нужные нам команды загрузки:


    Hit any key to stop autoboot: 0

    • => printenv boot_fastboot=fastboot
    • boot_normal=sunxi_flash read 45000000 boot;bootm 45000000
    • boot_recovery=sunxi_flash read 45000000 recovery;bootm 45000000
    • bootcmd=run setargs_mmc boot_normal


    Здесь видно, что сачала содержимое раздела boot (или recavery) загружается в оперативную память по адресу 0x45000000 командой sunxi_flash read

    sunxi_flash read 45000000 boot

    А затем выполняется команда bootm - загрузка образа из памяти.

    bootm 45000000

    Таким образом, находясь в командном интерфейсе U-Boot, можно вручную загрузить образ из 2-х разделов:

  • 1) boot ( в нашем случае это TWRP)
  • 2) recovery

    Более того, можно предполжить, что если добавить таблицу разделов GPT новый раздел (например, с именем twrp), и записать в него (любым способом) содержимое файла twrp.img, то мы получим образ с тремя загрузочными разделами, позволяющий по выбору загружать одну из трех систем:

    • 1) ядро системы
    • 2) стандартный рекавери
    • 3) рекавери TWRP

    Для проверки этой идеи попробуем создать на основе стандартной версии прошивки Андроид V1.1 модифицированный образ прошивки, в который добавлен файл рекавери TWRP.fex, а также сделаны необходимые изменения для сохранения возможности записи этого образа c использованием утилиты PhoenixCard.


    2. Образ с тремя загрузочными разделами


    Для создания такого образа этого требуется модифицировать четыре файла в исходном образе прошивки:

  • sys_partition.fex - текстовый файл конфигурации разделов
  • sunxi_gpt.fex - бинарный образ таблицы разделов GPT
  • sunxi_mbr.fex - вспомогательный бинарный файл со структурой разделов
  • dlinfo.fex - вспомогательный бинарный файл с информацией о загружаемых файлах

    Отметим, что на самом деле, на карту памяти (в её начало) записывается лишь один GPT, а остальные файлы нужны только для нормальной работы утилиты-прошивальщика PhoenixCard. При этом исходный текстовый файл sys_partition.fex, хотя и присутствует в пакете образа прошивки img, но реально он этой утилитой не используется, а нужен для создания из него трех остальных бинарных файлов с помощью специального софта под Linux или Windows.

    В нашем случае для этих целей пришлось написать специальный конвертор, поскольку найденные утилиты (типа update_mbr) работать не захотели из-за какой-то несовместимости, проще было написать свой софт, чем с ними разбираться.

    Измененный текстовый файл конфигурации разделов выглядит так: sys_partition.fex

    Этот файл представляет собой список создаваемых разделов (имя и размер), с указанием имён записываемых в них файлов.

    Все четыре измененных файла лежат в архиве: Измененные файлы

    Итак, все подготовительные операции проделаны, нужные файлы скопированы в папку test.img.dump (с заменой прежних версий), а также в эту папку добавлен файл кастомного рекавери twrp.fex.

    Теперь утилитой imgrepacker упаковываем эту папку и получаем новый обрах test.img. Осталось только записать его на карту памяти утилитой PhoenixCard.

    Утилита восприняла этот образ как родной, извлекла из него нужную для себя информацию, сохранив её во временных файлах, и начала запись на SD-картe. Процесс начался весело, успешно записались GPT и несколько основных разделов (и даже super), но потом что-то пошло не так, утилита споткнулась на разделе recovery, выдала ошибку 2204 и остановилась.



    Разбираться с причинами появления ошибки в PhoenixCard мне не захотелось. Сложность в том, что это не обычная утилита, имеющая открытый исходный текст, а некий винегрет из программ (написанных на разных языках и в разное время ), а также служебных и вспомогательных файлов. Да, что-то ей не понравилось в этом собранном образе.

    Но на карточке нормально создана таблица разделов GPT и в основные разделы нормально записана информация, достаточная для первой загрузки и проверки работоспособности (пока без рекавери). Размер записанной карточки - 8 Gb . Для проверки можно посмотреть на неё, например, под Windows утилитой diskinternals.

    Видим, что d nf,kbwt все разделы на месте, в том числе добавленный раздел с именем twrp (хотя, он пока пустой, естественно, из-за ошибки, обраруженной прошивальщиком).



    Поэтому можно проверять эту карту непосредственно на устройстве, загрузчик должен работать. Так оно и есть: вставленная в устройство карточка нормально загружается в U-Boot и дальше - в основную систему.

    Для проверки таблицы разделов непосредственно на устройстве выполним консольную команду:

    console:/# sgdisk --print /dev/block/mmcblk0


    ********************
    Disk /dev/block/mmcblk0: 15523840 sectors, 7.4 GiB
    Logical sector size: 512 bytes
    Disk identifier (GUID): AB6F3888-569A-4926-9668-80941DCB40BC
    Partition table holds up to 20 entries
    First usable sector is 73728, last usable sector is 15523838
    Partitions will be aligned on 1024-sector boundaries
    Total free space is 33 sectors (16.5 KiB)
    
    Number  Start       End      Size       Code  Name
          (sector)    (sector)
       1      73728    139263   32.0 MiB    0700  bootloader
       2     139264    172031   16.0 MiB    0700  env
       3     172032    237567   32.0 MiB    0700  boot
       4     237568   3383295   1.5 GiB     0700  super
       5    3383296   3416063   16.0 MiB    0700  misc
       6    3416064   3481599   32.0 MiB    0700  recovery
       7    3481600   3547135   32.0 MiB    0700  twrp
       8    3547136   4857855   640.0 MiB   0700  cache
       9    4857856   4890623   16.0 MiB    0700  vbmeta
      10    4890624   4923391   16.0 MiB    0700  vbmeta_system
      11    4923392   4956159   16.0 MiB    0700  vbmeta_vendor
      12    4956160   4988927   16.0 MiB    0700  metadata
      13    4988928   5021695   16.0 MiB    0700  private
      14    5021696   5022719   512.0 KiB   0700  frp
      15    5022720   5054463   15.5 MiB    0700  empty
      16    5054464   5087231   16.0 MiB    0700  media_data
      17    5087232   5119999   16.0 MiB    0700  Reserve0
      18    5120000  15523805   5.0 GiB     0700  UDISK
    

    Примечание. При этом sgdisk почему-то грозно ругается на ошибку контрольной суммы CRC32.


    Warning! Error 2 reading partition table for CRC check!
    Warning! One or more CRCs don't match. You should repair the disk!

    Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk verification and recovery are STRONGLY recommended.

    Однако на стоковой прошивке (т.е. с оригинальным GPT) sgdisk выдает аналогичные предупреждения, что свидетельствует о его некоей несовместимости с данным GPT, не влияющей на нормальное использование такой таблицы разделов. Возможно, на момент раписания утилиты sgdisk формат GPT как-то отличался. В нём есть некоторая странность с альтернативным (резервным) заголовком GPT

    Дальше можно без проблем записать непосредственно на работающем устройстве два образа рекавери в соответствующие разделы с помощью консольных команд dd или с помощью fasboot.

    Для этого скопируем на ПК два файла recovery.fex и twrp.fex на USB-флешку и вставим её в наше устройство. Теперь флешка - это диск /mnt/media_rw/0000-006F/ (естественно, для другой флешки имя будет другим).

    Выполняем две консольные команды :

    dd if=/mnt/media_rw/0000-006F/recovery.fex of=/dev/block/mmcblk0p6 bs=512 count=65536

    dd if=/mnt/media_rw/0000-006F/twrp.fex of=/dev/block/mmcblk0p7 bs=512 count=65536

    Осталось только запустить и проверить каждое из двух рекавери. Входим в U-Boot, для этого в момент включения или перезагрузки устройства тормозим автозагрузку нажатием пробела на клавиатуре ПК, к которому подключено устройство через UART и запущен MobaXterm.

    Загрузка стандартного рекавери:

    run setargs_mmc; sunxi_flash read 45000000 recovery;bootm 45000000

    Загрузка рекавери TWRP:

    run setargs_mmc;sunxi_flash read 45000000 twrp;bootm 45000000

    Здесь три команды:

  • 1) run setargs_mmc - устанавливаются значения переменных, прописанных в строке setargs_mmc массива ENV
  • 2) sunxi_flash read 45000000 twrp - содержимое раздела twrp загружается в ОЗУ по адресу 0x45000000
  • 3) bootm 45000000 - старт образа, загруженного по адресу 0x45000000

    Подробно о команде смотрите: bootm .

    Кроме того, можно записать команду загрузки twrp в переменные окружения (слеш перед ';' обязателен !) :

    env set boot_twrp sunxi_flash read 45000000 twrp\;bootm 45000000

    env save

    В этом случае команда загрузки twrp становится ещё короче:

    run setargs_mmc;run boot_twrp

    Заметим, что эти приведенные выше команды демонстрируют возможности управления загрузкой с помощью простого редактирования ENV.

    Таким образом, для выбора и запуска нужного рекавери достаточно просто указать имя раздела, из которого требуется загрузить нужную систему. При этом имя раздела (и файла, который в него загружен) может быть любым (например, 'kakayachren'). Главное, чтобы этот раздел был прописан в таблице разделов GPT.

    Более того, если в такой раздел (например, с именем twrp) записать файл альтернативного ядра boot.fex (например, пропатченного магиском), то в устройстве появляется возможность выбора одного из двух (или трех) ядер boot при загрузке системы.

    dd if=/mnt/media_rw/0000-006F/boot.fex of=/dev/block/mmcblk0p7 bs=512 count=65536

    Теперь по команде

    run setargs_mmc;run boot_twrp

    загружается не twrp, а другое (альтернативное) ядро системы

    Для сравнения приведем два лога:

    1. Загрузка ядра из раздела boot (здесь лежит boot от стоковой прошивки V1.1):

    => boot [120.640]Starting kernel ... [120.642]mmc 2 not find, so not exit [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 4.9.170 (test@test) (gcc version 5.3.1 20160412 (Linaro GCC 5.3-2016.05) ) #61 SMP PREEMPT Wed Aug 25 17:06:18 CST 2021 [ 0.000000] Boot CPU: AArch64 Processor [410fd034] [ 0.000000] bootconsole [earlycon0] enabled console:/ $
    2. Загрузка ядра из раздела twrp (в котором лежит другой boot - от Lite образа):

    => run setargs_mmc;run boot_twrp [65.675]Starting kernel ... [65.677]mmc 2 not find, so not exit [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 4.9.170 (alex@alex-VirtualBox) (gcc version 5.3.1 20160412 (Linaro GCC 5.3-2016.05) ) #40 SMP PREEMPT Sat Jan 29 11:31:35 MSK 2022 [ 0.000000] Boot CPU: AArch64 Processor [410fd034] [ 0.000000] bootconsole [earlycon0] enabled console:/ $

    Cледует уточнить, что речь не идет о практической пользе или преимуществах установки на устройство одновременно двух рекавери (или двух ядер). Описанный выше эксперимент проведен лишь с целью понимания механизма загрузки и использования образов ядра и/или рекавери.

    Поскольку в многочисленных примерах и рекомендациях кастомное рекавери обычно ставится вместо стандартного рекавери (и в этом есть смысл из-за физических кнопок, его запускающих), что может создать не очень точное представление об особой роли этого раздела.


    На самом деле, можно пойти и дальше. Если добавить в таблицу разделов GPT несколько разделов размером 32 Мбайт с именами, например alpha, beta, gamma (или discC, diskD, discE) и записать в них командой dd разные образы ядра ( boot1.img, boot2.img, boot3.img), то появляется возможность выбора загружаемой системы с помощью простого редактирования командной строки в массиве переменных окружения ENV.

    Это можно делать двумя способами:

  • а) отредактировать текст ENV, конвертировать его в двоичный файл и записать через dd в раздел ENV
  • б) войти в интерфейс U-Boot и изменить командой setenv одну строку в ENV, например:

    bootcmd=run setargs_mmc boot_normal

    заменить на

    bootcmd=run setargs_mmc boot_alpha,

    предварительно прописав и сохранив в ENV строки

    boot_alpha=sunxi_flash read 45000000 alpha;bootm 45000000 boot_beta=sunxi_flash read 45000000 beta;bootm 45000000 boot_gamma=sunxi_flash read 45000000 gamma;bootm 45000000

    Таким образом, в дополнение к основному варианту с ядром, хранящимся в разделе boot, получим ещё три альтернативных варианта загрузки системы. Вероятно, это можно использовать, например, при отладке свежеиспеченного ядра.

    Не исключено, что этот вариант тоже пройдет практическую проверку в недалеком будущем....