Как стать автором
Обновить

23 Атрибута Хорошего Загрузчика

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров8.4K

Поговорим о загрузчиках для микроконтроллерных проектов. Допустим у вас есть фитнес браслет, электро-самокат, робот пылесос, беспроводные наушники, или электронная зубная щетка и Вы хотите обновить прошивку загрузчиком. Какое же поведение должно быть у хорошего загрузчика?

Сначала определимся с терминологией:

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

--боевая flash - диапазон flash памяти в котором исполняется приложение микроконтроллера.

--временная flash - диапазон Flash памяти (on-chip или off-chip) в которую складируется новая прошивка

--прошивка (firmware) - содержимое энергонезависимой памяти электронного устройства с микроконтроллером. В прошивке всегда есть код, а иногда ещё образ файловой системы NVRAM, конфиги процессора. Монолитная прошивка может содержать еще и загрузчик.

--NVRAM - энергонезависимая память с произвольным доступом. По сути Key-Val-Map(ка). В ней могут храниться любые бинарные данные, ассоциированные со своим ID(шником).

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

--таблица векторов прерываний - это таблица адресов функций обработчиков прерываний. Первая колонка это номер прерывания, вторая колонка абсолютный адрес обработчика прерываний в on-chip NOR-Flash памяти. Обычно в этой таблице сотни записей. У каждого микроконтроллера она своя. В бинарном файле перечислена вторая колонка как массив uint32_t: индекс массива-номер прерывания, значение - адрес ISR.

--connectivity - всё что связано с интерфейсами и протоколами.

Достоинства загрузчика

1--Можно обновлять прошивку без программатора (обновление "в полях").

Среди программистов-микроконтроллеров есть даже мем что

Самая лучшая функция прошивки это возможность обновления прошивки.

2--Можно обновлять прошивку по любому интерфейсу: UART, BlueTooth, WiFi, LTE, CAN и прочее.

3--Когда есть загрузчик, то можно делать DevOps. Автоматически обновлять прошивки после сборки из репозитория.

Недостатки загрузчика

2--Надо выделить под загрузчик часть Flash памяти. Это порядка нескольких десятков килобайт ROM.

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

Какие программные компоненты обычно должны быть в загрузчике?

Программный компонент

Необходимость

1

Драйвер Flash памяти

обязательно

2

Драйвер UART

обязательно

3

NVRAM

обязательно

4

CRC

обязательно

5

CLI

желательно

6

Драйвер внешней Flash памяти

зависит от платформы

7

Драйвер SD карты

зависит от платформы

8

драйвер SPI-flash

зависит от платформы

9

поддержка Fat-FS

зависит от платформы

Далее представлены атрибуты хорошего загрузчика. Эти правила написаны кровью! Можете просто сразу вставлять их в техническое задание на разработку встроенного программного обеспечения.

1) Саму физическую загрузку прошивки должен осуществлять не загрузчик, а как не парадоксально само приложение. Дело в том, что в приложении больше памяти и больше драйверов для разнообразных интерфейсов. Поэтому ничего не мешает 10% прошивки получить по WiFi, 20% по BlueTooth, 30% по RS485 еще 30% по CAN. Затем принять конфиги из модулированного звука через аудиокодек, а настройки NVRAM получить от сканера QR кодов в драйвере видеокамеры. У приложений обычно и так есть поддержка очень богатого connectivity. Если бы вы пытались засунуть поддержку всех возможных интерфейсов и протоколов в загрузчик, то загрузчик был бы больше самого приложения! Поэтому закачкой данных должно заниматься именно приложение. Приложение как губка впитывает прошивку со всех сторон.

2) Загрузчик должен быть компактным в плане требуемого ROM. Если загрузчик маленький, то будет больше места для приложения и его бизнес логики, которая и создает основную ценность продукта.

3) Загрузчик должен быть однопоточной NoRTOS прошивкой. Так можно уменьшить *.bin(арь) и упростить код загрузчика. По факту загрузчику на UART многозадачность нужна как собаке бензобак. Если код простой, то и ломаться там будет не чему и техподдержка и отладка проще.

4) Загрузчик перед записью должен проверять контрольную сумму приложения. Если CRC32 не cовпадает то такую прошивку лучше не прописывать. Правильная CRC это всего лишь гарантия, что прошивка как файл передалась корректно. Это не защита от чужеродного firmware.

5) Загрузчик должен уметь прошивать как минимум по UART. Когда всё сломается пере прошивка по UART окажется единственным спасением. Нет проводного интерфейса проще UART. В UART нечему ломаться. UART требует меньше всего NOR-Flash(а). Плюс для UART куча дешевых переходников USB-UART и бесплатного софта для serial портов типа Putty и TeraTerm.

6) Загрузчик должен проверять, что первое слово прошивки по нулевому адресу это реальный для данного микроконтроллера адрес RAM памяти. Дело в том, что в Cortex-M это указатель Stack Pointer прошивки. Если это не так, то эту прошивку загружать нельзя. Она всё равно зависнет тот час же.

7) Загрузчик должен проверять, что в бинарном файле прошивки действительные (валидные) адреса таблицы векторов прерывания. В Cortex-M это адрес +0x00000004 от начала прошивки в начале бинарного файла. Надо проверить, что там валидный ResetHandler. Что его адрес принадлежит Flash памяти.

8) В загрузчике должна быть NVRAM. Это позволит передавать команды от приложения к загрузчику. Или сервер может прописать в NVRAM правильную контрольную сумму для того чтобы загрузчик мог рассчитать CRC и сверить с тем что прописал сервер в NVRAM. В случае несовпадения отказаться загружать подозрительную прошивку. Вот минимальный список переменных для управления загрузчиком

Переменная NVRAM

размер, [байт]

1

Начальный адрес по которому следует прописывать прошивку

4

2

Ожидаемая контрольная сумма прошивки

4

3

Команда загрузчику. Запустить прошивку, остаться в загрузчике, запустить без проверки CRC

1

4

Длина прошивки в байтах

4

5

Счётчик числа запусков загрузчика (BootCnt)

1

6

Ключ шифрования

256

9) Перед прыжком в приложение загрузчик должен отключить все прерывания. Иначе программа приложения зависнет, если в ней сработает прерывание для которого в приложении нет функции обработчика.

10) отключить системный таймер, так как в приложении может быть RTOS. Если сработает прерывания по системному таймеру до инициализации планировщика, то приложение с RTOS зависнет.

11) Если это Cortex-M ядро, то перед прыжком в приложение надо указать ядру, где теперь будет новая таблица адресов обработчиков прерывания

SCB->VTOR = app_start_address;

12) Загрузчик может переназначить верхушку стека. Хотя это и так cделает StartUp код приложения до запуска main().

 __set_MSP(stack_top);

13) У загрузчика должна быть UART-CLI. Это позволит подключиться к консоли загрузчика и проверить диагностику, прописывать NVRAM. Протокол CLI - текстовый. Его понимает и человек и утилита. Через CLI можно даже прошивку обновить.

14) Если загрузчику не удалось записать прошивку из энергонезависимого Flash хранилища (SD-карта, off-chip SPI-flash или on-chip flash), то загрузчик должен написать про это красный текст в UART-CLI и перейти в режим ожидания прошивки по UART.

15) Загрузчик должен как-то проверить, что прошивка, которая поступила принадлежит именно нужному аккредитованному вендору, а не злоумышленнику. Поэтому в прошивку надо добавить специальную константу по константному заданному адресу, чтобы загрузчик мог найти эту константу и в случае её совпадения с нужным значением прописать эту прошивку в боевую flash. Или можно оформить эту магическую константу по произвольному адресу просто добавив перед ней известную преамбулу. Тогда загрузчик может просканировать всю память приложения и найти константу по преамбуле.

16)Прошивка должна приходить в зашифрованном виде. В этом же зашифрованном виде она должна храниться в off-chip flash (SPI или SD). Прошивка должна расшифровываться по кусочкам прямо в загрузчике и после расшифровки записываться в боевую flash. Ключ шифрования брать из NVRAM.

17) Для дополнительной безопасности на электронной плате можно поставить внешний аппаратный сторожевой таймер (например этот TPS3828-33QDBVRQ1). Этот таймер сбрасывается через GPIO пин. Надо периодически подавать отрицательные перепады чтобы плата не сбрасывалась. Оригинальная прошивка знает про этот пин. Если кто-то попытается записать неоригинальную прошивку, которая не догадывается о существовании внешнего аппаратного сторожевого таймера, то эта неоригинальная прошивка будет постоянно сбрасываться.

*18) instant firmware update (мгновенное обновление прошивки)

Загрузчик должен уметь записывать и запускать принятую прошивку по любому смещению адреса Flash памяти. Это позволит сэкономить время на отчистку и запись прошивки в боевую flash память. Вместо удаления старой прошивки и записи на её место новой можно просто переключить боевую память на другой диапазон и тут же запустить на исполнение от туда код.

https://habr.com/ru/articles/575014/

19) Когда стартует загрузчик или приложение прошивка должна увеличивать счётчик запусков BootCnt. Если счетчик запусков превысит число N (например N=5), то загрузчик/приложение прикажет сам себе остаться в загрузчике. Приложение же должно обнулять счетчик запусков BootCnt после успешной инициализации или, например, на 30й секунде своей работы. Это своего рода защита от тыквы (зависания в инициализации приложения). Если залили приложение, которое из-за исключения постоянно reset(тится ) где-то в инициализации, то такое приложение после N reset(тов) автоматически свалится в загрузчик и можно будет спокойно записать по UART нормальную прошивку для ремонта.

20) Загрузчику не обязательно находится в начале Flash памяти. Загрузчик можно прописывать в самый последний сектор flash памяти, а приложение в начало Flash. Это позволит запускать приложение быстрее. Как загрузчик прыгает в приложение так и приложение может прыгать в загрузчик. Но тогда не будет работать пункт 19.

21) Загрузчик это не только еще одна прошивка в репозитории. Для загрузчика нужна инфраструктура и экосистема. В самом простом виде это консольное Win приложение (FW Loader) под PC для отправки прошивки по serial COM порту. А для смузи-поколения надо сделать FW Loader c GUI-интерфейсом, потом загрузку из браузера Chrome/Opera/FireFox. Причем надо всё сделать под три операционки: Windows, Linux, Mac. Также нужно мобильное приложение для отправки прошивки из-под Android и iOS. И ещё нужен Web-сервер с авторизацией для поиска необновлённых электронных зубных щеток и раскатывания новых обновлений FW на них. Поэтому загрузчики это на самом деле очень-очень много работы.

22) Сам загрузчик обычно записывают программатором по SWD/JTAG однако в приложении должна быть возможность записать другой загрузчик.

23) Исходя из соображений компактности в самом загрузчике должен быть только один интерфейс для обновления (DFU). Если гаджет - это автомобильный ECU, то интерфейс CAN, в остальных случаях часто самым компактным вариантом оказывается UART. Если в загрузчике вообще нет ни одного интерфейса, то появление кирпичей это вопрос буквально пары недель эксплуатации.

Вывод

Как видите, разработка надежного универсального быстрого загрузчика это достаточно комплексная задача.

Если есть, что добавить, то пишите в комментариях.

Словарь

Акроним

Расшифровка

SPI

Serial Peripheral Interface

DFU

device firmware update

FOTA

Firmware Over The Air

OTA 

Over The Air

NVRAM

Non-volatile random-access memory

CRC

cyclic redundancy check

FW

FirmWare

UART

universal asynchronous receiver-transmitter

SD

Secure Digital (Memory Card)

FS

File System

ISR

Interrupt Service Routine

Links
https://habr.com/ru/companies/rainbow/articles/275381/

http://microsin.net/programming/arm/appnote-6282-safe-and-secure-bootloader-implementation.html

https://microtechnics.ru/mikrokontroller-i-bootloader-opisanie-i-princip-raboty/

https://chipenable.ru/index.php/item/140

https://www.feaser.com/openblt/doku.php

https://habr.com/ru/articles/432966/
https://habr.com/ru/companies/ruvds/articles/536132/
https://habr.com/ru/companies/ruvds/articles/536156/

Контрольные вопросы про загрузчики

1--Зачем нужен загрузчик во встраиваемых системах? Назовите минимум 3 его функции.

2--Как загрузчик может обмениваться данными с приложением?

3--В чем опасность вызова функций загрузчика из приложения?

4--Как защитить микроконтроллер от загрузки чужеродного кода через загрузчик?

5--Как загрузчику понять, что загрузчик принял в самом деле прошивку, а не набор случайных циферок с правильной CRC?

6--Как сделать обновление прошивки по TCP/IP, если в загрузчике хватает NorFlash памяти только для драйвера UART?

7--Можно ли сделать так, чтобы загрузчик стартовал не с адреса начала Main Flash 0x0800_0000, а например с адреса 0x0806_0000?

8--Почему в микроконтроллерах STM32 секторы NOR Flash(а) разных размеров?

*9--Вам прислали прошивки в *.bin файле. Как загрузить и запустить эту прошивку по произвольному отступу в on-chip Nor Flash памяти?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы добавляете загрузчик в свои прошивки?
62.75% да32
37.25% нет19
Проголосовал 51 пользователь. Воздержались 11 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы делали генерация перемещаемого кода для Cortex-M?
20.93% да9
79.07% нет34
Проголосовали 43 пользователя. Воздержались 11 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы делали обновление прошивки по UART?
78.85% да41
21.15% нет11
Проголосовали 52 пользователя. Воздержались 10 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы добавляете в загрузчик NVRAM?
25% да11
75% нет33
Проголосовали 44 пользователя. Воздержались 13 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
В вашем загрузчике есть UART-CLI?
34.78% да16
65.22% нет30
Проголосовали 46 пользователей. Воздержались 12 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы подписываете прошивки?
40% да18
60% нет27
Проголосовали 45 пользователей. Воздержались 11 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы ставите внешний аппаратный сторожевой таймер?
28.89% да13
71.11% нет32
Проголосовали 45 пользователей. Воздержались 11 пользователей.
Теги:
Хабы:
Всего голосов 15: ↑10 и ↓5+7
Комментарии72

Публикации

Истории

Работа

DevOps инженер
42 вакансии

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань