Итак, на руках у меня довольно свежий продукт от компании 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 +e
set -x
echo "you are running bl2"
set resetcause `resetcause`
echo $resetcause
if [ "$resetcause" == "cpu_soft_reset(restore)" -o "$resetcause" == "cpu_soft_reset(factory)" ]
then
  echo "recovery reset system"  # обработка reboot с флагом restore/factory
  sh /etc/recovery_reset.sh     # запуск очистки раздела /data
fi
if [ "$resetcause" == "cpu_soft_reset(bootloader)" ]
then
  rb -f /dev --skip_prefix vela_ --skip_suffix .bin
  reboot
fi
if [ -e /data/ota.zip ]  # если нашли /data/ota.zip - запускаем установку OTA
then
  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
  fi
fi
miwear_recovery_boot
set boot_reset `getprop boot_reset_flag`
echo $boot_reset
if [ "$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!"
fi
set factory_finished `getprop ro.factory.finished`
echo $factory_finished
if [ "$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!"
fi
echo "save checkpt"
umount -f /data
echo "System Mode (AP).."
boot
echo "Boot ap failed!"

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

  1. normal

  2. bootloader

  3. restore

  4. factory

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

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

set +e

echo "umount /data"
umount -f /data
echo "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 +e
set -x
echo "you are running bl2"
set resetcause `resetcause`
echo $resetcause
if [ "$resetcause" == "cpu_soft_reset(restore)" ]  # здесь идет копирование recovery
then                                               # 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.sh
fi
if [ "$resetcause" == "cpu_soft_reset(factory)" ]
then
  echo "recovery reset system"
  sh /etc/recovery_reset.sh
fi
if [ "$resetcause" == "cpu_soft_reset(bootloader)" ]
then
  rb -f /dev --skip_prefix vela_ --skip_suffix .bin
  reboot
fi
if [ -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 настолько банальны, что даже не буду рассказывать, чтобы не поломать чуткое китайское самолюбие.

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

Комментарии (0)