Skip to content
Draft
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
59 changes: 59 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,65 @@ 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.

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 boot image failed verification, triggering an emergency update
(`WOLFBOOT_FAILURE_PHASE_BOOT`).
- 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.

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
37 changes: 37 additions & 0 deletions include/wolfboot/wolfboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,43 @@ 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 */

/* 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. Exactly 16 bytes so it maps to a single 128-bit
* write-once flash word and can be appended without read-modify-write. */
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
14 changes: 14 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ 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
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