Руководство по реализации встроенного ПО обновления компонентов (CFU)
Обновление встроенного ПО компонентов (CFU) — это протокол и процесс для отправки новых образов встроенного ПО, которые будут установлены на целевом устройстве.
Примечание
CFU доступен в Windows 10 версии 2004 (обновление Windows 10 за май 2020 г.) и более поздних версиях.
Отправка CFU в резидентное встроенное ПО — это пары файлов, один файл является частью предложения, а другой файл — частью содержимого. Каждую отправку CFU (каждую пару предложений и содержимого) необходимо создать в автономном режиме перед отправкой во встроенное ПО, реализующее процесс CFU.
В примере исходного кода встроенного ПО в репозитории CFU на GitHub общий не зависящий от реализации общий код для CFU содержится в ComponentFwUpdate.c
. Все остальные файлы — это вспомогательные файлы, которые можно обновить или изменить для уникальной реализации разработчика.
Содержимое
- Части предложения и содержимого
Части предложения и содержимого
Предложение и содержимое составляют пару файлов в схеме CFU.
Часть предложения — это просто 16-байтовый файл, который сопоставляется с FWUPDATE_OFFER_COMMAND структурой, описанной ниже.
Часть содержимого, фактическое встроенное ПО, который требуется обновить, имеет формат, определяемый разработчиком конечного пользователя. Предоставленный пример кода CFU использует файлы SREC для содержимого встроенного ПО.
Предложение представляет собой 16-байтовую последовательность. Эта структура предложения помещается в файл предложения. По сути, это двоичные данные, а не текст, так как предложение содержит битовые поля определенного значения.
Предложение, представленное в файле, сопоставляется со структурой C:
typedef struct
{
struct
{
UINT8 segmentNumber;
UINT8 reserved0 : 6;
UINT8 forceImmediateReset : 1;
UINT8 forceIgnoreVersion : 1;
UINT8 componentId;
UINT8 token;
} componentInfo;
UINT32 version;
UINT32 hwVariantMask;
struct
{
UINT8 protocolRevision : 4;
UINT8 bank : 2;
UINT8 reserved0 : 2;
UINT8 milestone : 3;
UINT8 reserved1 : 5;
UINT16 productId;
} productInfo;
} FWUPDATE_OFFER_COMMAND;
Первый байт предложения — от низкого до высокого адреса— номер сегмента.
<------- 4 bytes -----------> <-- 8 bytes --> <-------- 4 bytes --------->
+================================-=============================================+
| 15:0 7:3 2:0 7:6 5:4 3:0 31:0 31:0 7:0 7:0 7:7 6:6 5:0 7:0 |
| PI | R1 | MS | R0 | BK | PR | VM | VN | TK | CI | FV | FR | R0 | SN |
+================================-=============================================+
С высокого адреса на низкий адрес:
Byte(s) Value
---------------------------------------------------------
15:14 | (PI) Product ID is 2 bytes
13 | (R1) Reserved1 5-bit register
| (MS) Milestone 3-bit register
12 | (R2) Reserved2 2-bit register
| (BK) Bank 2-bit register
| (PR) Protocol Revision 2-bit register
11:8 | (VM) Hardware Variant Mask 32-bit register
7:4 | (VN) Version 32-bit register
3 | (TK) Token 8-bit register
2 | (CI) Component ID 8-bit register
1 | (FV) Force Ignore Version 1-bit register
| (FR) Force Immediate Reset 1-bit register
| (R0) Reserved0 6-bit register
0 | (SN) Segment Number 8-bit register
---------------------------------------------------------
Сведения о регистрации предложения
Код продукта. К этому полю можно применить уникальное значение идентификатора продукта для этого образа CFU.
UINT16 productID;
Веха встроенного ПО, которую представляет содержимое предложения. Вехами могут быть разные версии сборки HW, например сборка EV1, сборка EV2 и т. д. Определение вехи и назначение значения остаются за разработчиком.
UINT8 milestone : 3;
Если встроенное ПО предназначено для определенного банка, 2-битное поле поддерживает четыре банка. Использование банковского регистра включается в формат предложения, так как существуют случаи, когда целевые устройства используют регионы встроенного ПО для банка.
Если это так, а предложение предназначено для обновления используемого банка, встроенное ПО, реализующее CFU в целевом объекте, может отклонить предложение. В противном случае встроенное ПО в целевом объекте, реализующее CFU, может предпринять другие действия, как это требуется.
Если банковские операции образов встроенного ПО не входят в структуру встроенного ПО конечного пользователя, то это поле имеет смысл игнорировать (задайте для любых удобных значений, но значение в поле банка является необязательным и зависит от способа реализации CFU в целевом встроенном ПО).
UINT8 bank : 2;
Используемая версия протокола CFU имеет 4 бита.
UINT8 protocolRevision : 4;
Битовая маска, соответствующая всем уникальным HW, с которым может работать этот образ встроенного ПО. Например, предложение может означать, что оно может работать на verX HW, но не на verY HW. Определение бита и назначение значений оставляются разработчику.
UINT32 hwVariantMask;
Предлагаемая версия встроенного ПО.
UINT32 version;
Байт-маркер для идентификации программного обеспечения конкретного пользователя, выполняющего предложение. Это предназначено для различения драйверов и средств, которые могут пытаться обновить одно и то же работающее встроенное ПО. Например, драйверу обновления CFU может быть назначен маркер 0xA, а средство обновления разработки может быть назначено 0xB. Теперь работающее встроенное ПО может выборочно принимать или игнорировать команды в зависимости от того, какой процесс пытается обновить его.
UINT8 token;
Компонент на устройстве для применения обновления встроенного ПО.
UINT8 componentId;
флаги интерпретации предложения. Если мы хотим, чтобы встроенное ПО на месте игнорировали несоответствие версий (более старые версии поверх более новых), установите для бита принудительное игнорирование версии.
UINT8 forceIgnoreVersion: 1;
Принудительный немедленный сброс утверждается с помощью одного бита. Если этот бит утверждается, программное обеспечение узла ожидает, что встроенное ПО in situ приведет к сбросу устройства. Действия сброса зависят от платформы. Встроенное ПО устройства может принять меры, которые переключают банки, чтобы сделать только что обновленное встроенное ПО активным на месте встроенного ПО. Или нет. Это осталось до реализации встроенного ПО. Обычно ожидается, что при утверждении принудительного немедленного сброса устройство выполнит все необходимые действия, чтобы заставить встроенное ПО сделать новое обновление банка активным встроенным ПО, запущенным на целевом устройстве.
UINT8 forceImmediateReset : 1;
В случае, если контентная часть пары "Предложение и содержимое" включает несколько частей содержимого.
UINT8 segmentNumber;
Обработка предложений
API ProcessCFWUOffer принимает два аргумента:
void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
FWUPDATE_OFFER_RESPONSE* pResponse)
В этом случае предположим, что пользовательское программное обеспечение отправляет байты данных в работающее встроенное ПО, тогда первым сообщением будет сообщение о предложении.
Сообщение о предложении представляет собой 16-байтное сообщение, описанное выше (структура FWUPDATE_OFFER_COMMAND).
Это сообщение о предложении — это данные, используемые запущенным встроенным ПО для ликвидации предложения.
Во время ликвидации предложения запущенное встроенное ПО уведомляет отправителя, заполняя поля в FWUPDATE_OFFER_RESPONSE
структуре.
Интерпретация предложения
Работающее встроенное ПО должно отслеживать свое состояние в процессе CFU. Он может быть готов или ожидает принятия предложения, в середине транзакции CFU или ожидает переключения банков между активным или неактивным встроенным ПО.
Если запущенное встроенное ПО находится в середине транзакции CFU, не принимайте и не обрабатывайте это предложение и уведомляйте узел соответствующим образом.
if (s_currentOffer.updateInProgress)
{
memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));
pResponse->status = FIRMWARE_UPDATE_OFFER_BUSY;
pResponse->rejectReasonCode = FIRMWARE_UPDATE_OFFER_BUSY;
pResponse->token = token;
return;
}
Поле идентификатора компонента предложения можно использовать, чтобы сообщить работающему встроенному ПО о том, что у работающего встроенного ПО запрашивается специальное действие. В примере кода CFU команда специального предложения используется узлом для получения состояния подсистемы CFU — указывает, поддерживает ли работающее программное обеспечение и готово ли принимать предложения CFU.
else if (componentId == CFU_SPECIAL_OFFER_CMD)
{
FWUPDATE_SPECIAL_OFFER_COMMAND* pSpecialCommand =
(FWUPDATE_SPECIAL_OFFER_COMMAND*)pCommand;
if (pSpecialCommand->componentInfo.commandCode == CFU_SPECIAL_OFFER_GET_STATUS)
{
memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));
pResponse->status = FIRMWARE_UPDATE_OFFER_COMMAND_READY;
pResponse->token = token;
return;
}
}
Наконец, проверка выполняется, если ожидается банковский своп. Под переключением банка понимается встроенное ПО, сохраняющее информацию о том, находится ли оно в процессе переключения с запущенного активного приложения на новый скачанный образ.
Способ и место выполнения переключения банка — это конкретная задача реализации встроенного ПО. Протокол и процесс CFU позволяют обмениваться информацией между приложением удаленного пользователя, выполняющим CFU, и запущенным встроенным ПО на месте.
else if (s_bankSwapPending)
{
memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));
pResponse->status = FIRMWARE_UPDATE_OFFER_REJECT;
pResponse->rejectReasonCode = FIRMWARE_UPDATE_OFFER_SWAP_PENDING;
pResponse->token = token;
return;
}
Наконец, если состояние запущенного встроенного ПО не занято, а componentId не является специальной командой и не ожидается обмен банками, то мы можем обработать это предложение.
Обработка предложения включает, помимо прочего, четыре шага, описанные ниже.
Шаг 1. Проверка банка
Проверьте банк работающего приложения в банке в предложении. Они одинаковые или разные?
В этом случае отклоните предложение (мы не хотим перезаписывать работающий или активный образ).
В противном случае продолжайте.
Шаг 2. Проверка hwVariantMask
Работающее встроенное hwVariantMask
ПО проверяет в предложении на наличие HW, на котором оно выполняется. Это позволяет встроенному ПО отклонять предложение, если предложение является недопустимым для целевого объекта. (например, если запущенное встроенное ПО находится в старой сборке HW, а новое предлагаемое встроенное ПО предназначено для более новой сборки HW, то работающее встроенное ПО должно отклонить это предложение)
Если предложение недопустимо, отклоните предложение.
В противном случае продолжайте.
Шаг 3. Проверка версии встроенного ПО
Проверьте, имеет ли предлагаемая версия содержимого встроенного ПО версию старше или новее текущего встроенного ПО приложения.
Пользователи должны решить, как проверка, какое встроенное ПО больше другого, и разрешить ли использовать поле forceIgnoreVersion в предложении. Типичная разработка встроенного ПО позволяет использовать поле forceIgnoreVersion во время разработки продукта и в отладочных версиях встроенного ПО, но запрещено (не позволяя обновлять старое встроенное ПО по на основе нового встроенного ПО) в встроенном ПО продукта или выпуска.
Если эта проверка завершилась сбоем, отклоните предложение.
В противном случае продолжайте.
Шаг 4. Принятие предложения
Предложение хорошее. Примите предложение с ответом, адаптированным к тому, как сообщения и состояние возвращаются встроенным ПО в удаленное приложение пользователя. Так называемый "ответ" — это данные (упакованная структура данных, как показано в файлах демонстрационного заголовка), и эти данные записываются в приложение пользователя соответствующими средствами для устройства.
Обработка содержимого
Обработка содержимого обычно является многоэтапным процессом. Несколько шагов относятся к возможности встроенного ПО принимать образ встроенного ПО частям, также называемые "блоками" данных. Не всегда возможно отправить весь образ сразу во встроенное ПО, поэтому можно ожидать, что реализация протокола CFU и процесс будут принимать содержимое небольшими фрагментами.
В этом обсуждении используется предположение при описании процесса содержимого CFU.
Конечный автомат обработки содержимого включает три состояния.
Состояние обработки первого блока.
Состояние обработки последнего блока.
Состояние обработки любого блока в период между первым и последним.
Структура команды содержимого
Как и предложение, содержимое имеет структуру с полями, которые используются алгоритмами CFU в демонстрации.
typedef struct
{
UINT8 flags;
UINT8 length;
UINT16 sequenceNumber;
UINT32 address;
UINT8 pData[MAX_UINT8];
} FWUPDATE_CONTENT_COMMAND;
Структура команды содержимого проще структуры предложения. Содержимое определяется как последовательность байтов, записываемых в память. В этой части содержимого содержатся поля этой структуры:
UINT8 flags
Указывает, является ли "блок" содержимого первым, последним или другим.UINT8 length
Помечает длинуpData
поля. В демонстрационном коде для CFU ограничение на размерpData
составляет 255 байт. В других реализациях может отличаться максимальный размер "блока".UINT16 sequenceNumber
Помечает счетчик индекса, блок которого отправляется как содержимое.UINT32 address
Смещение адреса блока. В демонстрации CFU этого выпуска реализация содержит предопределенные сведения о физическом адресе каждого региона приложения. Например, реализация встроенного ПО двух банков может иметь App1 начинается с адреса0x9000
и App2 начинается с адреса0xA0000
. Таким образом, в зависимости от того, как был подготовлен образ встроенного ПО (S-Records), адресом в SREC может быть либо физический адрес, либо смещение. В любом случае необходимо общее понимание между подготовкой содержимого и конкретными подпрограммами реализации обработки содержимого CFU, чтобы определить истинный физический адрес, где следует записывать блок в память. Разработчик встроенного ПО может принять рекомендации и проверить допустимые диапазоны адресов для каждого блога о содержимом. Например, код CFU демонстрирует проверка, если, возможно, App1 (предназначен для0x9000
) имеет адреса, которые перекрываются в App2 и т. д.UINT8 pData[MAX_UINT8]
— Это необработанные байты блока образа встроенного ПО. В приложении user-application необходимо поместитьlength
только байты в полный поток байтов блока содержимого.
В структуре содержимого не используются битовые поля, как указано в демонстрации CFU из предоставленного кода.
Первый блок
Первый блок запускает скачивание содержимого встроенного ПО. Встроенное ПО пытается записать блок в энергонезависимую память. Конечно, контентный "блок" содержит сведения о том, где в памяти должен быть записан блок, сколько данных для записи и другие поля.
Каждое целевое устройство componentID отличается и существует несколько методов сохранения данных в памяти. Например, для одного componentId может потребоваться запись во внутреннюю флэш-память, другой componentId может записывать данные во внешнюю вспышку SPI или другой может использовать протокол I2C другого ic для обновления образа. В демонстрации, включенной в этот документ, описывается использование функции с именем ICompFwUpdateBspWrite
, которую каждое уникальное встроенное ПО должно реализовать, зная базовые функции энергонезависимого ввода-вывода памяти целевого объекта, для которого она была разработана.
Любой другой блок, кроме первого или последнего
Процесс принятия новых блоков продолжается, когда приложение пользователя доставляет другой блок, опять же с метаданными в сообщении для адреса, где должен быть записан блок, количества байтов и других полей.
Встроенное ПО на месте будет рассматривать это так же, как сценарий первого блока.
Однако следует отметить, что в любой момент системе не удается записать и сохранить блок в памяти, встроенное ПО in situ отвечает с кодом сбоя.
Последний блок
Последний блок представляет проблему только в том случае, если встроенному ПО на месте необходимо выполнить задачи по проверке образа, который был только что записан в память.
Во-первых, последний блок записывается в память.
Затем необходимо как минимум выполнить проверка CRC между данными, уже записанными в память (от первого до последнего блоков), по сравнению с полем CRC в последнем блоке. Каждому встроенному ПО реализации остается знать, как получить CRC для загруженного образа.
Имейте в виду, что выполнение проверка CRC требует времени. В отличие от обычного потока выполнения CFU для отправки предложения и блока. Отправка последнего блока, если она включает проверка CRC, будет иметь определенную задержку только из-за того, что проверка CRC потенциально изучает большую область памяти. В зависимости от целевого устройства и других факторов это может не беспокоить.
Важно!
CRC проверка входящего изображения является необязательным и может быть закомментирован. Тем не менее, следует применять рекомендации по крайней мере для внедрения этого проверка. Настоятельно рекомендуется, чтобы на этом этапе процесса CFU были предприняты другие действия для обеспечения целостности загруженного образа. Некоторые из этих действий могут включать проверку "подписанной" части образа и (или) проверка цепочки доверия сертификатов или другие рекомендации по обеспечению безопасного образа встроенного ПО. Они остаются за разработчиком встроенного ПО.
Очистка после последнего блока
Теперь, когда записывается последний блок и проверка CRC завершена, встроенное ПО может отреагировать ошибкой, если какая-либо часть проверки не пройдена.
В противном случае ожидается, что процесс CFU в встроенном ПО будет отвечать с состоянием успешного выполнения.
Установлен флажок Принудительный сброс
Флаг принудительного сброса в предложении используется для определения того, должен ли MCU целевого объекта пройти сброс (определяемый пользователем сброс).
Обычно при принудительном сбросе цель состоит в том, чтобы сделать сброс MCU, чтобы банк приложений переключился. Обновление постоянных переменных для указания образа встроенного ПО для загрузки при сбросе остается разработчику встроенного ПО.