Добавляем надежный recovery в Xiaomi Band 10 Pro

от автора

Итак, на руках у меня довольно свежий продукт от компании Xiaomi — Smart Band 10 Pro.
В отличие от Band 9 Pro — апгрейд в основном затронул систему, но и как же без ложки дегтя:
глобальная версия опять со сломанными JerryScript приложениями — зачем делать приложения — а чтобы юзеры не стонали — просто вырежем движок — лол, молодцы, что уж..

SoC BES2700iMP

SoC BES2700iMP

Что же мы имеем, практически тот же BEST1503 — это внутренний идентификатор SoC, внешне это BES2700iMP, какая то его ревизия — не вскрывал еще данную модель,

только внутренний flash 16Mb, против 8 у 9Pro, и порядка 16Mb PSRAM, внешний SPI флеш на 512Mb (256Mb для обычной глобалки)

Устройство системы

В качестве операционной системы, тут опять используется RTOS NuttX OS, очень приятная embedded операционка, здесь неплохой shell, поддержка встроенных и загружаемых приложений.

Ну и классически Xiaomi встраивает 2 движка JerryScript для приложений и LUA для циферблатов, с практически полным сток API Lua 5.4

Именно в этой модели — здесь довольно продвинутый shell, например, позволяет читать в переменную вывод работы команды, что мне не хватало в Redmi Watch 5 с которыми я работал до этого.

system/data nand partitions

system/data nand partitions

внешний SPI Nand flash разбит на следующие сектора, resource — romfs образ системного раздела, nand_data — yaffs RW данные, циферблаты, приложения, данные фитнеса и т.п.

 SoC flash partitions

SoC flash partitions

структура внутреннего флеш выглядит вот так, bl2 — secondary bootloader, ap — непосредственно главное приложение, mode/nv — настройки устройства, ну и в данном случае, это данные драйвера ap — не хватает раздела bl — primary bootloader, но он и не нужен.

Запускается устройство в таком порядке:

1. bl — primary bootloader — делает базовую инициализацию, смотрит на флаги загрузки — прописывает ключи setprop параметры загрузки, передает управление bl2
2. bl2 — secondary bootloader — смотрит причину сброса, выбирает режим загрузки — передает управление в app/recovery/factory
3. в обычном режиме запускает app — основное приложение, либо recovery приложение, либо factory mode.

каждый сегмент прошивки — это отдельная NuttX сборка, в каждой сборке в приложение зашит etc romfs, содержащий скрипты инициализации /etc/init.d/rcS и /etc/init.d/rc.sysinit,
первым выполняется rc.sysinit, затем rcS.

Проблема recovery

Как работает сток механизм рекавери:

любой сбой в приложении перезагружает устройство, которое попадает в bl2 загрузчик, посмотрим как работает его скрипт rcS — именно он отвечает за логику загрузки устройства.

set +eset -xecho "you are running bl2"set resetcause `resetcause`echo $resetcauseif [ "$resetcause" == "cpu_soft_reset(restore)" -o "$resetcause" == "cpu_soft_reset(factory)" ]then  echo "recovery reset system"  # обработка reboot с флагом restore/factory  sh /etc/recovery_reset.sh     # запуск очистки раздела /datafiif [ "$resetcause" == "cpu_soft_reset(bootloader)" ]then  rb -f /dev --skip_prefix vela_ --skip_suffix .bin  rebootfiif [ -e /data/ota.zip ]  # если нашли /data/ota.zip - запускаем установку OTAthen  echo "mount ota.zip to /ota"  mount -t zipfs -o /data/ota.zip /ota  set errcode $?  if [ $errcode -ne 0 ]  then    echo "mount ota.zip failed!!!"    setprop persist.ota_fail 1  else    set ota_in_progress `getprop persist.ota.inprocess`    echo $ota_in_progress    if [ "$ota_in_progress" == "0" ]    then      setprop persist.bl2.ota.trytimes 0    else            # механизм защиты от сбоев при установке OTA обновления       set trytimes `getprop persist.bl2.ota.trytimes`      set trytimes `expr $trytimes + 1`      echo $trytimes      if [ $trytimes -gt 5 ]      then        setprop persist.bl2.ota.trytimes 0        setprop persist.ota.precheck.finished 0        setprop persist.ota.inprocess 0        echo "Tried many times"        rm -r /data/ota_tmp        rm /data/ota.zip        reboot      else        setprop persist.bl2.ota.trytimes $trytimes      fi    fi    if [ "$resetcause" == "cpu_soft_reset(recovery)" -o "$ota_in_progress" == "1" ]    then      avb_verify /ota/vela_ota.bin /etc/key.avb  # проверка цифровой подписи установщика      if [ $? -eq 0 ]      then        echo "Recovery Mode (OTA).."        cp -f /ota/vela_ota.bin /data/vela_ota.bin        boot /data/vela_ota.bin        echo "Boot ota failed!"      else        echo "Verify ota failed!"        setprop persist.ota.precheck.finished 0        setprop persist.ota_fail 1        rm /data/ota.zip        reboot      fi    fi  fifimiwear_recovery_bootset boot_reset `getprop boot_reset_flag`echo $boot_resetif [ "$boot_reset" == "1" ]then    echo "jump boot reset mode"    mount -t romfs /dev/resource /resource    boot /resource/prebuild/vela_recovery.bin  # загрузка recovery ELF приложения    echo "Boot recovery failed!"fiset factory_finished `getprop ro.factory.finished`echo $factory_finishedif [ "$factory_finished" != "1" ]then    echo "Boot factory"    mount -t romfs /dev/resource /resource    boot /resource/prebuild/vela_factory.bin  # загрузка factory ELF приложения    echo "Boot factory failed!"fiecho "save checkpt"umount -f /dataecho "System Mode (AP).."bootecho "Boot ap failed!"

у режима загрузки — resetcause — как видите есть несколько состояний:

  1. normal

  2. bootloader

  3. restore

  4. factory

Это все параметры команды reboot <param>.

Режимы restore/factory запускают скрипт /etc/recovery_reset.sh, который просто форматирует раздел c данными — пользовательские кривые циферблаты или приложения.

set +eecho "umount /data"umount -f /dataecho "force format /data"mount -t yaffs -o forceformat /dev/nand_data /data

Это все хорошо, только это нисколько не спасает от сбойного приложения.

В случае когда падает основное приложение — ap, rcS скрипт попадает в строки 68-75 и запускает в RAM ELF приложение boot /resource/prebuild/vela_recovery.bin

задача этого приложения вывести ошибку на экран и дать пользователю одну единственную кнопку — сбросить данные — выполнить команду — reboot restore

В результате этого действия, скрипт rcS попадает в строки 6-9 и происходит очистка внешней флешки, раздела nand_data — идет очистка данных, дальше идет загрузка сбойного ap — и все повторяется по кругу — получаем циклический ребут.

Улучшаем механизм recovery

Самое интересное, что данный механизм реализован не только во флагманских Watch S3/S4/S5 но и в Xiaomi Smart Band 10, ну и в Mi Band 11 (там прошивка 1в1 как у нашего пациента)

Как же он работает?

А очень просто — системный раздел содержит backup OTA пакет, который не содержит ресурсов, а содержит лишь необходимый минимум для функционирования часов. Слава богу, китайцы научились делать независимые от ресурсов сборки и корректно обрабатывают, как отсутствие текстовых ресурсов i18n, так и графических.

Mi Band 10 - recovery

Mi Band 10 — recovery

благодаря выстроенной системе код рекавери очень простой и краткий.

Стандартный OTA пакет MB10 Pro

Стандартный OTA пакет MB10 Pro

вот полный OTA пакет, vela_resource.bin это системый romfs, который монтируется в /resource при старте системы, в случае bl2 он не нужен и не монтируется.

Recovery ota.zip

Recovery ota.zip

fac_to_user.zip — это recovery OTA пакет с минимальным содержимым для обновления.

Нам остается только упаковать fac_to_user.zip в romfs — vela_resource.bin
и переписать скрипт загрузчика vela_bl2.bin.

Итоговый код загрузчика

set +eset -xecho "you are running bl2"set resetcause `resetcause`echo $resetcauseif [ "$resetcause" == "cpu_soft_reset(restore)" ]  # здесь идет копирование recoverythen                                               # OTA в стандартный путь обновления  echo "recovery restore system"                   # система дальше автоматом  mount -t romfs /dev/resource /resource           # начинает установку  cp /resource/prebuild/fac_to_user.zip /data/ota.zip  sh /etc/recovery_reset.shfiif [ "$resetcause" == "cpu_soft_reset(factory)" ]then  echo "recovery reset system"  sh /etc/recovery_reset.shfiif [ "$resetcause" == "cpu_soft_reset(bootloader)" ]then  rb -f /dev --skip_prefix vela_ --skip_suffix .bin  rebootfiif [ -e /data/ota.zip ]then  echo "mount ota.zip to /ota"  mount -t zipfs -o /data/ota.zip /ota  set errcode $?...

Самое интересное код Secondary bootloader разработчики включили в пакет OTA, т.е. даже стараться не нужно его достать, вот вам пример как не нужно делать прошивки господа.

Обход цифровой подписи и запись OTA настолько банальны, что даже не буду рассказывать, чтобы не поломать чуткое китайское самолюбие.

Всем добра и интересных гаджетов.

ссылка на оригинал статьи https://habr.com/ru/articles/1066280/