Структура и работа SD-карт, записанных утилитой PhoenixCard


  Структура       Прошивка       Эксперименты     RAW-образ  

Процесс программирования устройства с помощью SD-карты

Попытаемся выяснить в деталях: что конкретно происходит, когда записанная в режиме Product карта вставлена в устройство и подано питание ?

Из приведеннной выше структуры прошивочной карты следует, что если прочитать раздел ENV по его адресу в GPT, то мы получим в самом его начале содержимое общего заголовка imagewty-образа.
В котором можно увидеть имена и смещения входящих в состав образа файлов относительно начала раздела ENV. Поэтому абсолютный адрес любого файла прошивки на карте легко определяется как сумма адреса ENV и адреса файла в этом заголовке. Это означает, что с такой прошивочной карты можно без труда извлечь любой нужный файл. Тогда получается, что это может сделать загрузчик, когда такая карта вставлена в устройство и пошел процесс загрузки, т.е. загрузчик стартовал с SD-карты и получил доступ к её содержимому.

Вероятно, механизм программирования ("прошивания") внутренней eMMC-памяти заключается в извлечении нужных файлов из imagewty-образа, записанного на карту, и копирования их в соответствующие разделы на внутренней памяти устройства. И эти операции должен уметь выполнять один из вторичных загрузчиков: eGON, в качестве которого служит boot0, или загрузчик U-Boot. При этом где-то должен быть указан признак того, что нужно выполнять именно процедуру копирования в eMMC, а не обычную загрузку. Какой именно загрузчик используется - пока вопрос открытый (спойлер: это U-Boot).

Анализ структуры начала прошивочной (Product)карты показывает, что оно практически не отличается от аналогичного места на загрузочной (StartUp) карты: есть таблица разделов (GPT), первая часть вторичного загрузчика (boot0), блоб package (дерево устройств DTB и вторичный загрузчик U-Boot) - всё на месте.

Хотя из-за затертого раздела ENV отсутствуют переменные окружения, который должны содержать командные строки, необходимые для "продолжения банкета", но они требуются только для загрузки boot - "ядра", а точнее - Android Boot Image (образа загрузки Android). А учитывая, что раздел boot фактически отсутствует (он есть, но тоже затерт), то дальше и загружать нечего.

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

В случае с загрузочной (StartUp) картой при автоматическом продолжении загрузки U-Boot загружает ядро boot, выполняя содержащуюся в ENV командную строку:
bootcmd=run setargs_mmc boot_normal.

Но из-за отсутствия на своем месте переменных ENV загрузчик U-Boot этого, естественно, сделать не сможет. Поэтому продолжение будет другим, а именно, произойдет переход к программированию (или "прошиванию") внутренней eMMC-памяти данными, содержащимися на карте в файле imagewty-образа.

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

5. Для того, чтобы узнать, какой из загрузчиков занимается программированием, проведем простой эксперимент. Один и тот же imagewty-образ запишем на две разных карточки: первая в режиме StartUp, вторая - Product. И посмотрим в терминале через последовательный порт лог загрузки в обоих вариантах.

Анализ и сравнение логов показывает, что в обоих вариантах записанной карты логи загрузчика Boot0 полностью совпадают. Более того, в обоих вариантах происходит запуск вторичного загрузчика U-Boot, о чём свидетельствуют строки лога:

  • Jump to second Boot.

  • U-Boot 2018.05 (Jan 13 2022 - 16:58:13 +0800) Allwinner Technology

    Из логов видно, что после настроек памяти и контроллера питания дальнейшая работа U-Boot определяется параметром workmode (т.е. режим работы):

  • для варианта StartUp - workmode = 0,storage type = 1

  • для варианта Product - workmode = 17,storage type = 1

    Кроме того, в логе с картой Product обнаруживаются такие строки с чистосердечными признаниями загрузчика U-Boot:

  • current is product mode, it will tune sdly later - текущий режим product

  • Is not Boot mode! - это не режим загрузки Boot

  • start mmc_init_product... - начало инициализации режима product

    6. Таким образом, можно сделать однозначный вывод о том, что процедуру программирования внутренней eMMC-памяти с записанного на карту образа выполняет вторичный загрузчик U-Boot.

    Теперь остался вопрос: каким образом, по каким признакам U-Boot определяет, что нужно работать в режиме Product ? Где он находит это значение workmode = 17?

    Ниже будет показано, что секрет разного поведения загрузчика U-Boot в режимах Product и StartUp заключается всего в двух(!) байтах его заголовка.


     Адрес   Startup   Product
     0x0c      0xc3      0xd4
     0xe0      0x00      0x11
    

    Причем значения этих байтов для режима StartUp совпадают с их значениями в исходном файле U-Boot IMAGEWTY-образа, то есть по умолчанию блоб U-Boot скомпилирован для загрузочного варианта StartUp, а для режима Product эти два байта модифицирует утилита PhoenixCard.

    Точный смысл этих двух байтов пока не ясен, но предварительно можно предположить, что один из них - это флаг или признак режима работы. А второй может оказаться просто частью изменившейся контрольной суммы.

    После долгих, трудных и сложных вычислений удалось выяснить, что Hex-число 0x11 - это десятичное 17.

    0x11 = 17
    Бинго ! Ведь workmode = 17 (см. чуть выше) это и есть режим работы U-Boot в Product.

    А что же со вторым измененным байтом ?
    Проделаем не менее сложные вычисления на суперкомпьютере:

     0xd4 - 0xc3 = 0x11

    Получается, что разница в значении байта по адресу 0x0c совпадает с изменением значения в байте режима работы (адрес = 0xe0), а это означает что в этом байте хранится младшая часть 32-разрядной контрольной суммы, которая вычисляется как простая сумма всех байтов блоба (т.е. это не CRC32). Значит наше предположение о контрольной сумме подтвердилось. Четыре байта в заголовке U-Boot (с 12-го по 15-й) представляют собой контрольную сумму 0x724440с3 или 0x724440d4

    Таким образом, байт по адресу 0xe0 в заголовке блоба U-Boot устанавливает режим его работы: Startup или Product

    И теперь можно утверждать, что установка одного из флажков Startup или Product в интерфейсе утилиты PhoenixCard всего-навсего изменяет два бита(!) в блобе U-Boot, который и делает потом всю работу по программированию ("прошиванию") устройства с карточки.

    Естественно, такой U-Boot должен быть собран под специфику образов IMAGEWTY для Allwinner и уметь с ним разбираться (распаковывать, разбирать super и т.д.). Видимо, по этой причине в OpiZero2 и ТВ-боксах используется исключительно одна и та же сборка U-Boot версии "U-Boot 2018.05 Allwinner Technology" для образов Android.

    Вполне вероятно, что способы прошивания устройств с Allwinner через USB-кабель с помощью утилиты Phoenix Suite (или аналогичных) также основаны на функционале U-Boot. Возможно, что работает такой механизм: Phoenix извлекает из файла imagewty-образа и записывает (по протоколу FEX-FEL) в память устройства блобы вторичного загрузчика Boot0 и U-Boot (U-Boot - в пакете с другими компонентами), который стартует и выполняет всю остальную работу по программированию (так же, как и в варианте с SD-картой).

    Но об этом - в следующий раз...