Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,79 @@ For more information about the update process, see [Firmware Update](firmware_up

For the image format, see [Firmware Image](firmware_image.md)

### Failure diagnostics

When wolfBoot is compiled with `WOLFBOOT_PERSIST_FAILURE_STATUS`, it records the
cause of update, boot and rollback failures into a dedicated flash region so the
application can read them back after a failed update or an unexpected rollback.

This feature is disabled by default. To enable it, set
`WOLFBOOT_PERSIST_FAILURE_STATUS=1` and provide:

- `WOLFBOOT_DIAGNOSTICS_ADDRESS`: the start address of the region reserved for
diagnostics (it must be sector-aligned and must not overlap any partition);
- `WOLFBOOT_DIAGNOSTICS_SECTORS`: the size of the region, specified as a number
of flash sectors (default: 2);
- `WOLFBOOT_DIAGNOSTICS_EXT`: setting this causes the region to live in
external flash. Only meaningful when `EXT_FLASH` is enabled.
- `WOLFBOOT_DIAGNOSTICS_RECORD_SIZE`: the on-flash slot size for each record and
the sector header, in bytes (default: 16). It must be a multiple of, and no
smaller than, the flash write granularity. Raise it on platforms whose flash
write word is wider than 16 bytes (for example 32 for the 256-bit words on
STM32H7).

The region is managed as a circular store over its sectors. With two or more
sectors, older records are retained until the log wraps all the way around, so
there is always at least one full sector of history. With a single sector
(`WOLFBOOT_DIAGNOSTICS_SECTORS=1`) the sector is erased and restarted when it
fills, discarding all previous records.

The following events are recorded:

- An update image was rejected before performing the update
(`WOLFBOOT_FAILURE_PHASE_UPDATE`).
- A bootloader self-update image was rejected
(`WOLFBOOT_FAILURE_PHASE_SELF_UPDATE`).
- A boot image failed verification, triggering an emergency update
(`WOLFBOOT_FAILURE_PHASE_BOOT`).
- The emergency-update image also failed verification, leaving the device
unbootable (`WOLFBOOT_FAILURE_PHASE_RECOVERY`).
- A rollback was caused by a new image that never confirmed via
`wolfBoot_success()` (`WOLFBOOT_FAILURE_PHASE_ROLLBACK`).

For verification failures, the cause distinguishes a bad header
(`WOLFBOOT_FAILURE_CAUSE_HEADER`), a failed integrity/hash check
(`WOLFBOOT_FAILURE_CAUSE_HASH`) and a failed signature/authenticity check
(`WOLFBOOT_FAILURE_CAUSE_SIGNATURE`). A rollback uses
`WOLFBOOT_FAILURE_CAUSE_NOT_CONFIRMED`.

The application can read the records, ordered newest-first, through this API:

- `int wolfBoot_get_failure_count(void)`: number of records currently stored.
- `int wolfBoot_get_failure(int index, struct wolfBoot_failure_record *out)`:
copy record `index` (0 is the most recent) into `out`. Returns 0 on success.
- `int wolfBoot_clear_failures(void)`: erase the diagnostics region.

The read functions only read the region and have no external dependency.
`wolfBoot_clear_failures()`, however, erases flash, so an application that calls
it must have the HAL flash driver (`hal_flash_unlock`, `hal_flash_erase`,
`hal_flash_lock`) similarly to `wolfBoot_success()`.

Each `struct wolfBoot_failure_record` reports `phase`, `cause`, `partition`,
`fw_version` (the version of the offending image, when known) and a monotonic
`seq` number.

```c
struct wolfBoot_failure_record rec;
int i, n = wolfBoot_get_failure_count();
for (i = 0; i < n; i++) {
if (wolfBoot_get_failure(i, &rec) == 0) {
printf("failure: phase %u cause %u part %u ver 0x%x\n",
rec.phase, rec.cause, rec.partition, rec.fw_version);
}
}
```

## NSC API

If you're running wolfBoot on an ARM TrustZone-enabled device (see for example
Expand Down
53 changes: 53 additions & 0 deletions include/target.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,59 @@
#endif
#endif

#ifdef WOLFBOOT_PERSIST_FAILURE_STATUS
#ifndef WOLFBOOT_DIAGNOSTICS_SECTORS
#define WOLFBOOT_DIAGNOSTICS_SECTORS 2
#endif

#if (WOLFBOOT_DIAGNOSTICS_ADDRESS % WOLFBOOT_SECTOR_SIZE) != 0
#error "Diagnostics region is not sector-aligned"
#endif

#if !defined(WOLFBOOT_PART_USE_ARCH_OFFSET) && \
!defined(PULL_LINKER_DEFINES) && !defined(WOLFBOOT_DIAGNOSTICS_EXT)
#if !defined(PART_BOOT_EXT) && \
((WOLFBOOT_PARTITION_BOOT_ADDRESS + 0) != 0) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0 + \
WOLFBOOT_DIAGNOSTICS_SECTORS * WOLFBOOT_SECTOR_SIZE) > \
(WOLFBOOT_PARTITION_BOOT_ADDRESS + 0)) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0) < \
(WOLFBOOT_PARTITION_BOOT_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0))
#error "Diagnostics region overlaps the boot partition"
#endif

#if !defined(PART_UPDATE_EXT) && \
((WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0) != 0) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0 + \
WOLFBOOT_DIAGNOSTICS_SECTORS * WOLFBOOT_SECTOR_SIZE) > \
(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0)) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0) < \
(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0))
#error "Diagnostics region overlaps the update partition"
#endif

#if !defined(PART_SWAP_EXT) && \
((WOLFBOOT_PARTITION_SWAP_ADDRESS + 0) != 0) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0 + \
WOLFBOOT_DIAGNOSTICS_SECTORS * WOLFBOOT_SECTOR_SIZE) > \
(WOLFBOOT_PARTITION_SWAP_ADDRESS + 0)) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0) < \
(WOLFBOOT_PARTITION_SWAP_ADDRESS + 0 + WOLFBOOT_SECTOR_SIZE))
#error "Diagnostics region overlaps the swap partition"
#endif

#if defined(WOLFBOOT_ORIGIN) && \
((BOOTLOADER_PARTITION_SIZE + 0) != 0) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0 + \
WOLFBOOT_DIAGNOSTICS_SECTORS * WOLFBOOT_SECTOR_SIZE) > \
(WOLFBOOT_ORIGIN + 0)) && \
((WOLFBOOT_DIAGNOSTICS_ADDRESS + 0) < \
(WOLFBOOT_ORIGIN + 0 + BOOTLOADER_PARTITION_SIZE + 0))
#error "Diagnostics region overlaps the bootloader"
#endif
#endif
#endif /* WOLFBOOT_PERSIST_FAILURE_STATUS */

#endif /* WOLFBOOT_FIXED_PARTITIONS */

#if !defined(WOLFBOOT_NO_LOAD_ADDRESS)
Expand Down
40 changes: 40 additions & 0 deletions include/wolfboot/wolfboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,46 @@ int wolfBoot_dualboot_candidate(void);
int wolfBoot_dualboot_candidate_addr(void**);
int wolfBoot_get_partition_state(uint8_t part, uint8_t *st);

#ifdef WOLFBOOT_PERSIST_FAILURE_STATUS
/* Phase in which the failure was detected */
#define WOLFBOOT_FAILURE_PHASE_UPDATE 1 /* update image rejected before swap */
#define WOLFBOOT_FAILURE_PHASE_BOOT 2 /* boot image failed verification */
#define WOLFBOOT_FAILURE_PHASE_ROLLBACK 3 /* rolled back to a previous image */
#define WOLFBOOT_FAILURE_PHASE_RECOVERY 4 /* emergency-update image also failed
* verification (device unbootable) */
#define WOLFBOOT_FAILURE_PHASE_SELF_UPDATE 5 /* bootloader self-update image
* rejected */

/* Cause of the failure */
#define WOLFBOOT_FAILURE_CAUSE_HEADER 1 /* bad/invalid image header */
#define WOLFBOOT_FAILURE_CAUSE_HASH 2 /* hash/integrity check failed */
#define WOLFBOOT_FAILURE_CAUSE_SIGNATURE 3 /* signature/auth check failed */
#define WOLFBOOT_FAILURE_CAUSE_NOT_CONFIRMED 4 /* image never confirmed via
* wolfBoot_success() */

/* Persisted failure record. */
struct wolfBoot_failure_record {
uint32_t seq; /* monotonic sequence number (higher = newer) */
uint8_t phase; /* WOLFBOOT_FAILURE_PHASE_* */
uint8_t cause; /* WOLFBOOT_FAILURE_CAUSE_* */
uint8_t partition; /* PART_BOOT / PART_UPDATE */
uint8_t reserved;
uint32_t fw_version; /* version of the offending image, 0 if unknown */
uint32_t crc; /* CRC32 over the preceding 12 bytes */
};

/* Public API */
int wolfBoot_get_failure_count(void);
int wolfBoot_get_failure(int index, struct wolfBoot_failure_record *out);
int wolfBoot_clear_failures(void);

#if defined(__WOLFBOOT) || defined(UNIT_TEST)
/* Internal API */
int wolfBoot_record_failure(uint8_t phase, uint8_t cause, uint8_t partition,
uint32_t fw_version);
#endif
#endif /* WOLFBOOT_PERSIST_FAILURE_STATUS */


/* Encryption algorithm constants - always available for tools */
#define ENCRYPT_BLOCK_SIZE_CHACHA 64
Expand Down
17 changes: 17 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ ifeq ($(WOLFBOOT_TIME_TEST),1)
CFLAGS+=-D"WOLFBOOT_TIME_TEST"
endif

# Persist update/boot/rollback failure diagnostics to a dedicated flash region
ifeq ($(WOLFBOOT_PERSIST_FAILURE_STATUS),1)
CFLAGS+=-D"WOLFBOOT_PERSIST_FAILURE_STATUS"
ifneq ($(WOLFBOOT_DIAGNOSTICS_ADDRESS),)
CFLAGS+=-D"WOLFBOOT_DIAGNOSTICS_ADDRESS=$(WOLFBOOT_DIAGNOSTICS_ADDRESS)"
endif
ifneq ($(WOLFBOOT_DIAGNOSTICS_SECTORS),)
CFLAGS+=-D"WOLFBOOT_DIAGNOSTICS_SECTORS=$(WOLFBOOT_DIAGNOSTICS_SECTORS)"
endif
ifneq ($(WOLFBOOT_DIAGNOSTICS_RECORD_SIZE),)
CFLAGS+=-D"WOLFBOOT_DIAGNOSTICS_RECORD_SIZE=$(WOLFBOOT_DIAGNOSTICS_RECORD_SIZE)"
endif
ifeq ($(WOLFBOOT_DIAGNOSTICS_EXT),1)
CFLAGS+=-D"WOLFBOOT_DIAGNOSTICS_EXT"
endif
endif

# Support for TPM signature verification
ifeq ($(WOLFBOOT_TPM_VERIFY),1)
WOLFTPM:=1
Expand Down
Loading
Loading