mirror of https://github.com/wolfSSL/wolfBoot.git
Merge pull request #892 from danielinux/fenrir-fixes-2026-09-15
Update_ram fallback fixes + fenrir fixes 2026 09 15pull/898/merge
commit
8bac8cc589
|
|
@ -3125,21 +3125,23 @@ int mpfs_ddr_init(unsigned int outer_retry)
|
|||
DDRPHY_REG(PHY_TIP_CFG_PARAMS) = LIBERO_SETTING_TIP_CFG_PARAMS;
|
||||
mb();
|
||||
|
||||
/* Step 9: Run training + post-training + MTC sanity, with retry on
|
||||
* MTC failure.
|
||||
/* Step 9: Run training + post-training write calibration, with retry on
|
||||
* calibration failure.
|
||||
*
|
||||
* Why MTC is the retry trigger (not PHY_TRAINING_STATUS): when the
|
||||
* manual ADDCMD training picks a marginal phase/dly that doesn't
|
||||
* resolve into a usable DRAM alignment, train_stat sticks at 0x1
|
||||
* (BCLK_SCLK only). But TIP keeps spinning in the background and
|
||||
* eventually flips the WRLVL/RDGATE/DQ_DQS bits to read 0x1D, even
|
||||
* though the alignment is bogus. An outer retry keyed on
|
||||
* PHY_TRAINING_STATUS sees that bogus 0x1D and stops. MTC actually
|
||||
* exercises the DDR controller -- it times out unambiguously when
|
||||
* training was bad, and is the reliable signal.
|
||||
* The reliability gate is WRCALIB (the per-lane write-cal sweep must
|
||||
* calibrate all lanes), not PHY_TRAINING_STATUS: when the manual ADDCMD
|
||||
* training picks a marginal phase/dly that doesn't resolve into a usable
|
||||
* DRAM alignment, TIP's train_stat self-report can still read "complete"
|
||||
* even though the write path is bad. Gating on train_stat alone let bad
|
||||
* boots through and the 19 MB load then hard-failed every block.
|
||||
*
|
||||
* Empirical baseline: ~30% per-attempt training failure rate -> 5
|
||||
* retries gives ~99.7% cumulative success rate.
|
||||
* MTC sanity is a secondary gate, run only when TIP did not report full
|
||||
* training (0x1C). When TIP does complete full training, MTC is skipped:
|
||||
* the MTC engine has a separate DDRC-internal access issue and would just
|
||||
* burn all retries and end in a WDT reset.
|
||||
*
|
||||
* Empirical baseline: ~30% per-attempt training failure rate; 3 inner x
|
||||
* 6 outer retries (up to 18 attempts) covers it with margin.
|
||||
*/
|
||||
{
|
||||
uint32_t train_retry = 0;
|
||||
|
|
|
|||
|
|
@ -587,11 +587,14 @@ static void hal_shm_init(void)
|
|||
|
||||
static void hal_shm_status_set(ShmInfo_t* info, uint32_t status)
|
||||
{
|
||||
IPC_TASKS_SEND(USE_IPC_SEND) = 1;
|
||||
if (info != NULL) {
|
||||
info->magic = SHAREM_MEM_MAGIC;
|
||||
info->status = status;
|
||||
}
|
||||
/* publish the fields before signaling: the peer reads them right
|
||||
* after seeing the IPC event */
|
||||
DSB();
|
||||
IPC_TASKS_SEND(USE_IPC_SEND) = 1;
|
||||
}
|
||||
|
||||
static uint32_t hal_shm_status_wait(ShmInfo_t* info, uint32_t status,
|
||||
|
|
@ -616,6 +619,9 @@ static uint32_t hal_shm_status_wait(ShmInfo_t* info, uint32_t status,
|
|||
}
|
||||
/* clear event */
|
||||
IPC_EVENTS_RECEIVE(USE_IPC_RECV) = 0;
|
||||
/* the sender published the fields before the event: order this
|
||||
* core's field reads after the event observation */
|
||||
DSB();
|
||||
/* if we got an event and "info" not provided, just return status to
|
||||
* signal event occurred */
|
||||
if (info == NULL) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
/* Assembly helpers */
|
||||
#define DMB() __asm__ volatile ("dmb")
|
||||
#define DSB() __asm__ volatile ("dsb")
|
||||
#define DSB() __asm__ volatile ("dsb" ::: "memory")
|
||||
#define ISB() __asm__ volatile ("isb")
|
||||
#define NOP() __asm__ volatile ("nop")
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ static void spi_push_tx(unsigned int sel, unsigned int pcs, unsigned char data,
|
|||
| SPI_PUSHR_PCS(pcs) | data;
|
||||
}
|
||||
|
||||
/* Perform a SPI transaction. Set cont!=0 to not let CS go low after this*/
|
||||
/* Perform a SPI transaction. Set cont!=0 to keep CS low (asserted) after this */
|
||||
static void spi_transaction(unsigned int sel, unsigned int pcs,
|
||||
const unsigned char *out, unsigned char *in, unsigned int size,
|
||||
int cont)
|
||||
|
|
|
|||
|
|
@ -336,6 +336,12 @@ enum elbc_amask_sizes {
|
|||
|
||||
#define NAND_CMD_READSTART 0x30 /* Extended command for large page devices */
|
||||
|
||||
/* NAND device status byte (ONFI): bit 0 set = program/erase failed,
|
||||
* bit 7 clear = write protected. Success requires bit 0 clear and
|
||||
* bit 7 set. */
|
||||
#define NAND_STATUS_FAIL (1 << 0) /* DQ0: 1 = program/erase fail */
|
||||
#define NAND_STATUS_WP_N (1 << 7) /* DQ7: 0 = write protected */
|
||||
|
||||
|
||||
/* DDR */
|
||||
/* DDR3: 512MB, 333.333 MHz (666.667 MT/s) */
|
||||
|
|
@ -689,6 +695,15 @@ static int hal_flash_command(uint8_t iswrite)
|
|||
if (!(ltesr & ELBC_LTESR_CC)) {
|
||||
ret = -1;
|
||||
}
|
||||
else if (ltesr & ELBC_LTESR_FCT) {
|
||||
/* a CW/RSW wait timed out: the device never became ready */
|
||||
ret = -1;
|
||||
}
|
||||
else if (iswrite == 0 && (ltesr & ELBC_LTESR_PAR)) {
|
||||
/* uncorrectable ECC error during the FCM read: the data in the
|
||||
* FCM buffer cannot be trusted */
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* clear interrupt */
|
||||
set32(ELBC_LTESR, ltesr & ELBC_NAND_MASK);
|
||||
|
|
@ -1063,18 +1078,18 @@ static void config_io_pin(uint8_t port, uint8_t pin, int dir, int open_drain,
|
|||
pin_2bit_dir = (uint32_t)(dir << (NUM_OF_PINS -
|
||||
(pin % (NUM_OF_PINS / 2) + 1) * 2));
|
||||
|
||||
/* Setup the direction */
|
||||
/* Setup the direction: one masked store - a clear-then-set pair
|
||||
* would drop a concurrent update to another pin in the same
|
||||
* register */
|
||||
tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ?
|
||||
get32(GUTS_CPDIR2(port)) :
|
||||
get32(GUTS_CPDIR1(port));
|
||||
|
||||
if (pin > (NUM_OF_PINS / 2) - 1) {
|
||||
set32(GUTS_CPDIR2(port), ~pin_2bit_mask & tmp_val);
|
||||
set32(GUTS_CPDIR2(port), pin_2bit_dir | tmp_val);
|
||||
set32(GUTS_CPDIR2(port), (~pin_2bit_mask & tmp_val) | pin_2bit_dir);
|
||||
}
|
||||
else {
|
||||
set32(GUTS_CPDIR1(port), ~pin_2bit_mask & tmp_val);
|
||||
set32(GUTS_CPDIR1(port), pin_2bit_dir | tmp_val);
|
||||
set32(GUTS_CPDIR1(port), (~pin_2bit_mask & tmp_val) | pin_2bit_dir);
|
||||
}
|
||||
|
||||
/* Calculate pin location for 1bit mask */
|
||||
|
|
@ -1089,21 +1104,21 @@ static void config_io_pin(uint8_t port, uint8_t pin, int dir, int open_drain,
|
|||
set32(GUTS_CPODR(port), ~pin_1bit_mask & tmp_val);
|
||||
}
|
||||
|
||||
/* Setup the assignment */
|
||||
/* Setup the assignment: one masked store (same reason as the
|
||||
* direction write above) */
|
||||
tmp_val = (pin > (NUM_OF_PINS/2) - 1) ?
|
||||
get32(GUTS_CPPAR2(port)):
|
||||
get32(GUTS_CPPAR1(port));
|
||||
pin_2bit_assign = (uint32_t)(assign <<
|
||||
(NUM_OF_PINS - (pin % (NUM_OF_PINS / 2) + 1) * 2));
|
||||
|
||||
/* Clear and set 2 bits mask */
|
||||
if (pin > (NUM_OF_PINS/2) - 1) {
|
||||
set32(GUTS_CPPAR2(port), ~pin_2bit_mask & tmp_val);
|
||||
set32(GUTS_CPPAR2(port), pin_2bit_assign | tmp_val);
|
||||
set32(GUTS_CPPAR2(port), (~pin_2bit_mask & tmp_val) |
|
||||
pin_2bit_assign);
|
||||
}
|
||||
else {
|
||||
set32(GUTS_CPPAR1(port), ~pin_2bit_mask & tmp_val);
|
||||
set32(GUTS_CPPAR1(port), pin_2bit_assign | tmp_val);
|
||||
set32(GUTS_CPPAR1(port), (~pin_2bit_mask & tmp_val) |
|
||||
pin_2bit_assign);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1641,13 +1656,19 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
page_size = 512;
|
||||
set32(ELBC_FCR, ELBC_FCR_CMD(0, NAND_CMD_READA) |
|
||||
ELBC_FCR_CMD(1, NAND_CMD_PAGE_PROG2) |
|
||||
ELBC_FCR_CMD(2, NAND_CMD_PAGE_PROG1));
|
||||
ELBC_FCR_CMD(2, NAND_CMD_PAGE_PROG1) |
|
||||
ELBC_FCR_CMD(3, NAND_CMD_STATUS));
|
||||
/* the CM3+RSW pair issues the status command after the program
|
||||
* execute and waits for it, so MDR holds the page status like the
|
||||
* large page path */
|
||||
set32(ELBC_FIR, ELBC_FIR_OP(0, ELBC_FIR_OP_CW0) |
|
||||
ELBC_FIR_OP(1, ELBC_FIR_OP_CM2) |
|
||||
ELBC_FIR_OP(2, ELBC_FIR_OP_CA) |
|
||||
ELBC_FIR_OP(3, ELBC_FIR_OP_PA) |
|
||||
ELBC_FIR_OP(4, ELBC_FIR_OP_WB) |
|
||||
ELBC_FIR_OP(5, ELBC_FIR_OP_CW1));
|
||||
ELBC_FIR_OP(5, ELBC_FIR_OP_CW1) |
|
||||
ELBC_FIR_OP(6, ELBC_FIR_OP_CM3) |
|
||||
ELBC_FIR_OP(7, ELBC_FIR_OP_RSW));
|
||||
#endif
|
||||
(void)block_size; /* not used - shown for reference */
|
||||
|
||||
|
|
@ -1690,7 +1711,13 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
wolfBoot_printf("write page %d, col %d, status %x\n",
|
||||
page, col, status);
|
||||
#endif
|
||||
(void)status;
|
||||
/* DQ0 set = program failed, DQ7 clear = write protected: the
|
||||
* page did not program. Stop; retrying the same page fails the
|
||||
* same way. */
|
||||
if ((status & NAND_STATUS_FAIL) || !(status & NAND_STATUS_WP_N)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
address += write_size;
|
||||
pos += write_size;
|
||||
data += write_size;
|
||||
|
|
@ -1851,7 +1878,12 @@ int ext_flash_erase(uintptr_t address, int len)
|
|||
#ifdef DEBUG_EXT_FLASH
|
||||
wolfBoot_printf("erase page %d, status %x\n", page, status);
|
||||
#endif
|
||||
(void)status;
|
||||
/* DQ0 set = erase failed, DQ7 clear = write protected: the block
|
||||
* did not erase. Stop; erasing the same block fails the same way. */
|
||||
if ((status & NAND_STATUS_FAIL) || !(status & NAND_STATUS_WP_N)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
address += block_size;
|
||||
len -= block_size;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1703,8 +1703,8 @@ void hal_prepare_boot(void)
|
|||
* profile when chasing VxWorks 7 64-bit silent boot:
|
||||
* - DUART1 MCR = 3 (DTR+RTS asserted; U-Boot sets this, our driver
|
||||
* leaves it at the post-reset 0)
|
||||
* - TCR = 0x04000000 (matches U-Boot's leftover; wolfBoot was clearing
|
||||
* it; VxWorks 7 BSP early code may inherit) */
|
||||
* - TCR = 0 (matches CW U-Boot's pre-bootm value; a nonzero WRC would let
|
||||
* the watchdog fire silently after VxWorks starts) */
|
||||
void RAMFUNCTION hal_flash_cache_disable_pre_os(void)
|
||||
{
|
||||
hal_flash_cache_disable();
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ int pic32_flash_write(uint32_t address, const uint8_t *data, int len)
|
|||
_addr = pic32_addr_dqword_align(address);
|
||||
/* Setup an aligned buffer with the following rules:
|
||||
* - For addresses outside the writing range: 0xFF (no change)
|
||||
* - For addresses inside the writing range: data | !current_data
|
||||
* - For addresses inside the writing range: data | ~current_data
|
||||
*
|
||||
* This approach ensures we only flip bits from 1 to 0 when writing
|
||||
* without an erase operation. When the address is aligned and length
|
||||
|
|
|
|||
|
|
@ -519,12 +519,13 @@ void hal_prepare_boot(void)
|
|||
WDOG_CNT = WDOG_CNT_UNLOCK;
|
||||
while (!(WDOG_CS & WDOG_CS_ULK)) {}
|
||||
|
||||
/* Enable watchdog with ~2 second timeout (256k ticks at 128kHz LPO)
|
||||
* Application should either service or reconfigure the watchdog
|
||||
*/
|
||||
WDOG_TOVAL = 0xFFFF; /* Max timeout ~512ms without prescaler */
|
||||
/* Enable watchdog with 65535 ticks at 128kHz LPO: ~512ms
|
||||
* without the prescaler, ~131 seconds with the 1:256 prescaler
|
||||
* (WDOG_CS_PRES). Application should either service or
|
||||
* reconfigure the watchdog. */
|
||||
WDOG_TOVAL = 0xFFFF;
|
||||
WDOG_CS = WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_CMD32EN |
|
||||
WDOG_CS_CLK_LPO | WDOG_CS_PRES; /* With prescaler: ~131 sec */
|
||||
WDOG_CS_CLK_LPO | WDOG_CS_PRES;
|
||||
|
||||
/* Wait for reconfiguration to complete */
|
||||
while (!(WDOG_CS & WDOG_CS_RCS)) {}
|
||||
|
|
|
|||
|
|
@ -247,14 +247,15 @@ static int uds_from_uid(uint8_t *out, size_t out_len)
|
|||
|
||||
static int buffer_is_all_value(const uint8_t *buf, size_t len, uint8_t value)
|
||||
{
|
||||
volatile uint8_t diff = 0U;
|
||||
size_t i;
|
||||
|
||||
/* Constant-time scan: the buffer holds the UDS, the DICE root
|
||||
* secret, so the loop must not early-exit on a data-dependent byte. */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buf[i] != value) {
|
||||
return 0;
|
||||
}
|
||||
diff |= (uint8_t)(buf[i] ^ value);
|
||||
}
|
||||
return 1;
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
int hal_uds_derive_key(uint8_t *out, size_t out_len)
|
||||
|
|
|
|||
|
|
@ -133,10 +133,10 @@ extern "C" {
|
|||
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_tpm
|
||||
#endif
|
||||
|
||||
/* Validate sector size is larger than image header size */
|
||||
/* Validate sector size is at least as large as the image header size */
|
||||
#if defined(WOLFBOOT_SECTOR_SIZE) && defined(IMAGE_HEADER_SIZE) && \
|
||||
(WOLFBOOT_SECTOR_SIZE < IMAGE_HEADER_SIZE)
|
||||
#error WOLFBOOT_SECTOR_SIZE must be larger than IMAGE_HEADER_SIZE
|
||||
#error WOLFBOOT_SECTOR_SIZE must be at least as large as IMAGE_HEADER_SIZE
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -1748,6 +1748,11 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
|
|||
/* get header type for image */
|
||||
uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type, uint8_t **ptr);
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
/* Drop the cached external image header so the next open reloads it. */
|
||||
void RAMFUNCTION wolfBoot_invalidate_hdr_cache(void);
|
||||
#endif
|
||||
|
||||
/* Find the key slot ID based on the SHA hash of the key. */
|
||||
int keyslot_id_by_sha(const uint8_t *hint);
|
||||
|
||||
|
|
|
|||
|
|
@ -167,18 +167,17 @@ extern int tolower(int c);
|
|||
|
||||
#ifdef USE_FAST_MATH
|
||||
/* WC_NO_HARDEN suits verify-only builds, which do public-key
|
||||
* operations only. Software DICE (WOLFCRYPT_TZ_PSA without
|
||||
* WOLFBOOT_DICE_HW) signs the attestation claims with the private
|
||||
* IAK, so it is excluded; hardware DICE keeps signing in the crypto
|
||||
* operations only. Secure-mode worlds (TZ_PSA/PKCS11/FWTPM/WOLFHSM)
|
||||
* process private keys in software, so they keep the timing-
|
||||
* resistant TFM path; hardware DICE keeps signing in the crypto
|
||||
* engine and stays verify-only. */
|
||||
# if !defined(WOLFCRYPT_TZ_PSA) || defined(WOLFBOOT_DICE_HW)
|
||||
# define WC_NO_HARDEN
|
||||
# if defined(WOLFCRYPT_SECURE_MODE) && !defined(WOLFBOOT_DICE_HW)
|
||||
# define TFM_TIMING_RESISTANT
|
||||
# else
|
||||
/* tfm.c never tests WC_NO_HARDEN, so dropping it alone changes
|
||||
* no code and only un-silences an advisory that -Werror turns
|
||||
* into a build failure. TFM_TIMING_RESISTANT is what makes
|
||||
* tfm.c constant time. */
|
||||
# define TFM_TIMING_RESISTANT
|
||||
* into a build failure. */
|
||||
# define WC_NO_HARDEN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@ extern "C" {
|
|||
#include "wolfboot/version.h"
|
||||
#include "wolfboot/wc_secure.h"
|
||||
|
||||
/* Partition trailers (magic + state flags) are persisted in flash only when
|
||||
* the target has fixed partitions or supplies a custom trailer backend.
|
||||
* Without either, get/set_trailer_at() are no-op stubs and the
|
||||
* wolfBoot_{get,set}_partition_state() API is absent, so the fallback
|
||||
* decision must be made on version + image validity alone. */
|
||||
#if defined(WOLFBOOT_FIXED_PARTITIONS) || defined(CUSTOM_PARTITION_TRAILER)
|
||||
#define HAVE_PARTITION_TRAILERS 1
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef RAMFUNCTION
|
||||
# if defined(__WOLFBOOT) && defined(RAM_CODE)
|
||||
|
|
|
|||
|
|
@ -925,6 +925,10 @@ static int32_t arm_tee_psa_ps_dispatch(int32_t type, const psa_invec *in_vec,
|
|||
if (data_len > 0 && data == NULL) {
|
||||
return PSA_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
/* Scrub the previous value before overwriting: a SET that stores
|
||||
* less data (or zero) must not leave the tail of the old object
|
||||
* readable via GET. Runs only after every validation check. */
|
||||
wc_ForceZero(entry->data, sizeof(entry->data));
|
||||
if (data_len > 0) {
|
||||
XMEMCPY(entry->data, data, data_len);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ static void mpu_init(void)
|
|||
mpu_setattr(6, MPUSIZE_1G | MPU_RASR_ENABLE | MPU_RASR_ATTR_S |
|
||||
MPU_RASR_ATTR_B | MPU_RASR_ATTR_AP_PRW_UNO | MPU_RASR_ATTR_XN);
|
||||
|
||||
/* System control 0xE0000000:0xEFFFFFF */
|
||||
/* System control 0xE0000000:0xEFFFFFFF (256M) */
|
||||
mpu_setaddr(7, 0xE0000000);
|
||||
mpu_setattr(7, MPUSIZE_256M | MPU_RASR_ENABLE | MPU_RASR_ATTR_S |
|
||||
MPU_RASR_ATTR_B | MPU_RASR_ATTR_AP_PRW_UNO | MPU_RASR_ATTR_XN);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset)
|
|||
* removes the need for a separate LINUX_PAYLOAD switch per target.
|
||||
*
|
||||
* Without MMU there is no DTB to pass, so we fall back to a minimal
|
||||
* handoff (all GPRs cleared) used by targets like sama5d3. */
|
||||
* handoff (r0-r3 cleared) used by targets like sama5d3. */
|
||||
#ifdef MMU
|
||||
register const uint32_t *dts_in = dts_offset;
|
||||
asm volatile (
|
||||
|
|
|
|||
|
|
@ -147,11 +147,10 @@ static int get_top_address(uint64_t *top, struct efi_hob *hoblist)
|
|||
* \brief Change the stack and invoke a function with the new stack.
|
||||
*
|
||||
* This function changes the stack to the specified 'new_stack' value and then
|
||||
* calls the function pointed to by 'other_func', passing the 'ptr' parameter as an argument.
|
||||
* calls the function pointed to by 'other_func'.
|
||||
*
|
||||
* \param new_stack The new stack address.
|
||||
* \param other_func Pointer to the function to be invoked with the new stack.
|
||||
* \param ptr Pointer to the parameter to be passed to the invoked function.
|
||||
*/
|
||||
static void change_stack_and_invoke(uint32_t new_stack,
|
||||
void (*other_func)(void))
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ static int disk_open_mbr(struct disk_drive *drive, const uint8_t *mbr_sector)
|
|||
* @param[in] drv The drive number to open (0 to `MAX_DISKS - 1`).
|
||||
*
|
||||
* @return The number of partitions found and initialized on success, or -1 if
|
||||
* the drive cannot be opened or no valid GPT partition table is found.
|
||||
* the drive cannot be opened or no valid partition table (GPT or MBR) is
|
||||
* found.
|
||||
*/
|
||||
int disk_open(int drv)
|
||||
{
|
||||
|
|
|
|||
14
src/image.c
14
src/image.c
|
|
@ -776,7 +776,7 @@ static void wolfBoot_verify_signature_xmss(uint8_t key_slot,
|
|||
/* Set the public key. */
|
||||
ret = wc_XmssKey_ImportPubRaw(&xmss, pubkey, KEYSTORE_PUBKEY_SIZE);
|
||||
if (ret != 0) {
|
||||
/* Something is wrong with the pub key or LMS parameters. */
|
||||
/* Something is wrong with the pub key or XMSS parameters. */
|
||||
wolfBoot_printf("error: wc_XmssKey_ImportPubRaw" \
|
||||
" returned %d\n", ret);
|
||||
return;
|
||||
|
|
@ -1090,6 +1090,18 @@ static uint8_t *fetch_hdr_cpy(struct wolfBoot_image *img)
|
|||
return hdr_cpy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invalidate the cached external image header.
|
||||
*
|
||||
* fetch_hdr_cpy() loads the header of the first image it sees and serves
|
||||
* it to every later get_header() call. Call this before opening a
|
||||
* different image so TLV lookups do not read the stale header.
|
||||
*/
|
||||
void RAMFUNCTION wolfBoot_invalidate_hdr_cache(void)
|
||||
{
|
||||
hdr_cpy_done = 0;
|
||||
}
|
||||
|
||||
static uint16_t get_header_ext(struct wolfBoot_image *img, uint16_t type,
|
||||
uint8_t **ptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -657,7 +657,7 @@ static void RAMFUNCTION set_partition_magic(uint8_t part)
|
|||
|
||||
|
||||
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
#ifdef HAVE_PARTITION_TRAILERS
|
||||
#ifdef __CCRX__
|
||||
#pragma section FRAM
|
||||
#endif
|
||||
|
|
@ -685,6 +685,11 @@ static void RAMFUNCTION set_partition_state(uint8_t part, uint8_t val)
|
|||
set_trailer_at(part, 1, val);
|
||||
}
|
||||
|
||||
/* Update-sector flag helpers and the fixed-partition APIs below need the
|
||||
* fixed partition addresses and wolfboot_magic_trail, which a
|
||||
* CUSTOM_PARTITION_TRAILER / WOLFBOOT_NO_PARTITIONS build does not define.
|
||||
* The partition state APIs above stay available to custom-trailer builds. */
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
/**
|
||||
* @brief Set the flags of an update sector.
|
||||
*
|
||||
|
|
@ -692,7 +697,6 @@ static void RAMFUNCTION set_partition_state(uint8_t part, uint8_t val)
|
|||
*
|
||||
* @param[in] pos Update sector position.
|
||||
* @param[in] val New flags value to set.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
static void RAMFUNCTION set_update_sector_flags(uint32_t pos, uint8_t val)
|
||||
{
|
||||
|
|
@ -711,6 +715,7 @@ static uint8_t* RAMFUNCTION get_update_sector_flags(uint32_t pos)
|
|||
{
|
||||
return (uint8_t *)get_trailer_at(PART_UPDATE, 2 + pos);
|
||||
}
|
||||
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
/**
|
||||
* @brief Set the state of a partition.
|
||||
|
|
@ -736,6 +741,7 @@ int RAMFUNCTION wolfBoot_set_partition_state(uint8_t part, uint8_t newst)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
/**
|
||||
* @brief Set the flag for sector
|
||||
*
|
||||
|
|
@ -766,6 +772,7 @@ int RAMFUNCTION wolfBoot_set_update_sector_flag(uint16_t sector,
|
|||
set_update_sector_flags(pos, fl_value);
|
||||
return 0;
|
||||
}
|
||||
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
/**
|
||||
* @brief Get the state of a partition.
|
||||
|
|
@ -790,6 +797,7 @@ int RAMFUNCTION wolfBoot_get_partition_state(uint8_t part, uint8_t *st)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
/**
|
||||
* @brief Get the flag for sector
|
||||
*
|
||||
|
|
@ -866,8 +874,8 @@ void RAMFUNCTION wolfBoot_erase_partition(uint8_t part)
|
|||
/**
|
||||
* @brief Update trigger function.
|
||||
*
|
||||
* This function updates the boot partition state to "IMG_STATE_UPDATING".
|
||||
* If the FLAGS_HOME macro is defined, it erases the last sector of the boot
|
||||
* This function sets the update partition state to "IMG_STATE_UPDATING".
|
||||
* If the FLAGS_HOME macro is defined, it erases the last sector of the update
|
||||
* partition before updating the partition state. It also checks FLAGS_UPDATE_EXT
|
||||
* and calls the appropriate flash unlock and lock functions before
|
||||
* updating the partition state.
|
||||
|
|
@ -933,6 +941,8 @@ void RAMFUNCTION wolfBoot_update_trigger(void)
|
|||
}
|
||||
}
|
||||
|
||||
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
/**
|
||||
* @brief Success function.
|
||||
*
|
||||
|
|
@ -960,7 +970,7 @@ void RAMFUNCTION wolfBoot_success(void)
|
|||
#ifdef __CCRX__
|
||||
#pragma section
|
||||
#endif
|
||||
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
||||
#endif /* HAVE_PARTITION_TRAILERS */
|
||||
|
||||
#ifdef WOLFBOOT_PERSIST_FAILURE_STATUS
|
||||
/* Persistent failure diagnostics.
|
||||
|
|
@ -2369,6 +2379,8 @@ static int pkcs11_enc_initialized = 0, pkcs11_dec_initialized = 0;
|
|||
static CK_AES_CTR_PARAMS pkcs11_params;
|
||||
#endif
|
||||
|
||||
static void pkcs11_pin_wipe(void);
|
||||
|
||||
int pkcs11_crypto_init(void)
|
||||
{
|
||||
CK_RV ret = 0;
|
||||
|
|
@ -2462,6 +2474,9 @@ int pkcs11_crypto_init(void)
|
|||
if (pkcs11_initialized) {
|
||||
pkcs11_function_list->C_Finalize(NULL);
|
||||
}
|
||||
/* terminal failure: the credential must not survive in retained
|
||||
* memory (same reason as the deinit wipe) */
|
||||
pkcs11_pin_wipe();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -2831,8 +2846,8 @@ exit:
|
|||
* @brief Read and decrypt data from an external flash.
|
||||
*
|
||||
* This function reads the encrypted data from the external flash,
|
||||
* decrypts it using the AES decryption algorithm, and stores the decrypted data
|
||||
* in the provided buffer.
|
||||
* decrypts it using the configured decryption algorithm (ChaCha20, AES-CTR,
|
||||
* or PKCS#11), and stores the decrypted data in the provided buffer.
|
||||
|
||||
* @param address The address in the external flash to read the encrypted data from.
|
||||
* @param data Pointer to the buffer to store the decrypted data.
|
||||
|
|
@ -2955,7 +2970,8 @@ typedef char wolfBoot_ramboot_blockalign_check[
|
|||
/**
|
||||
* @brief Decrypt data from RAM.
|
||||
*
|
||||
* This function decrypts data from the RAM using the AES decryption algorithm.
|
||||
* This function decrypts data from the RAM using the configured decryption
|
||||
* algorithm (ChaCha20, AES-CTR, or PKCS#11).
|
||||
*
|
||||
* @param src Pointer to the source buffer containing the encrypted data.
|
||||
* @param dst Pointer to the destination buffer to store the decrypted data.
|
||||
|
|
|
|||
|
|
@ -491,9 +491,9 @@ static uint32_t sdhci_set_clock(uint32_t clock_khz)
|
|||
base_clk_khz = sdhci_platform_set_clock(clock_khz, base_clk_khz);
|
||||
if (base_clk_khz == 0) {
|
||||
/* No usable base clock. The SD clock was already disabled above, so
|
||||
* the controller is left idle. NOTE: 0 is also what the "clock already
|
||||
* set" path above returns, so callers cannot currently tell these
|
||||
* apart - see the DEBUG_SDHCI log for which one happened. */
|
||||
* the controller is left idle. This path returns 0 (error), unlike
|
||||
* the "clock already set" path above which returns last_clock_khz,
|
||||
* so a 0 return is unambiguously an error for callers. */
|
||||
#ifdef DEBUG_SDHCI
|
||||
wolfBoot_printf("sdhci_set_clock: no usable base clock "
|
||||
"(CAPS and platform hook both 0)\n");
|
||||
|
|
@ -1170,7 +1170,7 @@ static int emmc_send_op_cond(uint32_t ocr_arg, uint32_t *ocr_reg)
|
|||
|
||||
response = SDHCI_REG(SDHCI_SRS04);
|
||||
|
||||
/* Check if device is ready (busy bit cleared = ready) */
|
||||
/* Check if device is ready (OCR bit 31 set = ready) */
|
||||
if (response & MMC_OCR_BUSY_BIT) {
|
||||
/* Device is ready */
|
||||
if (ocr_reg != NULL) {
|
||||
|
|
|
|||
|
|
@ -336,7 +336,7 @@ static int self_sha384(uint8_t *hash)
|
|||
* TPM2_PCR_Extend. Optionally, if DEBUG_WOLFTPM or WOLFBOOT_DEBUG_TPM defined,
|
||||
* prints debug info.
|
||||
*
|
||||
* @param[in] pcrIndex The PCR Index (0-24 is valid range).
|
||||
* @param[in] pcrIndex The PCR Index (0-23 is valid range).
|
||||
* @param[in] hash Pointer to the hash value to extend into the PCR.
|
||||
* @param[in] line Line number where the function is called (for debugging).
|
||||
* @return 0 on success, an error code on failure.
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
#endif
|
||||
uint32_t *load_address = NULL;
|
||||
uint32_t *source_address = NULL;
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
#ifdef HAVE_PARTITION_TRAILERS
|
||||
uint8_t p_state;
|
||||
#endif
|
||||
#if defined(MMU) || defined(WOLFBOOT_FDT)
|
||||
|
|
@ -314,15 +314,15 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
* kernel directly. */
|
||||
uintptr_t bl31_entry = 0;
|
||||
#endif
|
||||
#if !defined(ALLOW_DOWNGRADE) && defined(WOLFBOOT_FIXED_PARTITIONS)
|
||||
uint32_t boot_v = wolfBoot_current_firmware_version();
|
||||
uint32_t update_v = wolfBoot_update_firmware_version();
|
||||
uint32_t max_v = (boot_v > update_v) ? boot_v : update_v;
|
||||
#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
memset(&os_image, 0, sizeof(struct wolfBoot_image));
|
||||
|
||||
for (;;) {
|
||||
/* Each open needs fresh image state: wolfBoot_open_image_address()
|
||||
* adopts load_address only when hdr is NULL, and the external
|
||||
* header cache keeps the first image opened, so without this the
|
||||
* fallback re-verifies the previous partition's header. */
|
||||
memset(&os_image, 0, sizeof(struct wolfBoot_image));
|
||||
#ifdef EXT_FLASH
|
||||
wolfBoot_invalidate_hdr_cache();
|
||||
#endif
|
||||
#if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS)
|
||||
if (active < 0)
|
||||
active = wolfBoot_dualboot_candidate();
|
||||
|
|
@ -343,17 +343,6 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
wolfBoot_panic();
|
||||
break;
|
||||
}
|
||||
#if !defined(ALLOW_DOWNGRADE) && defined(WOLFBOOT_FIXED_PARTITIONS)
|
||||
{
|
||||
uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v;
|
||||
if ((max_v > 0U) && (active_v < max_v)) {
|
||||
wolfBoot_printf("Rollback to lower version not allowed\n");
|
||||
wolfBoot_panic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */
|
||||
|
||||
#if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS)
|
||||
wolfBoot_printf("Trying %s partition at %p\n",
|
||||
active == PART_BOOT ? "Boot" : "Update", source_address);
|
||||
|
|
@ -463,7 +452,7 @@ backup_on_failure:
|
|||
/* First time we boot this update, set to TESTING to await
|
||||
* confirmation from the system
|
||||
*/
|
||||
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
||||
#ifdef HAVE_PARTITION_TRAILERS
|
||||
if ((wolfBoot_get_partition_state(active, &p_state) == 0) &&
|
||||
(p_state == IMG_STATE_UPDATING))
|
||||
{
|
||||
|
|
@ -792,11 +781,10 @@ backup_on_failure:
|
|||
#endif /* MMU */
|
||||
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
/* Enter the uImage at ih_ep. Skipped if a later stage (ELF/FIT) re-derived
|
||||
* the load address, since that stage provides its own entry point. The
|
||||
* flag is tracked explicitly rather than by comparing load_address:
|
||||
* elf_load_image_mmu() publishes its entry point before it finishes
|
||||
* validating, so a rejected ELF also leaves load_address rewritten. */
|
||||
/* Enter the uImage at ih_ep. Skipped if a later stage (ELF/FIT) succeeded
|
||||
* and re-derived the load address, since that stage provides its own
|
||||
* entry point. Tracked with an explicit flag set on each stage's success
|
||||
* path rather than by comparing load_address. */
|
||||
if ((uboot_entry != NULL) && !stage_entry_override) {
|
||||
load_address = uboot_entry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,9 +61,16 @@ struct ata_async_info{
|
|||
int in_progress;
|
||||
int drv;
|
||||
int slot;
|
||||
/* 1 when the in-flight async command carries a passphrase in the
|
||||
* static DMA buffer and it must be scrubbed at completion. */
|
||||
int scrub_buffer;
|
||||
};
|
||||
|
||||
static struct ata_async_info ata_async_info;
|
||||
|
||||
#ifdef WOLFBOOT_ATA_DISK_LOCK
|
||||
static void ata_security_buffer_zeroize(void);
|
||||
#endif
|
||||
/**
|
||||
* @brief This structure holds the necessary information for an ATA drive,
|
||||
* including AHCI base address, AHCI port number, and sector cache.
|
||||
|
|
@ -286,21 +293,39 @@ int ata_cmd_complete_async()
|
|||
{
|
||||
struct ata_drive *ata;
|
||||
int slot;
|
||||
int ret;
|
||||
|
||||
if (!ata_async_info.in_progress)
|
||||
return ATA_ERR_OP_NOT_IN_PROGRESS;
|
||||
ata = &ATA_Drv[ata_async_info.drv];
|
||||
slot = ata_async_info.slot;
|
||||
if (mmio_read32(AHCI_PxIS(ata->ahci_base, ata->ahci_port)) & AHCI_PORT_IS_TFES) {
|
||||
ata_async_info.in_progress = 0;
|
||||
return -1;
|
||||
/* Task-file error: verify the HBA has retired the command (PxCI
|
||||
* clear) before scrubbing the DMA buffer it may still reference. */
|
||||
if ((mmio_read32(AHCI_PxCI(ata->ahci_base, ata->ahci_port)) &
|
||||
(1 << slot)) != 0)
|
||||
return ATA_ERR_BUSY;
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
slot = ata_async_info.slot;
|
||||
if ((mmio_read32(AHCI_PxCI(ata->ahci_base, ata->ahci_port)) & (1 << slot)) != 0)
|
||||
if ((mmio_read32(AHCI_PxCI(ata->ahci_base, ata->ahci_port)) &
|
||||
(1 << slot)) != 0)
|
||||
return ATA_ERR_BUSY;
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
/* The HBA has retired the command (success or task-file error), so
|
||||
* the static DMA buffer is no longer in flight: scrub the passphrase
|
||||
* it carried before it can be read back from SRAM. */
|
||||
ata_async_info.in_progress = 0;
|
||||
return 0;
|
||||
#ifdef WOLFBOOT_ATA_DISK_LOCK
|
||||
if (ata_async_info.scrub_buffer) {
|
||||
ata_async_info.scrub_buffer = 0;
|
||||
ata_security_buffer_zeroize();
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -472,7 +497,14 @@ static int security_command_passphrase(int drv, uint8_t ata_cmd,
|
|||
struct ata_drive *ata = &ATA_Drv[drv];
|
||||
size_t passphrase_len = 0;
|
||||
int ret;
|
||||
int slot = prepare_cmd_h2d_slot(drv, buffer,
|
||||
int slot;
|
||||
|
||||
/* A second security command must not touch the shared buffer while
|
||||
* an async transfer is in flight: prepare_cmd_h2d_slot() and the
|
||||
* memcpy below would clobber the passphrase still being DMA'd. */
|
||||
if (ata_async_info.in_progress)
|
||||
return ATA_ERR_OP_IN_PROGRESS;
|
||||
slot = prepare_cmd_h2d_slot(drv, buffer,
|
||||
ATA_SECURITY_COMMAND_LEN, 1);
|
||||
memset(buffer, 0, ATA_SECURITY_COMMAND_LEN);
|
||||
if (master)
|
||||
|
|
@ -502,8 +534,12 @@ static int security_command_passphrase(int drv, uint8_t ata_cmd,
|
|||
* may still be in flight when we return (the caller polls completion
|
||||
* via ata_cmd_complete_async()), so clearing the buffer now would race
|
||||
* the HBA and could corrupt the command still in progress. */
|
||||
if (!async)
|
||||
if (!async) {
|
||||
ata_security_buffer_zeroize();
|
||||
} else {
|
||||
/* Command is in flight: scrub once the HBA retires it. */
|
||||
ata_async_info.scrub_buffer = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,15 @@ static int exportPubKey = 0;
|
|||
static WC_RNG rng;
|
||||
static int noLocalKeys = 0;
|
||||
|
||||
/* Exit after the RNG has been initialised: free the DRBG state first so
|
||||
* it is not left resident in process memory, then terminate. */
|
||||
static void keygen_die(int code)
|
||||
{
|
||||
wc_FreeRng(&rng);
|
||||
wc_ForceZero(&rng, sizeof(rng));
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* ML-DSA pub keys are big. */
|
||||
#define KEYSLOT_MAX_PUBKEY_SIZE ML_DSA_L5_PUBKEY_SIZE
|
||||
|
||||
|
|
@ -567,7 +576,7 @@ static void keygen_rsa(const char *keyfile, int kbits, uint32_t id_mask,
|
|||
ret = wc_InitRsaKey(&k, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Unable to initialize RSA%d key\n", kbits);
|
||||
exit(1);
|
||||
keygen_die(1);
|
||||
}
|
||||
rsa_init = 1;
|
||||
|
||||
|
|
@ -616,7 +625,7 @@ cleanup:
|
|||
wc_FreeRsaKey(&k);
|
||||
wc_ForceZero(&k, sizeof(k));
|
||||
if (exit_code != 0)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
#define MAX_ECC_KEY_SIZE 66
|
||||
|
|
@ -731,7 +740,7 @@ cleanup:
|
|||
wc_ForceZero(priv_der, sizeof(priv_der));
|
||||
|
||||
if (exit_code != 0)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
|
||||
memcpy(k_buffer, Qx, ecc_key_size);
|
||||
memcpy(k_buffer + ecc_key_size, Qy, ecc_key_size);
|
||||
|
|
@ -803,7 +812,7 @@ cleanup:
|
|||
wc_ed25519_free(&k);
|
||||
wc_ForceZero(&k, sizeof(k));
|
||||
if (exit_code != 0)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
static void keygen_ed448(const char *privkey, uint32_t id_mask)
|
||||
|
|
@ -864,7 +873,7 @@ cleanup:
|
|||
wc_ed448_free(&k);
|
||||
wc_ForceZero(&k, sizeof(k));
|
||||
if (exit_code != 0)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
#include "../lms/lms_common.h"
|
||||
|
|
@ -986,7 +995,7 @@ cleanup:
|
|||
wc_ForceZero(&key, sizeof(key));
|
||||
}
|
||||
if (exit_code)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
#include "../xmss/xmss_common.h"
|
||||
|
|
@ -1105,7 +1114,7 @@ cleanup:
|
|||
wc_ForceZero(&key, sizeof(key));
|
||||
}
|
||||
if (exit_code)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1316,7 +1325,7 @@ cleanup:
|
|||
priv = NULL;
|
||||
}
|
||||
if (exit_code != 0)
|
||||
exit(exit_code);
|
||||
keygen_die(exit_code);
|
||||
}
|
||||
|
||||
static void key_gen_check(const char *kfilename)
|
||||
|
|
|
|||
|
|
@ -1218,13 +1218,15 @@ static int sign_digest(int sign, int hash_algo,
|
|||
mgf = WC_MGF1SHA384;
|
||||
} else {
|
||||
fprintf(stderr, "RSA-PSS requires SHA-256 or SHA-384\n");
|
||||
return -1;
|
||||
ret = -1;
|
||||
}
|
||||
ret = wc_RsaPSS_Sign(digest, digest_sz, signature, *signature_sz,
|
||||
hash_type, mgf, &k->rsa, &rng);
|
||||
if (ret > 0) {
|
||||
*signature_sz = ret;
|
||||
ret = 0;
|
||||
if (ret == 0) {
|
||||
ret = wc_RsaPSS_Sign(digest, digest_sz, signature, *signature_sz,
|
||||
hash_type, mgf, &k->rsa, &rng);
|
||||
if (ret > 0) {
|
||||
*signature_sz = ret;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -204,6 +204,11 @@ exit:
|
|||
wolfTPM2_UnloadHandle(&dev, &tpmSession.handle);
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
|
||||
/* Scrub the NV auth copy and the session state from the stack on
|
||||
* every exit path. */
|
||||
wc_ForceZero(&nv, sizeof(nv));
|
||||
wc_ForceZero(&tpmSession, sizeof(tpmSession));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ CFLAGS+=-ftest-coverage
|
|||
CFLAGS+=--coverage
|
||||
CFLAGS+=-DUNIT_TEST_COVERAGE
|
||||
CFLAGS+=-DUNIT_TEST -DWOLFSSL_USER_SETTINGS
|
||||
# Pin the standard: GCC 14+ defaults to gnu23, where glibc string.h defines
|
||||
# memchr/memcpy as _Generic macros that clash with wolfBoot's own declarations
|
||||
# in tests that #include a .c file. gnu17 matches the CI toolchain default.
|
||||
CFLAGS+=-std=gnu17
|
||||
LDFLAGS+=-fprofile-arcs
|
||||
LDFLAGS+=-ftest-coverage
|
||||
|
||||
|
|
@ -62,7 +66,7 @@ TESTS:=unit-parser unit-parser-large-header unit-fdt unit-extflash unit-string \
|
|||
unit-enc-nvm-flagshome unit-delta unit-gzip unit-update-flash unit-update-flash-delta \
|
||||
unit-update-flash-hook \
|
||||
unit-update-flash-self-update \
|
||||
unit-update-flash-enc unit-update-flash-enc-full unit-update-ram unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-noramboot unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
|
||||
unit-update-flash-enc unit-update-flash-enc-full unit-update-ram unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-nofixed-noramboot unit-update-ram-noramboot unit-update-ram-custom-trailer unit-custom-trailer-nopart unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
|
||||
unit-update-disk unit-update-disk-fsp unit-update-disk-oob unit-update-disk-fit unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
|
||||
unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-image-dts \
|
||||
unit-image-dts-sha384 unit-image-dts-sha3-384 unit-store-sbrk \
|
||||
|
|
@ -336,6 +340,37 @@ unit-update-ram-nofixed:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN \
|
|||
-DWOLFBOOT_RAMBOOT_MAX_SIZE=WOLFBOOT_PARTITION_SIZE \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \
|
||||
-DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# F-13604: same non-fixed-partition layout as unit-update-ram-nofixed but
|
||||
# without NO_XIP, so WOLFBOOT_USE_RAMBOOT stays off and
|
||||
# wolfBoot_open_image_address() runs with a varying load_address on every
|
||||
# retry iteration (the A/B fallback path that must re-open the second
|
||||
# partition instead of re-verifying the stale header of the first).
|
||||
unit-update-ram-nofixed-noramboot:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN \
|
||||
-DUNIT_TEST_AUTH -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH \
|
||||
-DPART_UPDATE_EXT -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT \
|
||||
-DWOLFBOOT_NO_PARTITIONS -DUNIT_TEST_NO_FIXED_PARTITIONS \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \
|
||||
-DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# CUSTOM_PARTITION_TRAILER: covers the HAVE_PARTITION_TRAILERS path where
|
||||
# partition state is managed via externally-defined get/set_trailer_at.
|
||||
unit-update-ram-custom-trailer:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN \
|
||||
-DUNIT_TEST_AUTH -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH \
|
||||
-DPART_UPDATE_EXT -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT \
|
||||
-DWOLFBOOT_FIXED_PARTITIONS \
|
||||
-DCUSTOM_PARTITION_TRAILER \
|
||||
-DWOLFBOOT_RAMBOOT_MAX_SIZE=WOLFBOOT_PARTITION_SIZE \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \
|
||||
-DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# CUSTOM_PARTITION_TRAILER + WOLFBOOT_NO_PARTITIONS: no WOLFBOOT_FIXED_PARTITIONS,
|
||||
# so wolfboot_magic_trail and the fixed partition addresses are excluded from
|
||||
# libwolfboot.c. Proves the partition state API compiles and works through the
|
||||
# custom get/set_trailer_at backend without fixed partitions (F-1130279967).
|
||||
unit-custom-trailer-nopart:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN \
|
||||
-DUNIT_TEST_AUTH -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED \
|
||||
-DWOLFBOOT_NO_PARTITIONS \
|
||||
-DCUSTOM_PARTITION_TRAILER \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \
|
||||
-DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# Bound the non-FSP disk load to this test's 64-byte load_buffer (TEST_PAYLOAD_SIZE),
|
||||
# the cap update_disk.c now requires; all images here are exactly that size.
|
||||
unit-update-disk:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_RAMBOOT_MAX_SIZE=0x40 \
|
||||
|
|
@ -912,9 +947,18 @@ unit-update-ram-enc-nopart: ../../include/target.h unit-update-ram-enc.c
|
|||
unit-update-ram-nofixed: ../../include/target.h unit-update-ram-nofixed.c
|
||||
gcc -o $@ unit-update-ram-nofixed.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-ram-nofixed-noramboot: ../../include/target.h unit-update-ram-nofixed-noramboot.c
|
||||
gcc -o $@ unit-update-ram-nofixed-noramboot.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-ram-noramboot: ../../include/target.h unit-update-ram-noramboot.c
|
||||
gcc -o $@ unit-update-ram-noramboot.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-ram-custom-trailer: ../../include/target.h unit-update-ram-custom-trailer.c
|
||||
gcc -o $@ unit-update-ram-custom-trailer.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-custom-trailer-nopart: ../../include/target.h unit-custom-trailer-nopart.c
|
||||
gcc -o $@ unit-custom-trailer-nopart.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-flash-hwswap: ../../include/target.h unit-update-flash-hwswap.c
|
||||
gcc -o $@ unit-update-flash-hwswap.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
@ -1318,6 +1362,8 @@ p1021_erase_extract.h: ../../hal/nxp_p1021.c
|
|||
sed -n '/#define NAND_CMD_BLOCK_ERASE1 /p' $< >> $@
|
||||
sed -n '/#define NAND_CMD_BLOCK_ERASE2 /p' $< >> $@
|
||||
sed -n '/#define FLASH_PAGE_SIZE /p' $< >> $@
|
||||
sed -n '/#define NAND_STATUS_WP_N /p' $< >> $@
|
||||
sed -n '/#define NAND_STATUS_FAIL /p' $< >> $@
|
||||
|
||||
p1021_erase_fn_extract.h: ../../hal/nxp_p1021.c
|
||||
sed -n '/^int ext_flash_erase/,/^}/p' $< > $@
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <x86/ahci.h>
|
||||
#include <x86/ata.h>
|
||||
|
||||
#define WOLFBOOT_ATA_DISK_LOCK
|
||||
|
|
@ -25,10 +26,18 @@
|
|||
* slot" error path. */
|
||||
static int mock_slots_full;
|
||||
|
||||
/* When set, port IS reads report a task-file error so
|
||||
* ata_cmd_complete_async() takes its error exit. */
|
||||
static int mock_tfes;
|
||||
|
||||
uint32_t mmio_read32(uintptr_t address)
|
||||
{
|
||||
(void)address;
|
||||
return mock_slots_full ? 0xFFFFFFFF : 0;
|
||||
if (mock_slots_full)
|
||||
return 0xFFFFFFFF;
|
||||
if (mock_tfes)
|
||||
return AHCI_PORT_IS_TFES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mmio_write32(uintptr_t address, uint32_t value)
|
||||
|
|
@ -54,6 +63,7 @@ static uint8_t *ctable_mem;
|
|||
static void setup(void)
|
||||
{
|
||||
mock_slots_full = 0;
|
||||
mock_tfes = 0;
|
||||
clb_mem = mmap(NULL, sizeof(struct hba_cmd_header) * 32,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||
|
|
@ -120,6 +130,45 @@ START_TEST(test_unlock_zeroizes_passphrase_on_no_free_slot)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_unit_async_zeroizes_on_completion)
|
||||
{
|
||||
static const char passphrase[] = "unit-test-disk-secret";
|
||||
volatile uint8_t *pw =
|
||||
(volatile uint8_t *)buffer + ATA_SECURITY_PASSWORD_OFFSET;
|
||||
int r;
|
||||
int i;
|
||||
|
||||
r = ata_security_erase_unit(0, passphrase, 0);
|
||||
ck_assert_int_eq(r, ATA_ERR_BUSY);
|
||||
|
||||
/* Command is in flight: scrubbing now would race the HBA DMA, so
|
||||
* the passphrase must still be present until completion. */
|
||||
for (i = 0; i < (int)strlen(passphrase); i++)
|
||||
ck_assert_uint_eq(pw[i], (uint8_t)passphrase[i]);
|
||||
|
||||
r = ata_cmd_complete_async();
|
||||
ck_assert_int_eq(r, 0);
|
||||
|
||||
assert_password_field_zero("after async SECURITY ERASE UNIT completion");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_erase_unit_async_zeroizes_on_port_error)
|
||||
{
|
||||
static const char passphrase[] = "unit-test-disk-secret";
|
||||
int r;
|
||||
|
||||
r = ata_security_erase_unit(0, passphrase, 0);
|
||||
ck_assert_int_eq(r, ATA_ERR_BUSY);
|
||||
|
||||
mock_tfes = 1;
|
||||
r = ata_cmd_complete_async();
|
||||
ck_assert_int_eq(r, -1);
|
||||
|
||||
assert_password_field_zero("after async port-error completion");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *ata_security_passphrase_zeroize_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("ata_security_passphrase_zeroize");
|
||||
|
|
@ -127,6 +176,8 @@ static Suite *ata_security_passphrase_zeroize_suite(void)
|
|||
tcase_add_checked_fixture(tc, setup, teardown);
|
||||
tcase_add_test(tc, test_unlock_zeroizes_passphrase_after_command_completes);
|
||||
tcase_add_test(tc, test_unlock_zeroizes_passphrase_on_no_free_slot);
|
||||
tcase_add_test(tc, test_erase_unit_async_zeroizes_on_completion);
|
||||
tcase_add_test(tc, test_erase_unit_async_zeroizes_on_port_error);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
/* unit-custom-trailer-nopart.c
|
||||
*
|
||||
* Compile + behaviour gate for the CUSTOM_PARTITION_TRAILER /
|
||||
* WOLFBOOT_NO_PARTITIONS configuration: no WOLFBOOT_FIXED_PARTITIONS, so
|
||||
* wolfboot_magic_trail and the fixed partition addresses are excluded from
|
||||
* libwolfboot.c. The partition state API must still compile and work through
|
||||
* the externally-defined get/set_trailer_at backend, while the
|
||||
* fixed-partition functions (sector flags, erase, trigger, success) are
|
||||
* absent.
|
||||
*/
|
||||
#ifndef WOLFBOOT_HASH_SHA256
|
||||
#define WOLFBOOT_HASH_SHA256
|
||||
#endif
|
||||
|
||||
#define NO_FORK 1
|
||||
|
||||
#include <check.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "target.h"
|
||||
|
||||
#include "user_settings.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
/* Custom partition trailer backend (mocked).
|
||||
* Layout per partition: [state(1)][magic(4)]
|
||||
* get_trailer_at(part, 0) -> magic (base+1 .. base+4)
|
||||
* get_trailer_at(part, 1) -> state (base+0)
|
||||
*/
|
||||
static uint8_t mock_trailer_boot[5];
|
||||
static uint8_t mock_trailer_update[5];
|
||||
|
||||
uint8_t* get_trailer_at(uint8_t part, uint32_t at)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot
|
||||
: mock_trailer_update;
|
||||
if (at == 0)
|
||||
return &base[1];
|
||||
return &base[at - 1];
|
||||
}
|
||||
|
||||
void set_trailer_at(uint8_t part, uint32_t at, uint8_t val)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot
|
||||
: mock_trailer_update;
|
||||
if (at == 0)
|
||||
base[1] = val;
|
||||
else
|
||||
base[at - 1] = val;
|
||||
}
|
||||
|
||||
void set_partition_magic(uint8_t part)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot
|
||||
: mock_trailer_update;
|
||||
/* WOLFBOOT_MAGIC_TRAIL = 0x544F4F42 on LE: 'B','O','O','T' */
|
||||
base[1] = 'B';
|
||||
base[2] = 'O';
|
||||
base[3] = 'O';
|
||||
base[4] = 'T';
|
||||
(void)part;
|
||||
}
|
||||
|
||||
#include "libwolfboot.c"
|
||||
#include "unit-mock-flash.c"
|
||||
|
||||
static void reset_trailers(void)
|
||||
{
|
||||
memset(mock_trailer_boot, 0, sizeof(mock_trailer_boot));
|
||||
memset(mock_trailer_update, 0, sizeof(mock_trailer_update));
|
||||
}
|
||||
|
||||
/* State API round-trips through the custom backend with no fixed
|
||||
* partitions present. */
|
||||
START_TEST(test_set_get_partition_state)
|
||||
{
|
||||
uint8_t st = 0;
|
||||
|
||||
reset_trailers();
|
||||
|
||||
/* No magic yet: set writes the magic then the state. */
|
||||
ck_assert_int_eq(wolfBoot_set_partition_state(PART_BOOT,
|
||||
IMG_STATE_TESTING), 0);
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &st), 0);
|
||||
ck_assert_uint_eq(st, IMG_STATE_TESTING);
|
||||
|
||||
/* Update partition is independent. */
|
||||
ck_assert_int_eq(wolfBoot_set_partition_state(PART_UPDATE,
|
||||
IMG_STATE_UPDATING), 0);
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_UPDATE, &st), 0);
|
||||
ck_assert_uint_eq(st, IMG_STATE_UPDATING);
|
||||
|
||||
/* Writing UPDATE must not clobber BOOT: re-read BOOT and confirm it
|
||||
* still holds TESTING, not the UPDATE value. */
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &st), 0);
|
||||
ck_assert_uint_eq(st, IMG_STATE_TESTING);
|
||||
|
||||
/* PART_NONE is rejected. */
|
||||
ck_assert_int_eq(wolfBoot_set_partition_state(PART_NONE, 0), -1);
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_NONE, &st), -1);
|
||||
|
||||
/* get on a partition without magic is rejected. */
|
||||
reset_trailers();
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &st), -1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int failed;
|
||||
Suite *s;
|
||||
TCase *tc;
|
||||
SRunner *sr;
|
||||
|
||||
s = suite_create("custom-trailer-nopart");
|
||||
tc = tcase_create("state-api");
|
||||
|
||||
tcase_add_checked_fixture(tc, reset_trailers, NULL);
|
||||
tcase_add_test(tc, test_set_get_partition_state);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
sr = srunner_create(s);
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (failed == 0) ? 0 : 1;
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
static const char *mock_xmss_param;
|
||||
static int mock_exit_code;
|
||||
static jmp_buf mock_exit_env;
|
||||
static int mock_force_zero_count;
|
||||
static int mock_free_rng_count;
|
||||
|
||||
static void mock_exit(int code);
|
||||
|
||||
|
|
@ -22,8 +24,10 @@ static void mock_exit(int code);
|
|||
#define wc_XmssKey_ExportPubRaw mock_wc_XmssKey_ExportPubRaw
|
||||
#define wc_XmssKey_Free mock_wc_XmssKey_Free
|
||||
#define wc_ForceZero mock_wc_ForceZero
|
||||
#define wc_FreeRng mock_wc_FreeRng
|
||||
#include "../keytools/keygen.c"
|
||||
#undef wc_ForceZero
|
||||
#undef wc_FreeRng
|
||||
#undef wc_XmssKey_Free
|
||||
#undef wc_XmssKey_ExportPubRaw
|
||||
#undef wc_XmssKey_GetPrivLen
|
||||
|
|
@ -115,12 +119,22 @@ void mock_wc_ForceZero(void *mem, size_t len)
|
|||
{
|
||||
(void)mem;
|
||||
(void)len;
|
||||
mock_force_zero_count++;
|
||||
}
|
||||
|
||||
int mock_wc_FreeRng(WC_RNG *rng)
|
||||
{
|
||||
(void)rng;
|
||||
mock_free_rng_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
mock_xmss_param = NULL;
|
||||
mock_exit_code = 0;
|
||||
mock_force_zero_count = 0;
|
||||
mock_free_rng_count = 0;
|
||||
unsetenv("XMSS_PARAMS");
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +154,11 @@ static void run_keygen_xmss(void)
|
|||
|
||||
ck_assert_int_eq(jumped, 1);
|
||||
ck_assert_int_eq(mock_exit_code, 1);
|
||||
/* The RNG must be freed (wc_FreeRng) and zeroized (wc_ForceZero) after
|
||||
* key generation (F-12883). keygen_die() frees+zeroizes the RNG; the
|
||||
* key is also zeroized in the caller's cleanup. */
|
||||
ck_assert_int_ge(mock_force_zero_count, 1);
|
||||
ck_assert_int_eq(mock_free_rng_count, 1);
|
||||
}
|
||||
|
||||
START_TEST(test_keygen_xmss_uses_env_param_when_set)
|
||||
|
|
|
|||
|
|
@ -351,6 +351,11 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
int mock_ext_flash_short_len = 0;
|
||||
int mock_ext_flash_short_bytes = 0;
|
||||
|
||||
/* Records the source address of the largest ext_flash_read() call (the
|
||||
* image load to RAM), so a test can verify which partition was booted. */
|
||||
uintptr_t mock_max_read_addr = 0;
|
||||
int mock_max_read_len = 0;
|
||||
|
||||
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -359,6 +364,10 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
|||
|
||||
if (mock_ext_flash_short_len == len && mock_ext_flash_short_bytes > 0)
|
||||
ret = len - mock_ext_flash_short_bytes;
|
||||
if (ret > mock_max_read_len) {
|
||||
mock_max_read_len = ret;
|
||||
mock_max_read_addr = address;
|
||||
}
|
||||
for (i = 0; i < ret; i++) {
|
||||
data[i] = a[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,12 +45,21 @@ static int g_pages[MAX_TRACKED_PAGES];
|
|||
static int g_page_calls;
|
||||
static int g_cmd_calls;
|
||||
static int g_cmd_ret;
|
||||
static uint32_t g_status;
|
||||
|
||||
static void mock_reset(int cmd_ret)
|
||||
/* ONFI status byte: DQ0 set = program/erase fail, DQ7 clear = protected.
|
||||
* 0x80 is a clean success (no fail, not protected); 0x81 is an
|
||||
* erase/program failure (DQ0 set); 0x00 is write-protected (DQ7 clear). */
|
||||
#define STATUS_OK 0x80
|
||||
#define STATUS_ERASE_FAIL 0x81
|
||||
#define STATUS_WP_PROTECTED 0x00
|
||||
|
||||
static void mock_reset(int cmd_ret, uint32_t status)
|
||||
{
|
||||
g_page_calls = 0;
|
||||
g_cmd_calls = 0;
|
||||
g_cmd_ret = cmd_ret;
|
||||
g_status = status;
|
||||
}
|
||||
|
||||
static void hal_flash_set_addr(int page, int col)
|
||||
|
|
@ -81,7 +90,7 @@ static void set32(volatile unsigned int *addr, unsigned int val)
|
|||
static uint32_t get32(volatile unsigned int *addr)
|
||||
{
|
||||
(void)addr;
|
||||
return 0; /* MDR status: no error */
|
||||
return g_status; /* MDR: the NAND status byte */
|
||||
}
|
||||
|
||||
/* The real ext_flash_erase() from hal/nxp_p1021.c (extracted). */
|
||||
|
|
@ -96,7 +105,7 @@ START_TEST (test_erase_advances_through_blocks)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mock_reset(0);
|
||||
mock_reset(0, STATUS_OK);
|
||||
|
||||
ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE);
|
||||
|
||||
|
|
@ -112,7 +121,40 @@ START_TEST (test_erase_stops_on_command_error)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mock_reset(-1);
|
||||
mock_reset(-1, STATUS_OK);
|
||||
|
||||
ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE);
|
||||
|
||||
ck_assert_int_eq(ret, -1);
|
||||
ck_assert_int_eq(g_page_calls, 1);
|
||||
ck_assert_int_eq(g_pages[0], 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_erase_stops_on_status_fail)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The command sequence completes (cmd_ret 0) but the NAND reports an
|
||||
* erase failure in the status byte (DQ0 set). */
|
||||
mock_reset(0, STATUS_ERASE_FAIL);
|
||||
|
||||
ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE);
|
||||
|
||||
ck_assert_int_eq(ret, -1);
|
||||
ck_assert_int_eq(g_page_calls, 1);
|
||||
ck_assert_int_eq(g_pages[0], 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_erase_stops_on_write_protected)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The command sequence completes (cmd_ret 0) but the NAND reports the
|
||||
* block as write-protected in the status byte (DQ7 clear). The erase
|
||||
* must fail and not advance to the next block. */
|
||||
mock_reset(0, STATUS_WP_PROTECTED);
|
||||
|
||||
ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE);
|
||||
|
||||
|
|
@ -129,6 +171,8 @@ Suite *p1021_erase_suite(void)
|
|||
|
||||
tcase_add_test(tc, test_erase_advances_through_blocks);
|
||||
tcase_add_test(tc, test_erase_stops_on_command_error);
|
||||
tcase_add_test(tc, test_erase_stops_on_status_fail);
|
||||
tcase_add_test(tc, test_erase_stops_on_write_protected);
|
||||
tcase_set_timeout(tc, 10);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
|
|
|
|||
|
|
@ -220,6 +220,8 @@ static void setup(void)
|
|||
flash_idx = 0;
|
||||
g_fbcr_n = 0;
|
||||
set32(ELBC_LTESR, ELBC_LTESR_CC); /* FCM commands complete instantly */
|
||||
/* MDR: DQ0 clear = no fail, DQ7 set = not write protected */
|
||||
set32(ELBC_MDR, 0x80);
|
||||
}
|
||||
|
||||
static void teardown(void)
|
||||
|
|
@ -457,6 +459,52 @@ START_TEST(test_p1021_write_unaligned)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* A program that reports failure in the status byte (DQ0 set) must stop:
|
||||
* the first page is the last programmed, the loop does not advance to the
|
||||
* next page. setup() pins MDR to 0x80 (success), so override it here. */
|
||||
START_TEST(test_p1021_write_status_fail)
|
||||
{
|
||||
uint8_t data[2 * 1024];
|
||||
size_t i;
|
||||
|
||||
fill(data, sizeof(data), 0x50);
|
||||
/* DQ0 set = program failed, DQ7 set = not protected. */
|
||||
set32(ELBC_MDR, 0x81);
|
||||
|
||||
ck_assert_int_eq(ext_flash_write(0, data, 600), -1);
|
||||
|
||||
/* Only the first page was programmed; the loop stopped before page 1. */
|
||||
ck_assert_int_eq(g_fbcr_n, 1);
|
||||
ck_assert_uint_eq(g_fbcr_log[0], 0);
|
||||
for (i = 0; i < 512; i++)
|
||||
ck_assert_uint_eq(g_nand[i], data[i]);
|
||||
for (i = 0; i < 512; i++)
|
||||
ck_assert_uint_eq(NAND(1, i), 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A program on a write-protected block (DQ7 clear) must stop the same way:
|
||||
* no later page is programmed. */
|
||||
START_TEST(test_p1021_write_write_protected)
|
||||
{
|
||||
uint8_t data[2 * 1024];
|
||||
size_t i;
|
||||
|
||||
fill(data, sizeof(data), 0x60);
|
||||
/* DQ0 clear = no fail, DQ7 clear = write protected. */
|
||||
set32(ELBC_MDR, 0x00);
|
||||
|
||||
ck_assert_int_eq(ext_flash_write(0, data, 600), -1);
|
||||
|
||||
ck_assert_int_eq(g_fbcr_n, 1);
|
||||
ck_assert_uint_eq(g_fbcr_log[0], 0);
|
||||
for (i = 0; i < 512; i++)
|
||||
ck_assert_uint_eq(g_nand[i], data[i]);
|
||||
for (i = 0; i < 512; i++)
|
||||
ck_assert_uint_eq(NAND(1, i), 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A full-page read from column 0 must keep BC = 0 (full page + spare,
|
||||
* the only ECC-checking setting). */
|
||||
START_TEST(test_p1021_read_full_page)
|
||||
|
|
@ -571,6 +619,8 @@ Suite *p1021_fcm_suite(void)
|
|||
tcase_add_test(tc, test_p1021_write_partial);
|
||||
tcase_add_test(tc, test_p1021_write_multipart);
|
||||
tcase_add_test(tc, test_p1021_write_unaligned);
|
||||
tcase_add_test(tc, test_p1021_write_status_fail);
|
||||
tcase_add_test(tc, test_p1021_write_write_protected);
|
||||
tcase_add_test(tc, test_p1021_read_full_page);
|
||||
tcase_add_test(tc, test_p1021_read_short_spare_loaded);
|
||||
tcase_add_test(tc, test_p1021_read_multipart);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* unit-pkcs11-pin-zeroize.c
|
||||
*
|
||||
* Unit test for the PKCS#11 login credential lifetime (F-12114).
|
||||
* Unit test for the PKCS#11 login credential lifetime (F-12114,
|
||||
* F-12942).
|
||||
*
|
||||
* pkcs11_pin is a file-scope copy of the credential supplied to
|
||||
* C_Login() for the token holding the firmware-decryption key.
|
||||
|
|
@ -58,6 +59,7 @@ static uint8_t test_encrypt_key[ENCRYPT_PKCS11_KEY_ID_SIZE +
|
|||
/* ---- PKCS#11 stubs ---- */
|
||||
|
||||
static int stub_close_session_calls;
|
||||
static int stub_login_fail;
|
||||
|
||||
static CK_RV stub_C_Initialize(CK_VOID_PTR pInitArgs)
|
||||
{
|
||||
|
|
@ -98,6 +100,9 @@ static CK_RV stub_C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
|
|||
(void)userType;
|
||||
(void)pPin;
|
||||
(void)ulPinLen;
|
||||
if (stub_login_fail) {
|
||||
return CKR_PIN_INCORRECT;
|
||||
}
|
||||
return CKR_OK;
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +176,7 @@ void panic(void)
|
|||
static void reset_stub_state(void)
|
||||
{
|
||||
stub_close_session_calls = 0;
|
||||
stub_login_fail = 0;
|
||||
}
|
||||
|
||||
/* F-12114: the pre-handoff deinitializer must erase the PKCS#11
|
||||
|
|
@ -223,6 +229,32 @@ START_TEST(test_pkcs11_deinit_no_session)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* F-12942: a terminal initialization failure after C_Login() was
|
||||
* attempted (login rejected) tears the session down and must also
|
||||
* erase the credential copy: the bootloader memory is retained
|
||||
* after the handoff, as on the deinit path. */
|
||||
START_TEST(test_pkcs11_pin_wiped_on_init_failure)
|
||||
{
|
||||
int ret;
|
||||
size_t i;
|
||||
|
||||
reset_stub_state();
|
||||
encrypt_initialized = 0;
|
||||
memcpy(pkcs11_pin, ENCRYPT_PKCS11_PIN, sizeof(ENCRYPT_PKCS11_PIN));
|
||||
stub_login_fail = 1;
|
||||
|
||||
ret = pkcs11_crypto_init();
|
||||
stub_login_fail = 0;
|
||||
|
||||
ck_assert_int_eq(ret, CKR_PIN_INCORRECT);
|
||||
ck_assert_int_eq(encrypt_initialized, 0);
|
||||
for (i = 0; i < sizeof(pkcs11_pin); i++) {
|
||||
ck_assert_msg(pkcs11_pin[i] == 0,
|
||||
"pkcs11_pin byte %zu not wiped", i);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfboot-pkcs11-pin");
|
||||
|
|
@ -230,6 +262,7 @@ Suite *wolfboot_suite(void)
|
|||
|
||||
tcase_add_test(tc, test_pkcs11_pin_wiped_on_deinit);
|
||||
tcase_add_test(tc, test_pkcs11_deinit_no_session);
|
||||
tcase_add_test(tc, test_pkcs11_pin_wiped_on_init_failure);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,352 @@
|
|||
/* unit-update-ram-custom-trailer.c
|
||||
*
|
||||
* Tests update_ram.c with CUSTOM_PARTITION_TRAILER (custom callbacks) and
|
||||
* WOLFBOOT_FIXED_PARTITIONS. Covers the HAVE_PARTITION_TRAILERS path where
|
||||
* partition state is managed via externally-defined get/set_trailer_at.
|
||||
*/
|
||||
#ifndef WOLFBOOT_HASH_SHA256
|
||||
#define WOLFBOOT_HASH_SHA256
|
||||
#endif
|
||||
|
||||
#define IMAGE_HEADER_SIZE 256
|
||||
#define MOCK_ADDRESS_UPDATE 0xCC000000
|
||||
#define MOCK_ADDRESS_BOOT 0xCD000000
|
||||
#define MOCK_ADDRESS_SWAP 0xCE000000
|
||||
#define NO_FORK 1
|
||||
|
||||
#include <check.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "target.h"
|
||||
|
||||
static __thread unsigned char
|
||||
wolfboot_ram[WOLFBOOT_RAMBOOT_MAX_SIZE + IMAGE_HEADER_SIZE];
|
||||
|
||||
#define WOLFBOOT_LOAD_ADDRESS (((uintptr_t)wolfboot_ram) + IMAGE_HEADER_SIZE)
|
||||
#define TEST_SIZE_SMALL 5300
|
||||
#define DIGEST_TLV_OFF_IN_HDR 28
|
||||
#define STAGE_ADDR_SENTINEL UINTPTR_MAX
|
||||
|
||||
#include "user_settings.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
/* Custom partition trailer state (mocked)
|
||||
* Layout: [state][magic(4 bytes)]
|
||||
* get_trailer_at(part, 0) -> magic (32-bit, base[1..4])
|
||||
* get_trailer_at(part, 1) -> state (8-bit, base[0])
|
||||
*/
|
||||
static uint8_t mock_trailer_boot[5];
|
||||
static uint8_t mock_trailer_update[5];
|
||||
|
||||
uint8_t* get_trailer_at(uint8_t part, uint32_t at)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot : mock_trailer_update;
|
||||
if (at == 0)
|
||||
return &base[1]; /* magic at base[1..4] */
|
||||
return &base[at - 1]; /* state at base[0], etc. */
|
||||
}
|
||||
|
||||
void set_trailer_at(uint8_t part, uint32_t at, uint8_t val)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot : mock_trailer_update;
|
||||
if (at == 0)
|
||||
base[1] = val; /* magic byte 0 */
|
||||
else
|
||||
base[at - 1] = val;
|
||||
}
|
||||
|
||||
void set_partition_magic(uint8_t part)
|
||||
{
|
||||
uint8_t *base = (part == PART_BOOT) ? mock_trailer_boot : mock_trailer_update;
|
||||
/* WOLFBOOT_MAGIC_TRAIL = 0x544F4F42 on LE: bytes are 'B','O','O','T' */
|
||||
base[1] = 'B';
|
||||
base[2] = 'O';
|
||||
base[3] = 'O';
|
||||
base[4] = 'T';
|
||||
(void)part;
|
||||
}
|
||||
|
||||
#define wolfBoot_dualboot_candidate wolfBoot_dualboot_candidate_impl
|
||||
#include "libwolfboot.c"
|
||||
#undef wolfBoot_dualboot_candidate
|
||||
|
||||
static int dualboot_candidate_calls;
|
||||
|
||||
int wolfBoot_dualboot_candidate(void)
|
||||
{
|
||||
dualboot_candidate_calls++;
|
||||
ck_assert_msg(dualboot_candidate_calls == 1,
|
||||
"wolfBoot_dualboot_candidate() called %d times",
|
||||
dualboot_candidate_calls);
|
||||
return wolfBoot_dualboot_candidate_impl();
|
||||
}
|
||||
|
||||
#include "update_ram.c"
|
||||
#include "unit-mock-flash.c"
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
int wolfBoot_staged_ok = 0;
|
||||
const uint32_t *wolfBoot_stage_address =
|
||||
(const uint32_t *)(uintptr_t)STAGE_ADDR_SENTINEL;
|
||||
|
||||
void* hal_get_primary_address(void)
|
||||
{
|
||||
return (void *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
}
|
||||
|
||||
void* hal_get_update_address(void)
|
||||
{
|
||||
return (void *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
}
|
||||
|
||||
void do_boot(const uint32_t *address)
|
||||
{
|
||||
wolfBoot_staged_ok++;
|
||||
wolfBoot_stage_address = address;
|
||||
}
|
||||
|
||||
static int mock_flash_protect_called = 0;
|
||||
static haladdr_t mock_flash_protect_addr = 0;
|
||||
static int mock_flash_protect_len = 0;
|
||||
|
||||
int hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
mock_flash_protect_called++;
|
||||
mock_flash_protect_addr = address;
|
||||
mock_flash_protect_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_mock_stats(void)
|
||||
{
|
||||
wolfBoot_panicked = 0;
|
||||
wolfBoot_staged_ok = 0;
|
||||
dualboot_candidate_calls = 0;
|
||||
mock_flash_protect_called = 0;
|
||||
mock_flash_protect_addr = 0;
|
||||
mock_flash_protect_len = 0;
|
||||
mock_max_read_addr = 0;
|
||||
mock_max_read_len = 0;
|
||||
memset(mock_trailer_boot, 0, sizeof(mock_trailer_boot));
|
||||
memset(mock_trailer_update, 0, sizeof(mock_trailer_update));
|
||||
}
|
||||
|
||||
static void prepare_flash(void)
|
||||
{
|
||||
int ret;
|
||||
char ext_path[64];
|
||||
char int_path[64];
|
||||
|
||||
snprintf(ext_path, sizeof(ext_path),
|
||||
"/tmp/wolfboot-unit-ext-file-custom-trailer-%d.bin", (int)getpid());
|
||||
snprintf(int_path, sizeof(int_path),
|
||||
"/tmp/wolfboot-unit-int-file-custom-trailer-%d.bin", (int)getpid());
|
||||
|
||||
ret = mmap_file(ext_path,
|
||||
(void *)(uintptr_t)MOCK_ADDRESS_UPDATE,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert_int_ge(ret, 0);
|
||||
ret = mmap_file(int_path,
|
||||
(void *)(uintptr_t)MOCK_ADDRESS_BOOT,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert_int_ge(ret, 0);
|
||||
|
||||
ext_flash_unlock();
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_lock();
|
||||
}
|
||||
|
||||
static void cleanup_flash(void)
|
||||
{
|
||||
char ext_path[64];
|
||||
char int_path[64];
|
||||
|
||||
munmap((void *)WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
munmap((void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
snprintf(ext_path, sizeof(ext_path),
|
||||
"/tmp/wolfboot-unit-ext-file-custom-trailer-%d.bin", (int)getpid());
|
||||
snprintf(int_path, sizeof(int_path),
|
||||
"/tmp/wolfboot-unit-int-file-custom-trailer-%d.bin", (int)getpid());
|
||||
unlink(ext_path);
|
||||
unlink(int_path);
|
||||
}
|
||||
|
||||
static int add_payload(uint8_t part, uint32_t version, uint32_t size)
|
||||
{
|
||||
uint32_t word;
|
||||
uint16_t word16;
|
||||
int i;
|
||||
uint8_t *base = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
int ret;
|
||||
wc_Sha256 sha;
|
||||
uint8_t digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (part == PART_UPDATE)
|
||||
base = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
srandom(part);
|
||||
|
||||
ext_flash_unlock();
|
||||
ext_flash_write((uintptr_t)base, "WOLF", 4);
|
||||
ext_flash_write((uintptr_t)base + 4, (void *)&size, 4);
|
||||
|
||||
word = 4 << 16 | HDR_VERSION;
|
||||
ext_flash_write((uintptr_t)base + 8, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + 12, (void *)&version, 4);
|
||||
|
||||
word = 2 << 16 | HDR_IMG_TYPE;
|
||||
ext_flash_write((uintptr_t)base + 16, (void *)&word, 4);
|
||||
word16 = HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP;
|
||||
ext_flash_write((uintptr_t)base + 20, (void *)&word16, 2);
|
||||
|
||||
ret = wc_Sha256Update(&sha, base, DIGEST_TLV_OFF_IN_HDR);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
size += IMAGE_HEADER_SIZE;
|
||||
for (i = IMAGE_HEADER_SIZE; i < (int)size; i += 4) {
|
||||
uint32_t rand_word = (random() << 16) | random();
|
||||
ext_flash_write((uintptr_t)base + i, (void *)&rand_word, 4);
|
||||
}
|
||||
for (i = IMAGE_HEADER_SIZE; i < (int)size; i += WOLFBOOT_SHA_BLOCK_SIZE) {
|
||||
int len = WOLFBOOT_SHA_BLOCK_SIZE;
|
||||
|
||||
if (((int)size - i) < len)
|
||||
len = (int)size - i;
|
||||
ret = wc_Sha256Update(&sha, base + i, len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wc_Sha256Final(&sha, digest);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
wc_Sha256Free(&sha);
|
||||
|
||||
word = SHA256_DIGEST_SIZE << 16 | HDR_SHA256;
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR + 4, digest,
|
||||
SHA256_DIGEST_SIZE);
|
||||
ext_flash_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test 1: Update partition in UPDATING state transitions to TESTING after boot */
|
||||
START_TEST(test_custom_trailer_updating_sets_testing)
|
||||
{
|
||||
uint8_t state;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0);
|
||||
|
||||
/* Set the update partition to UPDATING state via custom trailer */
|
||||
set_partition_magic(PART_UPDATE);
|
||||
mock_trailer_update[0] = IMG_STATE_UPDATING; /* state at base[0] */
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
/* After boot, the update partition should be in TESTING state */
|
||||
ck_assert_int_eq(wolfBoot_staged_ok, 1);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(wolfBoot_get_partition_state(PART_UPDATE, &state), 0);
|
||||
ck_assert_int_eq(state, IMG_STATE_TESTING);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Test 2: Invalid update falls back to boot partition */
|
||||
START_TEST(test_custom_trailer_invalid_update_falls_back_to_boot)
|
||||
{
|
||||
uint8_t bad_digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0);
|
||||
|
||||
memset(bad_digest, 0xBA, sizeof(bad_digest));
|
||||
ext_flash_unlock();
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + DIGEST_TLV_OFF_IN_HDR + 4,
|
||||
bad_digest, sizeof(bad_digest));
|
||||
ext_flash_lock();
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
/* Falls back to boot partition (version 1): the image loaded to RAM
|
||||
* must come from the BOOT partition payload, not the corrupted UPDATE. */
|
||||
ck_assert_int_eq(wolfBoot_staged_ok, 1);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_uint_eq(mock_max_read_addr,
|
||||
(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Test 3: Newer update is preferred over boot */
|
||||
START_TEST(test_custom_trailer_newer_update_prefers_update)
|
||||
{
|
||||
int candidate;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0);
|
||||
|
||||
candidate = wolfBoot_dualboot_candidate_impl();
|
||||
|
||||
ck_assert_int_eq(candidate, PART_UPDATE);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfboot-update-ram-custom-trailer");
|
||||
TCase *tc = tcase_create("custom_trailer");
|
||||
|
||||
tcase_add_test(tc, test_custom_trailer_updating_sets_testing);
|
||||
tcase_add_test(tc, test_custom_trailer_invalid_update_falls_back_to_boot);
|
||||
tcase_add_test(tc, test_custom_trailer_newer_update_prefers_update);
|
||||
tcase_set_timeout(tc, 5);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fails;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
argv0 = strdup(argv[0]);
|
||||
(void)argc;
|
||||
|
||||
s = wolfboot_suite();
|
||||
sr = srunner_create(s);
|
||||
#if (NO_FORK == 1)
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
#endif
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return (fails == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/* unit-update-ram-nofixed-noramboot.c
|
||||
*
|
||||
* Reproducer for fallback selection in update_ram.c without fixed
|
||||
* partitions and without RAMBOOT (XIP): the configuration in which
|
||||
* wolfBoot_open_image_address() is called with a varying load_address
|
||||
* on every retry iteration.
|
||||
*
|
||||
* Pins F-13604: os_image was zeroed once, before the retry loop, so the
|
||||
* fallback iteration kept the stale img->hdr of the failed partition
|
||||
* and re-verified the wrong image. wolfBoot_open_image_address() only
|
||||
* adopts the address when img->hdr is NULL (documented precondition:
|
||||
* the struct is memset to 0 before each call), so the second
|
||||
* partition was never examined and a valid alternate image could not
|
||||
* boot.
|
||||
*/
|
||||
#ifndef WOLFBOOT_HASH_SHA256
|
||||
#define WOLFBOOT_HASH_SHA256
|
||||
#endif
|
||||
|
||||
#define IMAGE_HEADER_SIZE 256
|
||||
#define MOCK_ADDRESS_UPDATE 0xCC000000
|
||||
#define MOCK_ADDRESS_BOOT 0xCD000000
|
||||
#define MOCK_ADDRESS_SWAP 0xCE000000
|
||||
#define NO_FORK 1
|
||||
|
||||
#include <check.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "target.h"
|
||||
|
||||
#define TEST_SIZE_SMALL 5300
|
||||
#define DIGEST_TLV_OFF_IN_HDR 28
|
||||
#define STAGE_ADDR_SENTINEL UINTPTR_MAX
|
||||
|
||||
#include "user_settings.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
#define wolfBoot_dualboot_candidate_addr wolfBoot_dualboot_candidate_addr_impl
|
||||
#include "libwolfboot.c"
|
||||
#undef wolfBoot_dualboot_candidate_addr
|
||||
|
||||
static int dualboot_candidate_addr_calls;
|
||||
|
||||
int wolfBoot_dualboot_candidate_addr(void** addr)
|
||||
{
|
||||
dualboot_candidate_addr_calls++;
|
||||
ck_assert_msg(dualboot_candidate_addr_calls == 1,
|
||||
"wolfBoot_dualboot_candidate_addr() called %d times",
|
||||
dualboot_candidate_addr_calls);
|
||||
return wolfBoot_dualboot_candidate_addr_impl(addr);
|
||||
}
|
||||
|
||||
#include "update_ram.c"
|
||||
#include "unit-mock-flash.c"
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
int wolfBoot_staged_ok = 0;
|
||||
const uint32_t *wolfBoot_stage_address =
|
||||
(const uint32_t *)(uintptr_t)STAGE_ADDR_SENTINEL;
|
||||
|
||||
void* hal_get_primary_address(void)
|
||||
{
|
||||
return (void *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
}
|
||||
|
||||
void* hal_get_update_address(void)
|
||||
{
|
||||
return (void *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
}
|
||||
|
||||
void do_boot(const uint32_t *address)
|
||||
{
|
||||
if (wolfBoot_panicked)
|
||||
return;
|
||||
|
||||
wolfBoot_staged_ok++;
|
||||
wolfBoot_stage_address = address;
|
||||
}
|
||||
|
||||
static int mock_flash_protect_called = 0;
|
||||
static haladdr_t mock_flash_protect_addr = 0;
|
||||
static int mock_flash_protect_len = 0;
|
||||
|
||||
int hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
mock_flash_protect_called++;
|
||||
mock_flash_protect_addr = address;
|
||||
mock_flash_protect_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_mock_stats(void)
|
||||
{
|
||||
wolfBoot_panicked = 0;
|
||||
wolfBoot_staged_ok = 0;
|
||||
dualboot_candidate_addr_calls = 0;
|
||||
mock_flash_protect_called = 0;
|
||||
mock_flash_protect_addr = 0;
|
||||
mock_flash_protect_len = 0;
|
||||
}
|
||||
|
||||
static void prepare_flash(void)
|
||||
{
|
||||
int ret;
|
||||
char ext_path[64];
|
||||
char int_path[64];
|
||||
|
||||
snprintf(ext_path, sizeof(ext_path),
|
||||
"/tmp/wolfboot-unit-ext-file-nofixed-noramboot-%d.bin", (int)getpid());
|
||||
snprintf(int_path, sizeof(int_path),
|
||||
"/tmp/wolfboot-unit-int-file-nofixed-noramboot-%d.bin", (int)getpid());
|
||||
|
||||
ret = mmap_file(ext_path,
|
||||
(void *)(uintptr_t)MOCK_ADDRESS_UPDATE,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert_int_ge(ret, 0);
|
||||
ret = mmap_file(int_path,
|
||||
(void *)(uintptr_t)MOCK_ADDRESS_BOOT,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL);
|
||||
ck_assert_int_ge(ret, 0);
|
||||
|
||||
ext_flash_unlock();
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
ext_flash_lock();
|
||||
}
|
||||
|
||||
static void cleanup_flash(void)
|
||||
{
|
||||
char ext_path[64];
|
||||
char int_path[64];
|
||||
|
||||
munmap((void *)WOLFBOOT_PARTITION_BOOT_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
munmap((void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS,
|
||||
WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE);
|
||||
snprintf(ext_path, sizeof(ext_path),
|
||||
"/tmp/wolfboot-unit-ext-file-nofixed-noramboot-%d.bin", (int)getpid());
|
||||
snprintf(int_path, sizeof(int_path),
|
||||
"/tmp/wolfboot-unit-int-file-nofixed-noramboot-%d.bin", (int)getpid());
|
||||
unlink(ext_path);
|
||||
unlink(int_path);
|
||||
}
|
||||
|
||||
static int add_payload(uint8_t part, uint32_t version, uint32_t size)
|
||||
{
|
||||
uint32_t word;
|
||||
uint16_t word16;
|
||||
int i;
|
||||
int ret;
|
||||
uint8_t *base = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
wc_Sha256 sha;
|
||||
uint8_t digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (part == PART_UPDATE)
|
||||
base = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
srandom(part);
|
||||
|
||||
ext_flash_unlock();
|
||||
ext_flash_write((uintptr_t)base, "WOLF", 4);
|
||||
ext_flash_write((uintptr_t)base + 4, (void *)&size, 4);
|
||||
|
||||
word = 4 << 16 | HDR_VERSION;
|
||||
ext_flash_write((uintptr_t)base + 8, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + 12, (void *)&version, 4);
|
||||
|
||||
word = 2 << 16 | HDR_IMG_TYPE;
|
||||
ext_flash_write((uintptr_t)base + 16, (void *)&word, 4);
|
||||
word16 = HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP;
|
||||
ext_flash_write((uintptr_t)base + 20, (void *)&word16, 2);
|
||||
|
||||
ret = wc_Sha256Update(&sha, base, DIGEST_TLV_OFF_IN_HDR);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
size += IMAGE_HEADER_SIZE;
|
||||
for (i = IMAGE_HEADER_SIZE; i < (int)size; i += 4) {
|
||||
uint32_t rand_word = (random() << 16) | random();
|
||||
ext_flash_write((uintptr_t)base + i, (void *)&rand_word, 4);
|
||||
}
|
||||
for (i = IMAGE_HEADER_SIZE; i < (int)size; i += WOLFBOOT_SHA_BLOCK_SIZE) {
|
||||
int len = WOLFBOOT_SHA_BLOCK_SIZE;
|
||||
|
||||
if (((int)size - i) < len)
|
||||
len = (int)size - i;
|
||||
ret = wc_Sha256Update(&sha, base + i, len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wc_Sha256Final(&sha, digest);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
wc_Sha256Free(&sha);
|
||||
|
||||
word = SHA256_DIGEST_SIZE << 16 | HDR_SHA256;
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR, (void *)&word, 4);
|
||||
ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR + 4, digest,
|
||||
SHA256_DIGEST_SIZE);
|
||||
ext_flash_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
START_TEST(test_invalid_boot_falls_back_to_update)
|
||||
{
|
||||
uint8_t bad_digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
/* BOOT is the newer image but carries a corrupted digest: the
|
||||
* candidate selection picks it first, and the fallback must boot
|
||||
* the valid, older UPDATE image. */
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 2, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 1, TEST_SIZE_SMALL), 0);
|
||||
|
||||
memset(bad_digest, 0xBA, sizeof(bad_digest));
|
||||
ext_flash_unlock();
|
||||
ext_flash_write(WOLFBOOT_PARTITION_BOOT_ADDRESS + DIGEST_TLV_OFF_IN_HDR + 4,
|
||||
bad_digest, sizeof(bad_digest));
|
||||
ext_flash_lock();
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(wolfBoot_staged_ok, 1);
|
||||
ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address,
|
||||
(uintptr_t)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + IMAGE_HEADER_SIZE));
|
||||
#ifndef TZEN
|
||||
ck_assert_int_eq(mock_flash_protect_called, 1);
|
||||
ck_assert_uint_eq((uintptr_t)mock_flash_protect_addr,
|
||||
(uintptr_t)WOLFBOOT_ORIGIN);
|
||||
ck_assert_int_eq(mock_flash_protect_len, BOOTLOADER_PARTITION_SIZE);
|
||||
#endif
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_invalid_update_falls_back_to_boot)
|
||||
{
|
||||
uint8_t bad_digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
/* Mirror of the previous case: the newer UPDATE image is corrupt,
|
||||
* the fallback must boot the valid, older BOOT image. */
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0);
|
||||
|
||||
memset(bad_digest, 0xBA, sizeof(bad_digest));
|
||||
ext_flash_unlock();
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + DIGEST_TLV_OFF_IN_HDR + 4,
|
||||
bad_digest, sizeof(bad_digest));
|
||||
ext_flash_lock();
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(wolfBoot_staged_ok, 1);
|
||||
ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address,
|
||||
(uintptr_t)(WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE));
|
||||
#ifndef TZEN
|
||||
ck_assert_int_eq(mock_flash_protect_called, 1);
|
||||
ck_assert_uint_eq((uintptr_t)mock_flash_protect_addr,
|
||||
(uintptr_t)WOLFBOOT_ORIGIN);
|
||||
ck_assert_int_eq(mock_flash_protect_len, BOOTLOADER_PARTITION_SIZE);
|
||||
#endif
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_candidate_addr_equal_versions_prefers_boot)
|
||||
{
|
||||
void *addr = NULL;
|
||||
int ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 1, TEST_SIZE_SMALL), 0);
|
||||
|
||||
ret = wolfBoot_dualboot_candidate_addr_impl(&addr);
|
||||
|
||||
ck_assert_int_eq(ret, 0);
|
||||
ck_assert_ptr_eq(addr, hal_get_primary_address());
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_candidate_addr_newer_update_prefers_update)
|
||||
{
|
||||
void *addr = NULL;
|
||||
int ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0);
|
||||
ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0);
|
||||
|
||||
ret = wolfBoot_dualboot_candidate_addr_impl(&addr);
|
||||
|
||||
ck_assert_int_eq(ret, 1);
|
||||
ck_assert_ptr_eq(addr, hal_get_update_address());
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfboot-update-ram-nofixed-noramboot");
|
||||
TCase *tc = tcase_create("fallback");
|
||||
TCase *tc_candidate = tcase_create("candidate_addr");
|
||||
|
||||
tcase_add_test(tc, test_invalid_boot_falls_back_to_update);
|
||||
tcase_add_test(tc, test_invalid_update_falls_back_to_boot);
|
||||
tcase_set_timeout(tc, 5);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tcase_add_test(tc_candidate, test_candidate_addr_equal_versions_prefers_boot);
|
||||
tcase_add_test(tc_candidate, test_candidate_addr_newer_update_prefers_update);
|
||||
tcase_set_timeout(tc_candidate, 5);
|
||||
suite_add_tcase(s, tc_candidate);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fails;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
argv0 = strdup(argv[0]);
|
||||
(void)argc;
|
||||
|
||||
s = wolfboot_suite();
|
||||
sr = srunner_create(s);
|
||||
#if (NO_FORK == 1)
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
#endif
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -81,6 +81,8 @@ static void reset_mock_stats(void)
|
|||
{
|
||||
wolfBoot_panicked = 0;
|
||||
wolfBoot_staged_ok = 0;
|
||||
mock_max_read_addr = 0;
|
||||
mock_max_read_len = 0;
|
||||
}
|
||||
|
||||
static void prepare_flash(void)
|
||||
|
|
@ -184,16 +186,15 @@ START_TEST (test_noramboot_sunnyday) {
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Regression test for F-4410: firmware versions with the high bit set
|
||||
* (>= 0x80000000) must still feed the anti-rollback guard in wolfBoot_start.
|
||||
/* Regression test for F-4410 + F-12922: firmware versions with the high
|
||||
* bit set (>= 0x80000000) must be read without signed-int clamping (the
|
||||
* two version asserts below), and a failed high-version image must not
|
||||
* block fallback to the lower-versioned (but valid) UPDATE partition.
|
||||
*
|
||||
* BOOT carries the higher version but is marked oversize so wolfBoot_open_image()
|
||||
* rejects it and the boot path falls back to the lower-versioned (but valid)
|
||||
* UPDATE partition. That downgrade must be denied. Before the fix the versions
|
||||
* were cast through a signed int and clamped to 0, collapsing max_v to 0 and
|
||||
* silently bypassing the "(max_v > 0U)" guard, so the lower UPDATE image was
|
||||
* staged for boot. */
|
||||
START_TEST (test_noramboot_highversion_rollback_denied) {
|
||||
* BOOT carries the higher version but is marked oversize so
|
||||
* wolfBoot_open_image() rejects it; the boot path must fall back to the
|
||||
* valid UPDATE image instead of panicking on the version difference. */
|
||||
START_TEST (test_noramboot_fallback_to_lower_version) {
|
||||
uint32_t oversize = WOLFBOOT_PARTITION_SIZE;
|
||||
|
||||
reset_mock_stats();
|
||||
|
|
@ -212,10 +213,14 @@ START_TEST (test_noramboot_highversion_rollback_denied) {
|
|||
|
||||
wolfBoot_start();
|
||||
|
||||
/* Rollback to the lower UPDATE version must be denied: wolfBoot panics and
|
||||
* stages nothing. */
|
||||
ck_assert(!wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 1);
|
||||
/* A failed high-version boot image must not block fallback to the
|
||||
* valid lower-version update image (F-12922). The image loaded to RAM
|
||||
* must come from the UPDATE partition payload, not the oversize BOOT
|
||||
* partition. */
|
||||
ck_assert(wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_uint_eq(mock_max_read_addr,
|
||||
(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS + IMAGE_HEADER_SIZE);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
|
@ -249,19 +254,20 @@ Suite *wolfboot_suite(void)
|
|||
TCase *sunnyday = tcase_create("Non-RAMBOOT sunny day");
|
||||
TCase *ext_short_read =
|
||||
tcase_create("Non-RAMBOOT short ext flash read rejected");
|
||||
TCase *rollback_denied =
|
||||
tcase_create("Non-RAMBOOT high-version rollback denied");
|
||||
TCase *fallback_to_lower_version =
|
||||
tcase_create("Non-RAMBOOT fallback to lower version");
|
||||
|
||||
tcase_add_test(sunnyday, test_noramboot_sunnyday);
|
||||
tcase_add_test(ext_short_read,
|
||||
test_noramboot_ext_flash_short_read_rejected);
|
||||
tcase_add_test(rollback_denied, test_noramboot_highversion_rollback_denied);
|
||||
tcase_add_test(fallback_to_lower_version,
|
||||
test_noramboot_fallback_to_lower_version);
|
||||
suite_add_tcase(s, sunnyday);
|
||||
suite_add_tcase(s, ext_short_read);
|
||||
suite_add_tcase(s, rollback_denied);
|
||||
suite_add_tcase(s, fallback_to_lower_version);
|
||||
tcase_set_timeout(sunnyday, 5);
|
||||
tcase_set_timeout(ext_short_read, 5);
|
||||
tcase_set_timeout(rollback_denied, 5);
|
||||
tcase_set_timeout(fallback_to_lower_version, 5);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -434,9 +434,10 @@ START_TEST (test_invalid_update_type) {
|
|||
ext_flash_lock();
|
||||
wolfBoot_update_trigger();
|
||||
wolfBoot_start();
|
||||
ck_assert(!wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 1);
|
||||
ck_assert_int_eq(get_version_ramloaded(), 2);
|
||||
/* Failed update must fall back to the valid boot image, not panic. */
|
||||
ck_assert(wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(get_version_ramloaded(), 1);
|
||||
cleanup_flash();
|
||||
}
|
||||
|
||||
|
|
@ -453,8 +454,10 @@ START_TEST (test_update_toolarge) {
|
|||
|
||||
wolfBoot_update_trigger();
|
||||
wolfBoot_start();
|
||||
ck_assert(!wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 1);
|
||||
/* Failed update must fall back to the valid boot image, not panic. */
|
||||
ck_assert(wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(get_version_ramloaded(), 1);
|
||||
cleanup_flash();
|
||||
}
|
||||
|
||||
|
|
@ -471,8 +474,10 @@ START_TEST (test_invalid_sha) {
|
|||
ext_flash_lock();
|
||||
wolfBoot_update_trigger();
|
||||
wolfBoot_start();
|
||||
ck_assert(!wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 1);
|
||||
/* Failed update must fall back to the valid boot image, not panic. */
|
||||
ck_assert(wolfBoot_staged_ok);
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(get_version_ramloaded(), 1);
|
||||
cleanup_flash();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue