mirror of https://github.com/wolfSSL/wolfBoot.git
1884 lines
61 KiB
C
1884 lines
61 KiB
C
/* image.h
|
|
*
|
|
* Functions to help with wolfBoot image header
|
|
*
|
|
*
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#ifndef IMAGE_H
|
|
#define IMAGE_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "target.h"
|
|
#include "wolfboot/wolfboot.h"
|
|
|
|
#ifdef EXT_FLASH
|
|
#include "hal.h"
|
|
#endif
|
|
|
|
#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST))
|
|
#include "encrypt.h"
|
|
#endif
|
|
|
|
#ifndef WOLFBOOT_FLAGS_INVERT
|
|
#define SECT_FLAG_NEW 0x0F
|
|
#define SECT_FLAG_SWAPPING 0x07
|
|
#define SECT_FLAG_BACKUP 0x03
|
|
#define SECT_FLAG_UPDATED 0x00
|
|
#else
|
|
#define SECT_FLAG_NEW 0x00
|
|
#define SECT_FLAG_SWAPPING 0x08
|
|
#define SECT_FLAG_BACKUP 0x0c
|
|
#define SECT_FLAG_UPDATED 0x0f
|
|
#endif
|
|
|
|
#ifdef WOLFBOOT_SIGN_ED25519
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ed25519
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_ED448
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ed448
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_RSA2048) || \
|
|
defined (WOLFBOOT_SIGN_RSA3072) || \
|
|
defined (WOLFBOOT_SIGN_RSA4096) || \
|
|
defined (WOLFBOOT_SIGN_RSA2048ENC) || \
|
|
defined (WOLFBOOT_SIGN_RSA3072ENC) || \
|
|
defined (WOLFBOOT_SIGN_RSA4096ENC)
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_RSAPSS2048) || \
|
|
defined (WOLFBOOT_SIGN_RSAPSS3072) || \
|
|
defined (WOLFBOOT_SIGN_RSAPSS4096)
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_rsa_pss
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_ECC256) || \
|
|
defined (WOLFBOOT_SIGN_ECC384) || \
|
|
defined (WOLFBOOT_SIGN_ECC521)
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ecc
|
|
#endif
|
|
#if defined(WOLFBOOT_SIGN_LMS)
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_lms
|
|
#endif
|
|
#if defined(WOLFBOOT_SIGN_XMSS )
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_xmss
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_ML_DSA
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_ml_dsa
|
|
#endif
|
|
|
|
#ifdef WOLFBOOT_SIGN_SECONDARY_ED25519
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ed25519
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_SECONDARY_ED448
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ed448
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_SECONDARY_RSA2048) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSA3072) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSA4096) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSA2048ENC) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSA3072ENC) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSA4096ENC)
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS2048) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS3072) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_RSAPSS4096)
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_rsa_pss
|
|
#endif
|
|
#if defined (WOLFBOOT_SIGN_SECONDARY_ECC256) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_ECC384) || \
|
|
defined (WOLFBOOT_SIGN_SECONDARY_ECC521)
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ecc
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_SECONDARY_LMS
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_lms
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_SECONDARY_XMSS
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_xmss
|
|
#endif
|
|
#ifdef WOLFBOOT_SIGN_SECONDARY_ML_DSA
|
|
#define wolfBoot_verify_signature_secondary wolfBoot_verify_signature_ml_dsa
|
|
#endif
|
|
|
|
/* Thin wrappers: dispatch RSA / RSA-PSS to the unified verify function */
|
|
#define wolfBoot_verify_signature_rsa(ks, img, sig) \
|
|
wolfBoot_verify_signature_rsa_common(ks, img, sig, 0)
|
|
#define wolfBoot_verify_signature_rsa_pss(ks, img, sig) \
|
|
wolfBoot_verify_signature_rsa_common(ks, img, sig, 1)
|
|
|
|
#if defined(WOLFBOOT_TPM) && defined (WOLFBOOT_TPM_VERIFY)
|
|
#undef wolfBoot_verify_signature_primary
|
|
#define wolfBoot_verify_signature_primary wolfBoot_verify_signature_tpm
|
|
#endif
|
|
|
|
/* 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 at least as large as IMAGE_HEADER_SIZE
|
|
#endif
|
|
|
|
|
|
#if (defined(WOLFBOOT_ARMORED) && defined(__WOLFBOOT))
|
|
#if !defined(ARCH_ARM) || (!defined(__GNUC__) && \
|
|
!(defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__)))
|
|
# error WOLFBOOT_ARMORED only available for ARM with IAR or gcc compilers
|
|
#endif
|
|
|
|
#if defined(__GNUC__)
|
|
#define likely(x) __builtin_expect((x),1)
|
|
#define unlikely(x) __builtin_expect((x),0)
|
|
#else
|
|
#define likely(x) (x)
|
|
#define unlikely(x) (x)
|
|
#endif
|
|
|
|
struct wolfBoot_image {
|
|
uint8_t *hdr;
|
|
#ifdef EXT_FLASH
|
|
uint8_t *hdr_cache;
|
|
#endif
|
|
uint8_t *trailer;
|
|
uint8_t *sha_hash;
|
|
uint8_t *fw_base;
|
|
uint32_t fw_size;
|
|
uint32_t part;
|
|
uint32_t hdr_ok;
|
|
uint32_t canary_FEED4567;
|
|
uint32_t signature_ok;
|
|
uint32_t canary_FEED6789;
|
|
uint32_t not_signature_ok;
|
|
uint32_t canary_FEED89AB;
|
|
uint32_t sha_ok;
|
|
uint32_t canary_FEEDCAFE;
|
|
uint32_t not_sha_ok;
|
|
uintptr_t not_fw_base; /* complement of fw_base, for FI hardening */
|
|
uint32_t not_ext; /* image is no longer external */
|
|
};
|
|
|
|
|
|
/**
|
|
* This function sets the flag that indicates the signature is valid for the
|
|
* wolfBoot_image.
|
|
*
|
|
* With ARMORED setup, the flag is redundant, and the information is wrapped in
|
|
* between canary variables, to mitigate attacks based on memory corruptions.
|
|
*/
|
|
static void NOINLINEFUNCTION wolfBoot_image_confirm_signature_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->canary_FEED4567 = 0xFEED4567UL;
|
|
img->signature_ok = 1UL;
|
|
img->canary_FEED6789 = 0xFEED6789UL;
|
|
img->not_signature_ok = ~(1UL);
|
|
img->canary_FEED89AB = 0xFEED89ABUL;
|
|
}
|
|
|
|
static void NOINLINEFUNCTION wolfBoot_image_clear_signature_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->canary_FEED4567 = 0xFEED4567UL;
|
|
img->signature_ok = 0UL;
|
|
img->canary_FEED6789 = 0xFEED6789UL;
|
|
img->not_signature_ok = 1UL;
|
|
img->canary_FEED89AB = 0xFEED89ABUL;
|
|
}
|
|
|
|
/**
|
|
* This function sets the flag that indicates the digest (integrity) check
|
|
* succeeded for the wolfBoot_image.
|
|
*
|
|
* As with the signature flag, the value is redundant and wrapped between
|
|
* canary variables, so that a single fault cannot forge a valid state.
|
|
*/
|
|
static void NOINLINEFUNCTION UNUSEDFUNCTION wolfBoot_image_confirm_sha_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->canary_FEED89AB = 0xFEED89ABUL;
|
|
img->sha_ok = 1UL;
|
|
img->canary_FEEDCAFE = 0xFEEDCAFEUL;
|
|
img->not_sha_ok = ~(1UL);
|
|
}
|
|
|
|
static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->canary_FEED89AB = 0xFEED89ABUL;
|
|
img->sha_ok = 0UL;
|
|
img->canary_FEEDCAFE = 0xFEEDCAFEUL;
|
|
img->not_sha_ok = 1UL;
|
|
}
|
|
|
|
/**
|
|
* Sets sha_ok from the value the digest comparison produced rather than from
|
|
* the control flow that reached here. Sole setter of sha_ok in the ARMORED
|
|
* integrity path, so skipping the call leaves the flag clear.
|
|
*/
|
|
static void NOINLINEFUNCTION wolfBoot_image_seal_sha_ok(
|
|
struct wolfBoot_image *img, uint32_t witness)
|
|
{
|
|
volatile uint32_t z1 = 0U, z2 = 0U, z3 = 0U, ok = 0U;
|
|
|
|
z1 = (witness - 1U) >> 31; /* 1 iff witness == 0 */
|
|
z2 = 1U ^ ((witness | (0U - witness)) >> 31); /* 1 iff witness == 0 */
|
|
z3 = (witness == 0U) ? 1U : 0U; /* 1 iff witness == 0 */
|
|
|
|
ok = z1 & z2 & z3;
|
|
ok &= z1;
|
|
ok &= z2;
|
|
ok &= z3;
|
|
|
|
img->canary_FEED89AB = 0xFEED89ABUL;
|
|
img->sha_ok = ok;
|
|
img->canary_FEEDCAFE = 0xFEEDCAFEUL;
|
|
img->not_sha_ok = ~ok;
|
|
}
|
|
|
|
/**
|
|
* Records the image entry base together with its complement, so that a single
|
|
* fault on the pointer that do_boot() jumps through can be detected by
|
|
* FW_BASE_SANITY_CHECK() before the branch is taken.
|
|
*/
|
|
static void NOINLINEFUNCTION wolfBoot_image_set_fw_base(
|
|
struct wolfBoot_image *img, void *base)
|
|
{
|
|
img->fw_base = (uint8_t *)base;
|
|
img->not_fw_base = ~(uintptr_t)base;
|
|
}
|
|
|
|
/**
|
|
* Final sanity check, performed just before do_boot, or before starting an
|
|
* update that has been verified.
|
|
*
|
|
* This procedure detects if any of the previous checks has been skipped.
|
|
* If any of the required flags does not match the expected value, wolfBoot
|
|
* panics.
|
|
*/
|
|
#define PART_SANITY_CHECK(p) \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading hdr_ok flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->hdr_ok):"r2"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading hdr_ok flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->sha_ok):"r2"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading ~(sha_ok) flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->not_sha_ok):"r2"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading canary value, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->canary_FEEDCAFE):"r2"); \
|
|
asm volatile("mov r0, %0" ::"r"(0xFEEDCAFE):"r0"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading signature_ok flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->signature_ok):"r2"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0"); \
|
|
asm volatile("mov r2, #0"); \
|
|
asm volatile("mov r2, #0"); \
|
|
asm volatile("mov r2, #0"); \
|
|
asm volatile("mov r2, #0"); \
|
|
/* Loading ~(signature_ok) flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->not_signature_ok):"r2"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading canary value, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->canary_FEED6789):"r2"); \
|
|
asm volatile("mov r0, %0" ::"r"(0xFEED6789):"r0"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading canary value, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->canary_FEED4567):"r2"); \
|
|
asm volatile("mov r0, %0" ::"r"(0xFEED4567):"r0"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading canary value, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->canary_FEED89AB):"r2"); \
|
|
asm volatile("mov r0, %0" ::"r"(0xFEED89AB):"r0"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("cmp r2, r0":::"cc"); \
|
|
asm volatile("bne .-12") \
|
|
|
|
/**
|
|
* First part of RSA verification. Ensure that the function is called by
|
|
* double checking its return value contains a valid
|
|
* len (>= WOLFBOOT_SHA_DIGEST_SIZE).
|
|
*
|
|
* Uses GAS local numeric labels (1f/1:) so the macro can be safely expanded
|
|
* multiple times in the same function (e.g. RSA PKCS#1.5 + RSA-PSS paths).
|
|
*/
|
|
#define RSA_VERIFY_FN(ret,fn,...) \
|
|
{ \
|
|
/* Redundant set of r0=0 */ \
|
|
asm volatile("mov r0, #0":::"r0"); \
|
|
asm volatile("mov r0, #0":::"r0"); \
|
|
asm volatile("mov r0, #0":::"r0"); \
|
|
/* Call the function */ \
|
|
int tmp_ret = fn(__VA_ARGS__); \
|
|
ret = -1; \
|
|
/* Redundant set of r2=SHA_DIGEST_SIZE */ \
|
|
asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \
|
|
asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \
|
|
asm volatile("mov r2, %0" ::"r"(WOLFBOOT_SHA_DIGEST_SIZE):"r2"); \
|
|
/* Redundant check for fn() return value >= r2 */ \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("blt 1f"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("blt 1f"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("blt 1f"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("blt 1f"); \
|
|
/* Return value is set here in case of success */ \
|
|
ret = tmp_ret; \
|
|
asm volatile("1:"); \
|
|
asm volatile("nop"); \
|
|
}
|
|
|
|
/**
|
|
* Second part of RSA verification.
|
|
*
|
|
* Compare the digest twice, then confirm via
|
|
* wolfBoot_image_confirm_signature_ok();
|
|
*
|
|
* Uses GAS local numeric labels (2f/2:) for safe multi-expansion.
|
|
*/
|
|
#define RSA_VERIFY_HASH(img,digest) \
|
|
{ \
|
|
volatile int compare_res; \
|
|
if (!img || !digest) \
|
|
asm volatile("b 2f"); \
|
|
/* Redundant set of r0=50*/ \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
compare_res = image_CT_compare(digest, img->sha_hash, \
|
|
WOLFBOOT_SHA_DIGEST_SIZE); \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f":::"cc"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("bne 2f":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
/* Repeat comparison call */ \
|
|
compare_res = image_CT_compare(digest, img->sha_hash, \
|
|
WOLFBOOT_SHA_DIGEST_SIZE); \
|
|
compare_res; \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 2f"); \
|
|
/* Confirm that the signature is OK */ \
|
|
wolfBoot_image_confirm_signature_ok(img); \
|
|
asm volatile("2:"); \
|
|
asm volatile("nop"); \
|
|
}
|
|
|
|
/**
|
|
* Second part of RSA-PSS verification.
|
|
*
|
|
* Call wc_RsaPSS_CheckPadding twice, then confirm via
|
|
* wolfBoot_image_confirm_signature_ok();
|
|
*
|
|
* Uses GAS local numeric labels (3f/3:) for safe multi-expansion.
|
|
*/
|
|
#define RSA_PSS_VERIFY_HASH(img, pss_data, pss_data_sz, hash_type) \
|
|
{ \
|
|
volatile int pss_res; \
|
|
if (!img || !pss_data) \
|
|
asm volatile("b 3f"); \
|
|
/* Redundant set of r0=50*/ \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
pss_res = wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \
|
|
pss_data, pss_data_sz, hash_type); \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f":::"cc"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("cmp r0, #0"); \
|
|
asm volatile("bne 3f":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
/* Repeat wc_RsaPSS_CheckPadding call */ \
|
|
pss_res = wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \
|
|
pss_data, pss_data_sz, hash_type); \
|
|
pss_res; \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 3f"); \
|
|
/* Confirm that the signature is OK */ \
|
|
wolfBoot_image_confirm_signature_ok(img); \
|
|
asm volatile("3:"); \
|
|
asm volatile("nop"); \
|
|
}
|
|
|
|
/* Redundant, canary-wrapped test for a confirmed integrity (digest) check. */
|
|
#define SHA_OK(imgp) (((imgp)->sha_ok == 1) && \
|
|
((imgp)->not_sha_ok == ~(uint32_t)1))
|
|
|
|
/**
|
|
* Digest comparison with no call and no return register, run inline as an
|
|
* additional gate. Emits a witness in `out`, zero only on a clean full pass.
|
|
* Branches to label 5 on mismatch, like the checks around it.
|
|
*/
|
|
#define CT_COMPARE_INLINE(out, a, b, len) \
|
|
asm volatile( \
|
|
"mov r0, %1\n" /* left */ \
|
|
"mov r1, %2\n" /* right */ \
|
|
"mov r2, #0\n" /* index */ \
|
|
"mov r2, #0\n" \
|
|
"mov r2, #0\n" \
|
|
"mov r3, #0\n" /* difference accumulator */ \
|
|
"mov r3, #0\n" \
|
|
"mov r3, #0\n" \
|
|
"mov r6, %3\n" /* iteration budget */ \
|
|
"mov r6, %3\n" \
|
|
"mov r6, %3\n" \
|
|
"7:\n" \
|
|
"ldrb r4, [r0, r2]\n" \
|
|
"ldrb r5, [r1, r2]\n" \
|
|
"eors r4, r4, r5\n" \
|
|
"orrs r3, r3, r4\n" \
|
|
"adds r2, r2, #1\n" \
|
|
"subs r6, r6, #1\n" \
|
|
"beq 8f\n" /* budget spent: stop regardless */ \
|
|
"cmp r2, %3\n" \
|
|
"blo 7b\n" \
|
|
"8:\n" \
|
|
/* witness = difference | iteration shortfall */ \
|
|
"eor r4, r2, %3\n" \
|
|
"orr %0, r3, r4\n" \
|
|
"orr %0, %0, r3\n" \
|
|
/* every byte matched */ \
|
|
"cmp r3, #0\n" \
|
|
"cmp r3, #0\n" \
|
|
"cmp r3, #0\n" \
|
|
"bne 5f\n" \
|
|
"cmp r3, #0\n" \
|
|
"cmp r3, #0\n" \
|
|
"cmp r3, #0\n" \
|
|
"bne 5f\n" \
|
|
/* and the loop really ran over the whole buffer */ \
|
|
"cmp r2, %3\n" \
|
|
"cmp r2, %3\n" \
|
|
"cmp r2, %3\n" \
|
|
"bne 5f\n" \
|
|
"cmp r2, %3\n" \
|
|
"cmp r2, %3\n" \
|
|
"cmp r2, %3\n" \
|
|
"bne 5f\n" \
|
|
: "=&r"(out) \
|
|
: "r"(a), "r"(b), "r"((uint32_t)(len)) \
|
|
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "cc", "memory")
|
|
|
|
/**
|
|
* Digest (integrity) verification.
|
|
*
|
|
* Compare the freshly computed digest against the stored one twice through
|
|
* image_CT_compare(), checking each result redundantly, then a third time
|
|
* inline. Only then record the digest and raise sha_ok from the witness.
|
|
*
|
|
* Uses GAS local numeric labels (5f/5:, 7f/7b, 8f/8:) for safe expansion.
|
|
*/
|
|
#define VERIFY_INTEGRITY_FN(img, computed_digest, stored) \
|
|
{ \
|
|
volatile int compare_res; \
|
|
/* Pre-set to "mismatch". */ \
|
|
volatile uint32_t ct_witness = 0xFFFFFFFFU; \
|
|
if (!(img) || !(stored)) \
|
|
asm volatile("b 5f"); \
|
|
/* Redundant set of r0=50 */ \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
compare_res = image_CT_compare((computed_digest), (stored), \
|
|
WOLFBOOT_SHA_DIGEST_SIZE); \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
/* Repeat comparison call */ \
|
|
compare_res = image_CT_compare((computed_digest), (stored), \
|
|
WOLFBOOT_SHA_DIGEST_SIZE); \
|
|
compare_res; \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 5f"); \
|
|
/* Third comparison, inline */ \
|
|
CT_COMPARE_INLINE(ct_witness, (computed_digest), (stored), \
|
|
WOLFBOOT_SHA_DIGEST_SIZE); \
|
|
(img)->sha_hash = (stored); \
|
|
wolfBoot_image_seal_sha_ok((img), ct_witness); \
|
|
asm volatile("5:"); \
|
|
asm volatile("nop"); \
|
|
}
|
|
|
|
/**
|
|
* Hardened assertion that the integrity (digest) check has actually been
|
|
* performed and passed. Mirrors the sha portion of PART_SANITY_CHECK and is
|
|
* used right after the integrity re-check in wolfBoot_verify_authenticity(),
|
|
* so that skipping the re-check (or its result) cannot let an unverified
|
|
* image proceed to signature verification.
|
|
*/
|
|
#define SHA_SANITY_CHECK(p) \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading sha_ok flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->sha_ok):"r2"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne .-12"); \
|
|
/* Redundant set of r2=0 */ \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
asm volatile("mov r2, #0":::"r2"); \
|
|
/* Loading ~(sha_ok) flag, verifying */ \
|
|
asm volatile("mov r2, %0" ::"r"((p)->not_sha_ok):"r2"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-4"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-8"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne .-12")
|
|
|
|
/**
|
|
* Hardened assertion that the image entry base is consistent with its stored
|
|
* complement (fw_base == ~not_fw_base), performed immediately before do_boot()
|
|
* dereferences fw_base. A single fault on either the pointer or the loads
|
|
* fails the check safely (spins) instead of redirecting the boot jump.
|
|
*/
|
|
#define FW_BASE_SANITY_CHECK(p) \
|
|
do { \
|
|
/* Single asm block so the compiler cannot allocate over r0/r2 between \
|
|
* steps: r2 = fw_base, r0 = ~not_fw_base (== expected fw_base), then \
|
|
* a redundant self-trapping comparison that spins on any mismatch. */ \
|
|
asm volatile( \
|
|
"mov r2, %[fwb]\n\t" \
|
|
"mov r0, %[nfwb]\n\t" \
|
|
"mvn r0, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"bne .\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"bne .-4\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"bne .-8\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"cmp r2, r0\n\t" \
|
|
"bne .-12\n\t" \
|
|
: \
|
|
: [fwb] "r" ((uintptr_t)(p)->fw_base), \
|
|
[nfwb] "r" ((uintptr_t)(p)->not_fw_base) \
|
|
: "r0", "r2", "cc"); \
|
|
} while (0)
|
|
|
|
/**
|
|
* ECC / Ed / PQ signature verification.
|
|
* Those verify functions set an additional value 'p_res'
|
|
* which is passed as a pointer.
|
|
*
|
|
* Ensure that the verification function has been called, and then
|
|
* set the return value accordingly.
|
|
*
|
|
* Double check by reading the value in p_res from memory a few times.
|
|
*
|
|
* Uses GAS local numeric labels (4f/4:) for safe multi-expansion.
|
|
*/
|
|
#if defined(__GNUC__)
|
|
|
|
#define VERIFY_FN(img,p_res,fn,...) \
|
|
/* Redundant set of r0=50*/ \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
asm volatile("mov r0, #50":::"r0"); \
|
|
/* Call the verify function */ \
|
|
fn(__VA_ARGS__); \
|
|
/* Redundant checks that ensure the function actually returned 0 */ \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("cmp r0, #0":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
/* Check that res = 1, a few times, reading the value from memory */ \
|
|
asm volatile("ldr r2, [%0]" ::"r"(p_res)); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("mvn r3, r2":::"r3"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("ldr r2, [%0]" ::"r"(p_res)); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("mvn r3, r2":::"r3"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("ldr r2, [%0]" ::"r"(p_res)); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("mvn r3, r2":::"r3"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("ldr r2, [%0]" ::"r"(p_res)); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("cmp r2, #1":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
asm volatile("mvn r3, r2":::"r3"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("cmp r3, #0xFFFFFFFE":::"cc"); \
|
|
asm volatile("bne 4f"); \
|
|
/* Confirm that the signature is OK */ \
|
|
wolfBoot_image_confirm_signature_ok(img); \
|
|
asm volatile("4:"); \
|
|
asm volatile("nop") \
|
|
|
|
#elif defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__)
|
|
|
|
#define VERIFY_FN(img, p_res, fn, ...) \
|
|
do { \
|
|
__asm volatile( \
|
|
"mov r0, #50\n" \
|
|
"mov r0, #50\n" \
|
|
"mov r0, #50\n" \
|
|
: /* No output operands */ \
|
|
: /* No input operands */ \
|
|
: "r0" /* Clobbered registers */ \
|
|
); \
|
|
void (*confirm_func)(struct wolfBoot_image *) = \
|
|
wolfBoot_image_confirm_signature_ok; \
|
|
fn(__VA_ARGS__); \
|
|
__asm volatile( \
|
|
"cmp r0, #0\n" \
|
|
"cmp r0, #0\n" \
|
|
"cmp r0, #0\n" \
|
|
"beq 30f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"30:\n" \
|
|
"ldr r2, [%0]\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"beq 31f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"31:\n" \
|
|
"mvn r3, r2\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"beq 32f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"32:\n" \
|
|
"ldr r2, [%0]\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"beq 33f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"33:\n" \
|
|
"mvn r3, r2\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"beq 34f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"34:\n" \
|
|
"ldr r2, [%0]\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"beq 35f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"35:\n" \
|
|
"mvn r3, r2\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"beq 36f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"36:\n" \
|
|
"ldr r2, [%0]\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"cmp r2, #1\n" \
|
|
"beq 37f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"37:\n" \
|
|
"mvn r3, r2\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"cmp r3, #0xFFFFFFFE\n" \
|
|
"beq 38f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"38:\n" \
|
|
/* Load 'img' into r0 (first argument to the function) */ \
|
|
"mov r0, %1\n" \
|
|
/* Load the function pointer into r3 */ \
|
|
"mov r3, %2\n" \
|
|
"blx r3\n"\
|
|
"b 2f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"1:\n" \
|
|
"nop\n" \
|
|
"2:\n" \
|
|
: /* No output operands */ \
|
|
: "r"(p_res), "r"(img), "r"(confirm_func) /* Input operands */ \
|
|
: "r0", "r2", "r3", "lr" /* Clobbered registers */ \
|
|
); \
|
|
} while (0)
|
|
#endif
|
|
|
|
|
|
/**
|
|
* This macro is only invoked after a successful update version check, prior to
|
|
* initiating the update installation.
|
|
*
|
|
* At this point, wolfBoot thinks that the version check has been successful.
|
|
*
|
|
*
|
|
* The fallback flag (checked with redundancy) causes wolfBoot to skip the
|
|
* redundant version checks.
|
|
*
|
|
* The redundant checks here ensure that the image version is read twice per
|
|
* each partition, and the two return values are the same.
|
|
*
|
|
*
|
|
* The comparison is also redundant, causing wolfBoot to panic if the update
|
|
* version is not strictly greater than the current one.
|
|
*
|
|
*/
|
|
#if defined(__GNUC__)
|
|
|
|
#define VERIFY_VERSION_ALLOWED(fb_ok) \
|
|
asm volatile( \
|
|
"push {r4, r5, r6, r7}\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r4, #1\n" \
|
|
"mov r5, #0\n" \
|
|
"mov r6, #2\n" \
|
|
"mov r7, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r4, #1\n" \
|
|
"mov r5, #0\n" \
|
|
"mov r6, #2\n" \
|
|
"mov r7, #0\n" \
|
|
"mov r0, %0\n" \
|
|
"mov r4, %0\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"beq 20f\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"20:\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"beq 21f\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"21:\n" \
|
|
"cmp r0, r4\n" \
|
|
"cmp r0, r4\n" \
|
|
"cmp r0, r4\n" \
|
|
"beq 22f\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"22:\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"beq 23f\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"bne do_check\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"23:\n" \
|
|
"b end_check\n" \
|
|
"do_check:\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r7, r0\n" \
|
|
"mov r7, r0\n" \
|
|
"mov r7, r0\n" \
|
|
"cmp r5, r7\n" \
|
|
"cmp r5, r7\n" \
|
|
"cmp r5, r7\n" \
|
|
"beq 24f\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"24:\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r6, r0\n" \
|
|
"mov r6, r0\n" \
|
|
"mov r6, r0\n" \
|
|
"cmp r4, r6\n" \
|
|
"cmp r4, r6\n" \
|
|
"cmp r4, r6\n" \
|
|
"beq 25f\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"bne ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"25:\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"blo 26f\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"26:\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"blo 27f\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"27:\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"blo 28f\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"28:\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"blo 29f\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"bhs ver_panic\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"29:\n" \
|
|
"b end_check\n" \
|
|
"ver_panic:\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"end_check:\n" \
|
|
"pop {r4, r5, r6, r7}\n" \
|
|
: \
|
|
: "r"(fb_ok) \
|
|
: "r0", "r4", "r5", "r6", "r7", "lr", "cc", "memory" \
|
|
)
|
|
|
|
#elif defined(__ICCARM__) && defined(__IAR_SYSTEMS_ICC__)
|
|
|
|
#define VERIFY_VERSION_ALLOWED(fb_ok) \
|
|
do { \
|
|
__asm volatile( \
|
|
"push {r4, r5, r6, r7}\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r4, #1\n" \
|
|
"mov r5, #0\n" \
|
|
"mov r6, #2\n" \
|
|
"mov r7, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r4, #1\n" \
|
|
"mov r5, #0\n" \
|
|
"mov r6, #2\n" \
|
|
"mov r7, #0\n" \
|
|
"mov r0, %0\n" \
|
|
"mov r4, %0\n" \
|
|
"cmp r0, #0\n" \
|
|
"cmp r0, #0\n" \
|
|
"cmp r0, #0\n" \
|
|
"beq 4f\n" \
|
|
"bne 6f\n" \
|
|
"bne 6f\n" \
|
|
"bne 6f\n" \
|
|
"bne 6f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"6:\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"beq 4f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"4:\n" \
|
|
"cmp r4, #0\n" \
|
|
"cmp r4, #0\n" \
|
|
"cmp r4, #0\n" \
|
|
"beq 5f\n" \
|
|
"bne 7f\n" \
|
|
"bne 7f\n" \
|
|
"bne 7f\n" \
|
|
"bne 7f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"7:\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"beq 5f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"5:\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"beq 8f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"8:\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"cmp r4, #1\n" \
|
|
"beq 9f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"9:\n" \
|
|
"cmp r0, r4\n" \
|
|
"cmp r0, r4\n" \
|
|
"cmp r0, r4\n" \
|
|
"beq 10f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"10:\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"cmp r0, #1\n" \
|
|
"beq 11f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"bne 1f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"11:\n" \
|
|
"b 2f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"1:\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r5, r0\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"mov r0, #1\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r7, r0\n" \
|
|
"mov r7, r0\n" \
|
|
"mov r7, r0\n" \
|
|
"cmp r5, r7\n" \
|
|
"cmp r5, r7\n" \
|
|
"cmp r5, r7\n" \
|
|
"beq 12f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"12:\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r4, r0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"bl wolfBoot_get_image_version\n" \
|
|
"mov r6, r0\n" \
|
|
"mov r6, r0\n" \
|
|
"mov r6, r0\n" \
|
|
"cmp r4, r6\n" \
|
|
"cmp r4, r6\n" \
|
|
"cmp r4, r6\n" \
|
|
"beq 13f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"bne 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"13:\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"mov r0, #0\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"blo 14f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"14:\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"blo 15f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"15:\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"cmp r4, r5\n" \
|
|
"blo 16f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"16:\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"cmp r6, r7\n" \
|
|
"blo 17f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"bhs 3f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"17:\n" \
|
|
"b 2f\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"3:\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"b .\n" \
|
|
"2:\n" \
|
|
"pop {r4, r5, r6, r7}\n" \
|
|
: /* No output operands */ \
|
|
: "r"(fb_ok) /* Input operands */ \
|
|
: "r0", "r4", "r5", "r6", "r7" /* Clobbered registers */ \
|
|
); \
|
|
} while (0)
|
|
#endif
|
|
|
|
|
|
#define CONFIRM_MASK_VALID(id, mask) \
|
|
asm volatile("mov r1, %0" :: "r"(id):"r1"); \
|
|
/* id &= 0x0F */ \
|
|
asm volatile("and.w r1, r1, #15":::"r1"); \
|
|
asm volatile("mov r0, %0" :: "r"(mask):"r0"); \
|
|
asm volatile("movs r2, #1":::"r2"); \
|
|
asm volatile("lsls r2, r1":::"r2","cc"); \
|
|
asm volatile("ands r2, r0":::"r2","cc"); \
|
|
asm volatile("movs r0, #1":::"cc"); \
|
|
asm volatile("lsls r0, r1":::"r0","cc"); \
|
|
asm volatile("cmp r0, r2"); \
|
|
asm volatile("cmp r0, r2"); \
|
|
asm volatile("cmp r0, r2"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("mov r0, %0" :: "r"(mask)); \
|
|
asm volatile("movs r2, #1":::"r2"); \
|
|
asm volatile("lsls r2, r1":::"r2", "cc"); \
|
|
asm volatile("ands r2, r0":::"r2", "cc"); \
|
|
asm volatile("movs r0, #1":::"r0"); \
|
|
asm volatile("lsls r0, r1":::"r0", "cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
asm volatile("mov r0, %0" :: "r"(mask):"r0"); \
|
|
asm volatile("movs r2, #1":::"r2"); \
|
|
asm volatile("lsls r2, r1":::"r2", "cc"); \
|
|
asm volatile("ands r2, r0":::"r2", "cc"); \
|
|
asm volatile("movs r0, #1":::"r0"); \
|
|
asm volatile("lsls r0, r1":::"r0", "cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("cmp r0, r2":::"cc"); \
|
|
asm volatile("bne ."); \
|
|
|
|
#else
|
|
|
|
struct wolfBoot_image {
|
|
uint8_t *hdr;
|
|
#ifdef EXT_FLASH
|
|
uint8_t *hdr_cache;
|
|
#endif
|
|
uint8_t *trailer;
|
|
uint8_t *sha_hash;
|
|
uint8_t *fw_base;
|
|
uint32_t fw_size;
|
|
uint8_t part;
|
|
uint8_t hdr_ok : 1;
|
|
uint8_t signature_ok : 1;
|
|
uint8_t sha_ok : 1;
|
|
uint8_t not_ext : 1; /* image is no longer external */
|
|
};
|
|
|
|
|
|
/* do not warn if this is not used */
|
|
static void UNUSEDFUNCTION wolfBoot_image_confirm_signature_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->signature_ok = 1;
|
|
}
|
|
static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->signature_ok = 0;
|
|
}
|
|
static void UNUSEDFUNCTION wolfBoot_image_confirm_sha_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->sha_ok = 1;
|
|
}
|
|
static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok(
|
|
struct wolfBoot_image *img)
|
|
{
|
|
img->sha_ok = 0;
|
|
}
|
|
static void UNUSEDFUNCTION wolfBoot_image_set_fw_base(
|
|
struct wolfBoot_image *img, void *base)
|
|
{
|
|
img->fw_base = (uint8_t *)base;
|
|
}
|
|
|
|
#define likely(x) (x)
|
|
#define unlikely(x) (x)
|
|
|
|
#define VERIFY_FN(img,p_res,fn,...) {\
|
|
int ret = fn(__VA_ARGS__); \
|
|
if ((ret == 0) && (*p_res == 1)) \
|
|
wolfBoot_image_confirm_signature_ok(img); \
|
|
}
|
|
|
|
#define RSA_VERIFY_FN(ret,fn,...) \
|
|
ret = fn(__VA_ARGS__);
|
|
|
|
#define RSA_VERIFY_HASH(img,digest) \
|
|
if (image_CT_compare(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE) == 0) \
|
|
wolfBoot_image_confirm_signature_ok(img);
|
|
|
|
#define RSA_PSS_VERIFY_HASH(img, pss_data, pss_data_sz, hash_type) \
|
|
if (wc_RsaPSS_CheckPadding(img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, \
|
|
pss_data, pss_data_sz, hash_type) == 0) \
|
|
wolfBoot_image_confirm_signature_ok(img);
|
|
|
|
#define SHA_OK(imgp) ((imgp)->sha_ok == 1)
|
|
|
|
#define VERIFY_INTEGRITY_FN(img, computed_digest, stored) \
|
|
if (image_CT_compare((computed_digest), (stored), \
|
|
WOLFBOOT_SHA_DIGEST_SIZE) == 0) { \
|
|
(img)->sha_hash = (stored); \
|
|
wolfBoot_image_confirm_sha_ok(img); \
|
|
}
|
|
|
|
#define PART_SANITY_CHECK(p) \
|
|
if (((p)->hdr_ok != 1) || ((p)->sha_ok != 1) || ((p)->signature_ok != 1)) \
|
|
wolfBoot_panic()
|
|
|
|
#define SHA_SANITY_CHECK(p) \
|
|
if ((p)->sha_ok != 1) \
|
|
wolfBoot_panic()
|
|
|
|
#define FW_BASE_SANITY_CHECK(p) do{} while(0)
|
|
|
|
#define CONFIRM_MASK_VALID(id, mask) \
|
|
if ((mask & (1UL << id)) != (1UL << id)) \
|
|
wolfBoot_panic()
|
|
|
|
#define VERIFY_VERSION_ALLOWED(fb_ok) do{} while(0) /* okay */
|
|
|
|
#endif
|
|
|
|
/* Defined in image.c */
|
|
int image_CT_compare(const uint8_t *expected, const uint8_t *actual,
|
|
uint32_t len);
|
|
int wolfBoot_hardened_CT_compare(const uint8_t *expected, const uint8_t *actual,
|
|
uint32_t len);
|
|
#if defined(MMU) || defined(WOLFBOOT_FDT)
|
|
/* Validate a DTB and return its size. `capacity` is the number of bytes
|
|
* readable at dts_addr. Callers holding only a header use fdt_peek_size(). */
|
|
int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity);
|
|
int wolfBoot_verify_dts_digest(const uint8_t *expected_digest,
|
|
const void *dts_addr, uint32_t dts_size);
|
|
#endif
|
|
int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part);
|
|
#ifdef EXT_FLASH
|
|
int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr);
|
|
#endif
|
|
int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image);
|
|
#ifdef WOLFBOOT_SELF_HEADER
|
|
int wolfBoot_open_self(struct wolfBoot_image *img);
|
|
int wolfBoot_open_self_address(struct wolfBoot_image *img, uint8_t *hdr,
|
|
uint8_t *image);
|
|
#endif
|
|
int wolfBoot_verify_integrity(struct wolfBoot_image *img);
|
|
int wolfBoot_verify_authenticity(struct wolfBoot_image *img);
|
|
int wolfBoot_set_partition_state(uint8_t part, uint8_t newst);
|
|
#ifdef WOLFBOOT_FIXED_PARTITIONS
|
|
int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag);
|
|
int wolfBoot_set_update_sector_flag(uint16_t sector, uint8_t newflag);
|
|
#endif /* WOLFBOOT_FIXED_PARTITIONS */
|
|
|
|
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
|
|
/* Support for ELF scatter/gather format */
|
|
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out,
|
|
int ext_flash);
|
|
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out);
|
|
#endif
|
|
|
|
uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
|
|
uint32_t* sz);
|
|
|
|
/* 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 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);
|
|
|
|
#ifdef EXT_FLASH
|
|
# ifdef PART_BOOT_EXT
|
|
# define BOOT_EXT 1
|
|
# else
|
|
# define BOOT_EXT 0
|
|
# endif
|
|
# ifdef PART_UPDATE_EXT
|
|
# define UPDATE_EXT 1
|
|
# else
|
|
# define UPDATE_EXT 0
|
|
# endif
|
|
# ifdef PART_SWAP_EXT
|
|
# define SWAP_EXT 1
|
|
# else
|
|
# define SWAP_EXT 0
|
|
# endif
|
|
# if defined(WOLFBOOT_SELF_HEADER_EXT)
|
|
# define SELF_HEADER_EXT 1
|
|
# else
|
|
# define SELF_HEADER_EXT 0
|
|
# endif
|
|
# define PARTN_IS_EXT(pn) \
|
|
((pn == PART_BOOT || pn == PART_DTS_BOOT) ? BOOT_EXT: \
|
|
((pn == PART_UPDATE || pn == PART_DTS_UPDATE) ? UPDATE_EXT : \
|
|
((pn == PART_SWAP) ? SWAP_EXT : \
|
|
((pn == PART_SELF) ? SELF_HEADER_EXT : 0))))
|
|
# define PART_IS_EXT(x) (!(x)->not_ext) && PARTN_IS_EXT(((x)->part))
|
|
|
|
|
|
#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST))
|
|
#define ext_flash_check_write ext_flash_encrypt_write
|
|
#define ext_flash_check_read ext_flash_decrypt_read
|
|
#else
|
|
#define ext_flash_check_write ext_flash_write
|
|
#define ext_flash_check_read ext_flash_read
|
|
#endif
|
|
|
|
static inline int wb_flash_erase(struct wolfBoot_image *img, uint32_t off,
|
|
uint32_t size)
|
|
{
|
|
if (PART_IS_EXT(img))
|
|
return ext_flash_erase((uintptr_t)(img->hdr) + off, size);
|
|
else
|
|
return hal_flash_erase((uintptr_t)(img->hdr) + off, size);
|
|
}
|
|
|
|
static inline int wb_flash_write(struct wolfBoot_image *img, uint32_t off,
|
|
const void *data, uint32_t size)
|
|
{
|
|
if (PART_IS_EXT(img))
|
|
return ext_flash_check_write((uintptr_t)(img->hdr) + off, data, size);
|
|
else
|
|
return hal_flash_write((uintptr_t)(img->hdr) + off, data, size);
|
|
}
|
|
|
|
static inline int wb_flash_write_verify_word(struct wolfBoot_image *img,
|
|
uint32_t off, uint32_t word)
|
|
{
|
|
int ret;
|
|
volatile uint32_t copy;
|
|
if (PART_IS_EXT(img))
|
|
{
|
|
ext_flash_check_read((uintptr_t)(img->hdr) + off, (void *)©,
|
|
sizeof(uint32_t));
|
|
while (copy != word) {
|
|
ret = ext_flash_check_write((uintptr_t)(img->hdr) + off,
|
|
(void *)&word, sizeof(uint32_t));
|
|
if (ret < 0)
|
|
return ret;
|
|
ext_flash_check_read((uintptr_t)(img->hdr) + off, (void *)©,
|
|
sizeof(uint32_t));
|
|
}
|
|
} else {
|
|
volatile uint32_t *pcopy = (volatile uint32_t*)(img->hdr + off);
|
|
while(*pcopy != word) {
|
|
hal_flash_write((uintptr_t)pcopy, (void *)&word, sizeof(uint32_t));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
# define SWAP_EXT (0)
|
|
# define BOOT_EXT (0)
|
|
# define UPDATE_EXT (0)
|
|
# define PART_IS_EXT(x) (0)
|
|
# define PARTN_IS_EXT(x) (0)
|
|
# define wb_flash_erase(im, of, siz) \
|
|
hal_flash_erase(((uintptr_t)(((im)->hdr)) + of), siz)
|
|
# define wb_flash_write(im, of, dat, siz) \
|
|
hal_flash_write(((uintptr_t)((im)->hdr)) + of, dat, siz)
|
|
|
|
#endif /* EXT_FLASH */
|
|
|
|
/* -- Image Formats -- */
|
|
/* Legacy U-Boot Image */
|
|
#ifdef BIG_ENDIAN_ORDER
|
|
#define UBOOT_IMG_HDR_MAGIC 0x27051956UL
|
|
#else
|
|
#define UBOOT_IMG_HDR_MAGIC 0x56190527UL
|
|
#endif
|
|
#define UBOOT_IMG_HDR_SZ 64
|
|
|
|
/* --- Flattened Device Tree Blob */
|
|
#if defined(MMU) || defined(WOLFBOOT_FDT)
|
|
#include "fdt.h"
|
|
#endif
|
|
|
|
#ifndef EXT_ENCRYPTED
|
|
#define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - \
|
|
(TRAILER_SKIP + sizeof(uint32_t) + \
|
|
((WOLFBOOT_PARTITION_SIZE + 1) / (WOLFBOOT_SECTOR_SIZE * 8))))
|
|
#else
|
|
#define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - ENCRYPT_TMP_SECRET_OFFSET)
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* !IMAGE_H */
|