Files
templates/c/protocan-boot/README.md

171 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ProtoCAN Boot
`protocan-boot` — переносимое C99-ядро адресной прошивки приборов по classic
CAN 2.0B и 29-битному ProtoCAN ID. Оно реализует сессию обновления, два
логических слота по 512 КиБ, последовательную запись 8-байтовых блоков,
CRC32, проверку совместимости, продолжение по номеру следующего блока и
безопасный выбор неактивного слота.
Это не готовый загрузчик конкретного STM32: внутри нет HAL, регистров Flash,
linker script, обработчиков прерываний, криптографии и перехода в приложение.
Эти операции предоставляет проект через таблицу callbacks.
## Слои
```text
приложение / CAN RX loop
|
v
pcan_boot_process() — протокол и state machine
|
v
pcan_boot_port_t — Flash, политика подписи, commit, reboot
|
v
STM32 HAL / другой MCU / host-тест
```
## Файлы
| Файл | Назначение | Зависимости |
|---|---|---|
| `include/pcan_boot.h` | публичный API, команды и структуры | C99 stdint/stdbool |
| `src/pcan_boot.c` | ProtoCAN ID, state machine и CRC32 | только публичный заголовок |
| `tests/test_pcan_boot.c` | хостовые тесты с RAM вместо Flash | стандартная библиотека C |
| `CMakeLists.txt` | сборка библиотеки и теста | CMake 3.13+ |
Модуль не зависит от `protocan-transport`: разбор пяти полей 29-битного ID
сделан переносимыми сдвигами, без непереносимых C-битовых полей.
## Карта `MsgType`
| Значение | Имя | Назначение |
|---:|---|---|
| `0x9` | `BOOT_CONTROL` | команды и метаданные |
| `0xA` | `BOOT_DATA_A` | блоки слота A |
| `0xB` | `BOOT_DATA_B` | блоки слота B |
| `0xC` | `BOOT_STATUS` | ответы и прогресс |
| `0xD` | `BOOT_DISCOVERY` | идентификация |
Для `BOOT_DATA_A/B` поле `MsgBody` — номер блока `0..65535`, а payload —
8 байт образа:
```text
offset = MsgBody * 8
512 КиБ = 512 * 1024 = 524 288 байт
524 288 / 8 = 65 536 блоков на слот
```
| `MsgBody` | Номер блока | Смещение | Байты слота |
|---:|---:|---:|---:|
| `0x0000` | 0 | `0x00000` | `0x00000..0x00007` |
| `0x0001` | 1 | `0x00008` | `0x00008..0x0000F` |
| `0xFFFF` | 65 535 | `0x7FFF8` | `0x7FFF8..0x7FFFF` |
Последний блок заканчивается на смещении `0x7FFFF`. Значение `0x80000`
равно размеру слота и является первой позицией за его границей. В коде эти
границы заданы константами `PCAN_BOOT_BLOCK_COUNT=65536` и
`PCAN_BOOT_SLOT_SIZE=524288`.
Последний кадр дополняется `0xFF`; ядро передаёт во Flash только оставшиеся
байты фактического образа и включает в CRC только их.
## Контракт порта
```c
typedef struct {
bool (*send)(void *, uint32_t can_id, const uint8_t *, uint8_t dlc);
bool (*erase_slot)(void *, uint8_t slot);
bool (*write_slot)(void *, uint8_t slot, uint32_t offset,
const uint8_t *, uint8_t length);
bool (*authorize)(void *, const pcan_boot_manifest_t *);
bool (*verify_image)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*set_pending_slot)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*confirm_running_slot)(void *);
void (*reboot)(void *);
} pcan_boot_port_t;
```
Обязательны `send`, `erase_slot`, `write_slot` и `set_pending_slot`.
`authorize` проверяет политику до стирания (тип, версия, anti-rollback).
`verify_image` выполняет платформенную проверку подписанного контейнера после
CRC32. Если callbacks отсутствуют, соответствующие дополнительные проверки
пропускаются; для серийного изделия их следует реализовать.
`set_pending_slot` должен атомарно сохранить boot metadata. До подтверждения
нового приложения загрузчик сохраняет возможность отката. `confirm_running_slot`
снимает pending-флаг после самопроверки приложения.
## Быстрый старт
```c
pcan_boot_t boot;
pcan_boot_config_t cfg = {
.device_type = 3, .device_id = 5, .product_type = 0x1234,
.hardware_revision = 2, .firmware_version = 0x01020000,
.active_slot = 0, .ack_window = 16
};
pcan_boot_port_t port = {
.send = can_send, .erase_slot = flash_erase_slot,
.write_slot = flash_write_slot, .authorize = image_authorize,
.verify_image = image_verify_signature,
.set_pending_slot = boot_set_pending, .reboot = system_reboot
};
pcan_boot_init(&boot, &cfg, &port, &board);
/* Из CAN RX-задачи, не из длительного Flash-прерывания: */
pcan_boot_process(&boot, rx.ExtId, rx.Data, rx.DLC);
```
## Последовательность обновления
1. `IDENTIFY` или discovery получает тип и версии прибора.
2. `ENTER_BOOT` открывает ненулевой `SessionID`.
3. `BEGIN_IMAGE` передаёт размер и CRC32.
4. `BEGIN_COMPAT` передаёт тип изделия, диапазон HW и версию FW.
5. Ядро выбирает неактивный слот и отвечает его номером.
6. `ERASE` вызывает порт стирания.
7. `BOOT_DATA_A` либо `BOOT_DATA_B` передаются строго по порядку.
8. `VERIFY` сравнивает CRC32 и вызывает проверку образа/подписи порта.
9. `COMMIT` атомарно помечает слот как pending.
10. После reboot приложение подтверждает запуск через `CONFIRM`.
Все изменяющие Flash команды адресуются конкретному `DeviceType/DeviceID`.
Ядро не принимает broadcast-запись и игнорирует кадры с чужим адресом или
`Route=FROM_DEVICE`.
## State machine
```text
IDLE -> ENTER_BOOT -> METADATA -> BEGIN_IMAGE + BEGIN_COMPAT
-> READY_TO_ERASE -> ERASE -> RECEIVING -> VERIFY -> VERIFIED
-> COMMIT -> REBOOT -> CONFIRM
```
При ошибке Flash, CRC, совместимости или подписи состояние переходит в
`FAILED`. Новая сессия начинается командой `ENTER_BOOT`; `ABORT` очищает
текущую сессию без изменения Flash.
## Сборка тестов
```sh
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
```
## Что остаётся проекту устройства
- зарезервировать bootloader, slot A/B и metadata в linker script;
- реализовать выравнивание и размеры страниц Flash;
- не выполнять длительное стирание непосредственно в CAN IRQ;
- атомарно хранить active/pending/confirmed и счётчик попыток запуска;
- проверить вектор, границы и подпись образа;
- включить watchdog и rollback при неподтверждённом запуске;
- согласовать фильтры CAN для `MsgType=0x9..0xD`.
Проект пока не подключён ни к одной конкретной плате; это самостоятельный
шаблон для интеграции в будущие загрузчики приборов SET.