Процесс программирования устройства с помощью 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-картой).
Но об этом - в следующий раз...