diff --git a/firmware/bootloader/README.md b/firmware/bootloader/README.md index e658199..f525058 100644 --- a/firmware/bootloader/README.md +++ b/firmware/bootloader/README.md @@ -1,71 +1,45 @@ # bootloader -> Загрузчик MIMXRT1052 — A/Б обновление `tft_app` через microSD (MCUboot, -> Direct-XIP). Сам bootloader обновляется только через USB ROM (blhost) или -> SWD — не в поле. Единственный канал диагностики: USB CDC ACM (JSON-lines, -> тот же стиль протокола, что у `firmware_test`). -> -> Версия: `0.1.0` | Статус: Фаза 2 (bootutil / MCUboot Direct-XIP) завершена — см. [PLAN.md](PLAN.md) +Загрузчик MIMXRT1052: выбирает и запускает приложение `tft_app` из одного из двух слотов +(MCUboot, Direct-XIP), обновляет его с microSD, восстанавливает плату при зависании образа. Сам +загрузчик прошивается только по USB ROM (blhost) или SWD — в поле не обновляется. Канал диагностики — +USB CDC ACM (JSON-строки). ---- - -## Содержание - -- [bootloader](#bootloader) - - [Содержание](#содержание) - - [Быстрый старт](#быстрый-старт) - - [1. Сборка](#1-сборка) - - [2. Прошивка](#2-прошивка) - - [3. Подключение](#3-подключение) - - [4. Отладка](#4-отладка) - - [Архитектура](#архитектура) - - [Карта Flash](#карта-flash) - - [Протокол](#протокол) - - [Roadmap](#roadmap) - - [Версионирование](#версионирование) +**Принцип работы** (карта памяти, выбор образа, обновление, версии, даунгрейд, recovery) — +[BOOT_FLOW.md](BOOT_FLOW.md). --- ## Быстрый старт -### 1. Сборка +### Сборка ```bash -just build::build-bootloader-debug -just build::hab-bootloader-debug +just build::build-bootloader-debug # bootloader.elf/.bin +just build::hab-bootloader-debug # HAB-контейнер bootloader_hab.bin ``` -### 2. Прошивка +### Прошивка -Для итеративной разработки — SWD (не требует смены boot-режима платы): +SWD (для итеративной разработки, не требует смены boot-режима платы): ```bash just host::flash-swd-bootloader-debug -# обязателен power cycle платы после прошивки +# после прошивки обязателен power cycle платы ``` -Через USB ROM (SDP, плата в режиме Serial Downloader): +USB ROM (SDP, плата в режиме Serial Downloader): ```bash just host::flash bootloader debug ``` -### 3. Подключение - -CDC поднимается, только если в обоих слотах (A/Б) нет валидного образа — иначе -bootloader сразу выбирает слот и прыгает в `tft_app` (Direct-XIP), не поднимая -USB вообще. Для проверки диагностического режима слоты должны быть пустыми -или содержать только невалидные образы. - -USB CDC ACM (тот же порядок, что у `firmware_test`): +### Подключение ```bash -# macOS -screen /dev/cu.usbmodemXXXX +screen /dev/cu.usbmodemXXXX # macOS; порт свой на каждое подключение ``` -Проверка связи: - ```json → {"type":"cmd","cmd":"ping"} ← {"type":"pong"} @@ -74,125 +48,103 @@ screen /dev/cu.usbmodemXXXX ← {"type":"version_response","fw":"0.1.0"} ``` -### 4. Отладка +USB поднимается на каждой загрузке до обращения к SD, поэтому статусы видны, даже если подключиться +заранее. -VSCode → `🐛 Debug: bootloader` (`.vscode/launch.json`) — пересобирает через -`build:bootloader-debug`, подключается к GDB-серверу (`just host::debug-server` -должен быть запущен), останавливается на `main`. +### Отладка + +VSCode → `🐛 Debug: bootloader` — пересобирает, подключается к GDB-серверу +(`just host::debug-server` должен быть запущен), останавливается на `main`. Под отладчиком аппаратный +watchdog приостановлен, пошаговая отладка сбросами не сбивается. --- ## Архитектура -Bring-up идентичен `firmware_test` (`firmware/test/src/main.c`), но вместо -тестового раннера — сразу попытка выбрать и запустить `tft_app` из одного из -двух слотов (MCUboot Direct-XIP, `bootutil`), и только если валидного образа -нет ни в одном слоте — диагностический CLI: +Загрузчик работает **без SDRAM** (SEMC поднимает само приложение в своём раннем startup) и без +дисплея/RTOS: инициализация, доступ к QSPI-flash, чтение FatFS с SD, проверка и выбор образа, прыжок. +Линкер жёстко ограничивает код бюджетом области загрузчика (256 КБ) с `ASSERT` на границу Slot A — +превышение становится ошибкой сборки, а не тихим заездом в чужую область. -```bash +```text firmware/bootloader/ -├── CMakeLists.txt -├── mcuboot_port/ — интеграция bootutil (MCUboot) с bsp_qspi_flash -│ ├── flash_map_backend.c — flash_area_* поверх bsp_qspi_flash (2 слота) -│ ├── keys.c — публичный ключ ECDSA-P256 для imgtool-подписи -│ ├── bootutil_sources.cmake — общий список исходников bootutil+TinyCrypt+ -│ │ ASN.1 (используется и host-тестами) -│ └── mcuboot_config/, sysflash/, flash_map_backend/, keys/ — конфиг-заголовки +├── src/ +│ ├── main.c — точка входа: инициализация → одна попытка загрузки +│ │ (SD-скан + recovery-гейт + прыжок) → цикл ожидания +│ ├── boot_select.* — выбор валидного слота и прыжок в выбранный образ +│ ├── slot_version.* — read-only проверка и чтение версии слота (без побочных +│ │ эффектов на flash) +│ ├── update_policy.* — чистая логика «ставить/пропустить» + целевой слот +│ ├── recovery.* — чистая логика решения recovery (порог / фолбэк / режим) +│ ├── sd_update.* — оркестрация: смонтировать SD, найти TFT_APP.BIN, +│ │ установить в целевой слот с потоковой verify-записью +│ ├── cli.* — построчный IO + диспетчеризация команд +│ ├── protocol.* — сериализация исходящих событий +│ └── version.h.in — шаблон версии (CMake → generated/version.h) │ -└── src/ - ├── main.c — board_hw_init → led/tick/qspi init → - │ boot_select_and_jump() (при успехе не - │ возвращается) → иначе usb_cdc init → ожидание - │ CDC (LED_HEARTBEAT мигает) → LED_APP on → - │ главный цикл (poll + cli_process) - │ - ├── boot_select.c/.h — обёртка над bootutil: `boot_go()` (выбор - │ валидного слота по подписи/версии) + прыжок в - │ выбранный образ (`jump_to_image`) - │ - ├── version.h.in — шаблон версии (CMake → generated/version.h) - │ - ├── cli.h / cli.c — IO-слой: буферизация строк, парсинг "type"/"cmd" - │ (урезанное подмножество firmware_test/src/cli.c) - │ - └── protocol.h / .c — сериализация исходящих событий → cli_send() +├── mcuboot_port/ — интеграция bootutil (MCUboot) поверх bsp_qspi_flash: +│ flash_area_* на 2 слота, конфиг, публичный ключ ECDSA-P256 +├── fatfs/ — read-only FatFS для чтения TFT_APP.BIN с карты +└── test_stub/ — самостоятельный подписанный образ-заглушка вместо + tft_app для аппаратной проверки загрузчика ``` -Bootloader **без SDRAM** — DCD не используется (`bsp_boot_xip_no_dcd` вместо -`bsp_boot_xip`). `tft_app` инициализирует SEMC сама в своём раннем startup -(см. [BOOTLOADER_FLASH_MAP.md](../../docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md)). - -Линкер: `cmake/linker/MIMXRT1052xxxxx_bootloader_flexspi_nor.ld` — `m_text` -жёстко ограничен 247 КБ, с `ASSERT` на границу Slot A. - -Верификация образов — подпись ECDSA-P256 (bootutil/TinyCrypt), два независимых -слота (A/Б) по 2 МБ каждый, без swap/scratch (Direct-XIP). Аппаратно -верифицировано все 5 сценариев чек-листа (единственный валидный слот, выбор -более новой версии, откат при повреждении, оба слота пусты, revert -неподтверждённого образа) - ---- - -## Карта Flash - -Полная карта, обоснование размеров и принцип "один bootloader на любую -ёмкость чипа" — в -[docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md](../../docs/mimxrt1052/BOOTLOADER_FLASH_MAP.md). - -| Область | Смещение | Размер | -| --------------------------- | ------------ | ------------------- | -| Bootloader | `0x60000000` | 256 КБ | -| Slot A (tft_app) | `0x60040000` | 2 МБ | -| Slot Б (tft_app) | `0x60240000` | 2 МБ | -| ФС ассетов (спрайты/музыка) | `0x60440000` | остальное (рантайм) | +`update_policy` и `recovery` — чистые функции без доступа к железу, целиком покрыты host-тестами. +`boot_select`, `sd_update`, `flash_map_backend` — тонкий аппаратный слой поверх них. --- ## Протокол -Транспорт и фреймирование — как у `firmware_test` -([firmware/test/README.md](../test/README.md#протокол-v2)): USB CDC ACM, -JSON-lines, максимум 128 байт на строку. +USB CDC ACM, JSON-строки, максимум 128 байт на строку. -**Реализовано (Фаза 1):** +**Команды хоста:** -| Команда хоста | Ответ | -| ------------------------------------ | ------------------------------------------ | -| `{"type":"cmd","cmd":"ping"}` | `{"type":"pong"}` | -| `{"type":"cmd","cmd":"get_version"}` | `{"type":"version_response","fw":"X.Y.Z"}` | +| Команда | Ответ | +| ------------------------------------ | ------------------------------------------------------------------------------------- | +| `{"type":"cmd","cmd":"ping"}` | `{"type":"pong"}` | +| `{"type":"cmd","cmd":"get_version"}` | `{"type":"version_response","fw":"X.Y.Z"}` | +| `{"type":"cmd","cmd":"wdog"}` | `{"type":"wdog","armed":…,"timeout_s":…,"recovered":…,"reset_count":…,"threshold":…}` | -Ошибки: `{"ok":false,"error":"PARSE_ERR"}` / `"UNKNOWN_CMD"` / `"LINE_TOO_LONG"`. +**Исходящие статусы** `{"type":"status","state":"…"}`: -Без `session_start` — в отличие от `firmware_test`, bootloader не шлёт -приветствие автоматически; живость проверяется явным `ping` (тот же паттерн, -что использует HIL-фикстура `firmware_cdc` для `firmware_test`). +| Состояние | Когда | +| ---------------- | ------------------------------- | +| `waiting_for_sd` | нет валидного слота, ждём карту | +| `installing` | идёт запись образа в слот | +| `update_skipped` | кандидат отклонён по версии | +| `recovery_mode` | плата в режиме восстановления | -**Появится в следующих фазах** (см. [PLAN.md](PLAN.md)): `status`-события -(`waiting_for_sd`, `installing`, `smoke_pass`/`smoke_fail`, `booting`) — -Фазы 3-4. +**Ошибки** `{"ok":false,"error":"…"}`: `SD_CANDIDATE_INVALID`, `SD_INSTALL_WRITE_FAILED`, +`SD_INSTALL_REJECTED`, `SD_DOWNGRADE_ERASE_FAILED`, `PARSE_ERR`, `UNKNOWN_CMD`, `LINE_TOO_LONG`. + +Событие `wdog` эмитится также автоматически один раз на старте, если предыдущий сброс был по +watchdog (`recovered:true`). --- -## Roadmap +## Тесты -Полный план по фазам с целями и критериями верификации — [PLAN.md](PLAN.md). +**Host** — вся логика без железа (выбор слота, сравнение версий, политика установки, решение +recovery, протокол, CLI) на in-memory flash-фейках: -| Фаза | Что делает | -| ---- | ---------------------------------------------------------------- | -| 0 ✅ | Карта Flash, регистрация в сборке | -| 1 ✅ | Скелет: bring-up, USB CDC, ping/get_version, LED heartbeat | -| 2 ✅ | bootutil (MCUboot Direct-XIP) — выбор слота, верификация подписи | -| 3 | Установка образа с microSD, состояние "нет валидного слота" | -| 4 | SDRAM/W25Q smoke-test, словарь LED-паттернов | -| 5 | HAB Release, интеграция в service-tui | +```bash +just build::test-host +``` + +**Аппаратный стенд** — сборка и подпись образов-заглушек, замещающих `tft_app` при ручной проверке +на плате (здоровые образы + варианты с зависанием на разных стадиях для проверки восстановления): + +```bash +just build::build-mcuboot-stub +``` + +Чек-листы ручной проверки лежат рядом со стендом в `test_stub/`. --- ## Версионирование -Версия задаётся `project(bootloader VERSION X.Y.Z)` в `CMakeLists.txt`, -прокидывается через `configure_file(src/version.h.in → generated/version.h)` -в `BOOTLOADER_VERSION_STR` — тот же механизм, что у `firmware_test` -(см. [firmware/test/README.md#версионирование](../test/README.md#версионирование)). - -`version.h` генерируется, не редактируется вручную. +Версия задаётся `project(bootloader VERSION X.Y.Z)` в `CMakeLists.txt` и прокидывается через +`configure_file(src/version.h.in → generated/version.h)` в строку, которую возвращает +`get_version`. `version.h` генерируется, вручную не редактируется.