Implement new swap scheme for devices with large erase size using scratch with status area
diff --git a/boot/bootutil/include/bootutil/bootutil.h b/boot/bootutil/include/bootutil/bootutil.h
index b45334a..cf08a59 100644
--- a/boot/bootutil/include/bootutil/bootutil.h
+++ b/boot/bootutil/include/bootutil/bootutil.h
@@ -72,6 +72,12 @@
struct boot_loader_state;
fih_int context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp);
+int boot_swap_type_multi(int image_index);
+int boot_swap_type(void);
+
+int boot_set_pending(int permanent);
+int boot_set_confirmed(void);
+
#define SPLIT_GO_OK (0)
#define SPLIT_GO_NON_MATCHING (-1)
#define SPLIT_GO_ERR (-2)
diff --git a/boot/bootutil/include/bootutil/caps.h b/boot/bootutil/include/bootutil/caps.h
index 8ead3be..77182a1 100644
--- a/boot/bootutil/include/bootutil/caps.h
+++ b/boot/bootutil/include/bootutil/caps.h
@@ -47,6 +47,7 @@
#define BOOTUTIL_CAP_DOWNGRADE_PREVENTION (1<<12)
#define BOOTUTIL_CAP_ENC_X25519 (1<<13)
#define BOOTUTIL_CAP_BOOTSTRAP (1<<14)
+#define BOOTUTIL_CAP_SWAP_USING_STATUS (1<<15)
/*
* Query the number of images this bootloader is configured for. This
diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h
index 779b0d4..1de6f81 100644
--- a/boot/bootutil/include/bootutil/enc_key.h
+++ b/boot/bootutil/include/bootutil/enc_key.h
@@ -45,6 +45,7 @@
struct enc_key_data {
uint8_t valid;
+ uint8_t aes_iv[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE];
bootutil_aes_ctr_context aes_ctr;
};
@@ -58,7 +59,7 @@
int boot_enc_load(struct enc_key_data *enc_state, int image_index,
const struct image_header *hdr, const struct flash_area *fap,
struct boot_status *bs);
-int boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey);
+int boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey, uint32_t sz, uint8_t *enciv);
bool boot_enc_valid(struct enc_key_data *enc_state, int image_index,
const struct flash_area *fap);
void boot_encrypt(struct enc_key_data *enc_state, int image_index,
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index 9ee317f..221d8a6 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -41,6 +41,12 @@
#include "bootutil/enc_key.h"
#endif
+#ifdef MCUBOOT_SWAP_USING_STATUS
+#include "swap_status.h"
+#endif
+
+#include "mcuboot_config/mcuboot_config.h"
+
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
/* Currently only used by imgmgr */
@@ -134,6 +140,7 @@
return -1;
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
uint32_t
boot_status_off(const struct flash_area *fap)
{
@@ -147,6 +154,7 @@
assert(off_from_end <= fap->fa_size);
return fap->fa_size - off_from_end;
}
+#endif
static inline uint32_t
boot_magic_off(const struct flash_area *fap)
@@ -154,6 +162,8 @@
return fap->fa_size - BOOT_MAGIC_SZ;
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
+
static inline uint32_t
boot_image_ok_off(const struct flash_area *fap)
{
@@ -171,6 +181,7 @@
{
return boot_swap_info_off(fap) - BOOT_MAX_ALIGN;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
#ifdef MCUBOOT_ENC_IMAGES
static inline uint32_t
@@ -185,6 +196,7 @@
}
#endif
+#ifndef MCUBOOT_SWAP_USING_STATUS
/**
* This functions tries to locate the status area after an aborted swap,
* by looking for the magic in the possible locations.
@@ -272,6 +284,8 @@
if (rc == 0) {
off = boot_enc_key_off(fap, slot);
#if MCUBOOT_SWAP_SAVE_ENCTLV
+ uint8_t aes_iv[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE];
+
rc = flash_area_read(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
if (rc == 0) {
for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) {
@@ -281,7 +295,7 @@
}
/* Only try to decrypt non-erased TLV metadata */
if (i != BOOT_ENC_TLV_ALIGN_SIZE) {
- rc = boot_enc_decrypt(bs->enctlv[slot], bs->enckey[slot]);
+ rc = boot_enc_decrypt(bs->enctlv[slot], bs->enckey[slot], 0, aes_iv);
}
}
#else
@@ -294,6 +308,8 @@
}
#endif
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
+
int
boot_write_copy_done(const struct flash_area *fap)
{
@@ -318,6 +334,8 @@
return boot_write_trailer(fap, off, (const uint8_t *) &swap_size, 4);
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
+
#ifdef MCUBOOT_ENC_IMAGES
int
boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
@@ -342,3 +360,5 @@
return 0;
}
#endif
+
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index d2ebca2..37a9350 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -217,6 +217,13 @@
size_t num_sectors;
} scratch;
#endif
+#if MCUBOOT_SWAP_USING_STATUS
+ struct {
+ const struct flash_area *area;
+ boot_sector_t *sectors;
+ size_t num_sectors;
+ } status;
+#endif
uint8_t swap_type[BOOT_IMAGE_NUMBER];
uint32_t write_sz;
@@ -240,6 +247,7 @@
uint32_t boot_trailer_sz(uint32_t min_write_sz);
int boot_status_entries(int image_index, const struct flash_area *fap);
uint32_t boot_status_off(const struct flash_area *fap);
+uint32_t boot_swap_info_off(const struct flash_area *fap);
int boot_read_swap_state(const struct flash_area *fap,
struct boot_swap_state *state);
int boot_read_swap_state_by_id(int flash_area_id,
@@ -267,6 +275,13 @@
int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz);
bool boot_status_is_reset(const struct boot_status *bs);
+#ifdef MCUBOOT_SWAP_USING_STATUS
+uint32_t boot_copy_done_off(const struct flash_area *fap);
+uint32_t boot_image_ok_off(const struct flash_area *fap);
+uint32_t boot_swap_size_off(const struct flash_area *fap);
+#endif
+
+
#ifdef MCUBOOT_ENC_IMAGES
int boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
const struct boot_status *bs);
diff --git a/boot/bootutil/src/bootutil_public.c b/boot/bootutil/src/bootutil_public.c
index 793c4e3..6057b41 100644
--- a/boot/bootutil/src/bootutil_public.c
+++ b/boot/bootutil/src/bootutil_public.c
@@ -46,6 +46,7 @@
#include "bootutil/image.h"
#include "bootutil/bootutil_public.h"
#include "bootutil/bootutil_log.h"
+#include "swap_status.h"
#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key_public.h"
#endif
@@ -117,6 +118,7 @@
#define BOOT_SWAP_TABLES_COUNT \
(sizeof boot_swap_tables / sizeof boot_swap_tables[0])
+#ifndef MCUBOOT_SWAP_USING_STATUS
static int
boot_magic_decode(const uint32_t *magic)
{
@@ -125,6 +127,7 @@
}
return BOOT_MAGIC_BAD;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
static int
boot_flag_decode(uint8_t flag)
@@ -135,6 +138,7 @@
return BOOT_FLAG_SET;
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
static inline uint32_t
boot_magic_off(const struct flash_area *fap)
{
@@ -153,17 +157,12 @@
return boot_image_ok_off(fap) - BOOT_MAX_ALIGN;
}
-static inline uint32_t
-boot_swap_size_off(const struct flash_area *fap)
-{
- return boot_swap_info_off(fap) - BOOT_MAX_ALIGN;
-}
-
uint32_t
boot_swap_info_off(const struct flash_area *fap)
{
return boot_copy_done_off(fap) - BOOT_MAX_ALIGN;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
/**
* Determines if a status source table is satisfied by the specified magic
@@ -191,19 +190,6 @@
}
}
-#ifdef MCUBOOT_ENC_IMAGES
-static inline uint32_t
-boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
-{
-#if MCUBOOT_SWAP_SAVE_ENCTLV
- return boot_swap_size_off(fap) - ((slot + 1) *
- ((((BOOT_ENC_TLV_SIZE - 1) / BOOT_MAX_ALIGN) + 1) * BOOT_MAX_ALIGN));
-#else
- return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_KEY_SIZE);
-#endif
-}
-#endif
-
bool bootutil_buffer_is_erased(const struct flash_area *area,
const void *buffer, size_t len)
{
@@ -249,6 +235,7 @@
return boot_read_flag(fap, copy_done, boot_copy_done_off(fap));
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
int
boot_read_swap_state(const struct flash_area *fap,
@@ -294,6 +281,8 @@
return boot_read_image_ok(fap, &state->image_ok);
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
+
int
boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state)
{
@@ -310,6 +299,8 @@
return rc;
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
+
int
boot_write_magic(const struct flash_area *fap)
{
@@ -361,6 +352,8 @@
return 0;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
+
int
boot_write_trailer_flag(const struct flash_area *fap, uint32_t off,
uint8_t flag_val)
@@ -408,6 +401,17 @@
return boot_write_trailer(fap, off, (const uint8_t *) &swap_info, 1);
}
+#define BOOT_LOG_SWAP_STATE(area, state) \
+ BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
+ "image_ok=0x%x", \
+ (area), \
+ ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
+ (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
+ "bad"), \
+ (state)->swap_type, \
+ (state)->copy_done, \
+ (state)->image_ok)
+
int
boot_swap_type_multi(int image_index)
{
@@ -429,6 +433,9 @@
return BOOT_SWAP_TYPE_PANIC;
}
+ BOOT_LOG_SWAP_STATE("boot_swap_type_multi: Primary image", &primary_slot);
+ BOOT_LOG_SWAP_STATE("boot_swap_type_multi: Secondary image", &secondary_slot);
+
for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
table = boot_swap_tables + i;
diff --git a/boot/bootutil/src/caps.c b/boot/bootutil/src/caps.c
index a21bc09..2033056 100644
--- a/boot/bootutil/src/caps.c
+++ b/boot/bootutil/src/caps.c
@@ -44,6 +44,8 @@
res |= BOOTUTIL_CAP_OVERWRITE_UPGRADE;
#elif defined(MCUBOOT_SWAP_USING_MOVE)
res |= BOOTUTIL_CAP_SWAP_USING_MOVE;
+#elif defined(MCUBOOT_SWAP_USING_STATUS)
+ res |= BOOTUTIL_CAP_SWAP_USING_STATUS;
#else
res |= BOOTUTIL_CAP_SWAP_USING_SCRATCH;
#endif
diff --git a/boot/bootutil/src/crc32c.c b/boot/bootutil/src/crc32c.c
new file mode 100644
index 0000000..d298a1f
--- /dev/null
+++ b/boot/bootutil/src/crc32c.c
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "crc32c.h"
+
+#define NIBBLE_POS (4u)
+#define NIBBLE_MSK (0xFu)
+#define CRC_TABLE_SIZE (16u) /* A number of uint32_t elements in the CRC32 table */
+#define CRC_INIT (0xFFFFFFFFu)
+
+
+/*******************************************************************************
+* Function Name: crc32c_checksum
+****************************************************************************//**
+*
+* This function computes a CRC-32C for the provided number of bytes contained
+* in the provided buffer.
+*
+* \param address The pointer to a buffer containing the data to compute
+* the checksum for.
+* \param length The number of bytes in the buffer to compute the checksum
+* for.
+*
+* \return CRC-32C for the provided data.
+*
+*******************************************************************************/
+uint32_t crc32c_checksum(const uint8_t *address, uint32_t length)
+{
+ /* Contains generated values to calculate CRC-32C by 4 bits per iteration*/
+ static const uint32_t crcTable[CRC_TABLE_SIZE] =
+ {
+ 0x00000000u, 0x105ec76fu, 0x20bd8edeu, 0x30e349b1u,
+ 0x417b1dbcu, 0x5125dad3u, 0x61c69362u, 0x7198540du,
+ 0x82f63b78u, 0x92a8fc17u, 0xa24bb5a6u, 0xb21572c9u,
+ 0xc38d26c4u, 0xd3d3e1abu, 0xe330a81au, 0xf36e6f75u,
+ };
+
+ uint32_t crc = CRC_INIT;
+ if (length != 0u)
+ {
+ do
+ {
+ crc = crc ^ *address;
+ crc = (crc >> NIBBLE_POS) ^ crcTable[crc & NIBBLE_MSK];
+ crc = (crc >> NIBBLE_POS) ^ crcTable[crc & NIBBLE_MSK];
+ --length;
+ ++address;
+ } while (length != 0u);
+ }
+ return (~crc);
+}
diff --git a/boot/bootutil/src/crc32c.h b/boot/bootutil/src/crc32c.h
new file mode 100644
index 0000000..329b9d3
--- /dev/null
+++ b/boot/bootutil/src/crc32c.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef H_CRC32C_
+#define H_CRC32C_
+
+#include <stdint.h>
+
+uint32_t crc32c_checksum(const uint8_t *address, uint32_t length);
+
+#endif /* H_CRC32C_ */
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
index d14855b..12c0a9c 100644
--- a/boot/bootutil/src/encrypted.c
+++ b/boot/bootutil/src/encrypted.c
@@ -297,15 +297,16 @@
* @param ikm_len Length of the input data.
* @param info An information tag.
* @param info_len Length of the information tag.
+ * @param salt An salt tag.
+ * @param salt_len Length of the salt tag.
* @param okm Output of the KDF computation.
* @param okm_len On input the requested length; on output the generated length
*/
static int
-hkdf(uint8_t *ikm, uint16_t ikm_len, uint8_t *info, uint16_t info_len,
- uint8_t *okm, uint16_t *okm_len)
+hkdf(const uint8_t *ikm, uint16_t ikm_len, const uint8_t *info, uint16_t info_len,
+ const uint8_t *salt, uint16_t salt_len, uint8_t *okm, uint16_t *okm_len)
{
bootutil_hmac_sha256_context hmac;
- uint8_t salt[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
uint8_t prk[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
uint8_t T[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
uint16_t off;
@@ -324,8 +325,7 @@
bootutil_hmac_sha256_init(&hmac);
- memset(salt, 0, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
- rc = bootutil_hmac_sha256_set_key(&hmac, salt, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
+ rc = bootutil_hmac_sha256_set_key(&hmac, salt, salt_len);
if (rc != 0) {
goto error;
}
@@ -429,21 +429,25 @@
return 0;
}
-#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE
+#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE
#if defined(MCUBOOT_ENCRYPT_RSA)
-# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_RSA2048
+# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_RSA2048
+# define EXPECTED_ENC_EXT_LEN EXPECTED_ENC_TLV
#elif defined(MCUBOOT_ENCRYPT_KW)
-# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_KW128
+# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_KW128
+# define EXPECTED_ENC_EXT_LEN EXPECTED_ENC_TLV
#elif defined(MCUBOOT_ENCRYPT_EC256)
-# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_EC256
+# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_EC256
+# define EXPECTED_ENC_EXT_LEN (EXPECTED_ENC_LEN + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE)
# define EC_PUBK_INDEX (0)
# define EC_TAG_INDEX (65)
# define EC_CIPHERKEY_INDEX (65 + 32)
_Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
"Please fix ECIES-P256 component indexes");
#elif defined(MCUBOOT_ENCRYPT_X25519)
-# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519
+# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519
+# define EXPECTED_ENC_EXT_LEN EXPECTED_ENC_TLV
# define EC_PUBK_INDEX (0)
# define EC_TAG_INDEX (32)
# define EC_CIPHERKEY_INDEX (32 + 32)
@@ -451,14 +455,19 @@
"Please fix ECIES-X25519 component indexes");
#endif
+#ifndef EXPECTED_ENC_EXT_LEN
+#define EXPECTED_ENC_EXT_LEN EXPECTED_ENC_LEN
+#endif
+
/*
* Decrypt an encryption key TLV.
*
- * @param buf An encryption TLV read from flash (build time fixed length)
+ * @param buf An encryption TLV buffer red from flash
+ * @param sz An encryption TLV buffer data size
* @param enckey An AES-128 key sized buffer to store to plain key.
*/
int
-boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey)
+boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey, uint32_t sz, uint8_t *enciv)
{
#if defined(MCUBOOT_ENCRYPT_RSA)
mbedtls_rsa_context rsa;
@@ -475,14 +484,22 @@
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
bootutil_hmac_sha256_context hmac;
bootutil_aes_ctr_context aes_ctr;
+ uint8_t salt[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
uint8_t tag[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
uint8_t shared[SHARED_KEY_LEN];
- uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
+ uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE + BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE * 2];
uint8_t *cp;
uint8_t *cpend;
uint8_t private_key[PRIV_KEY_LEN];
uint8_t counter[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE];
uint16_t len;
+ uint16_t out_len;
+ const uint8_t *my_salt = salt;
+ uint8_t *my_key_iv = counter;
+ uint8_t *my_counter = counter;
+#else
+ (void)sz;
+ (void)enciv;
#endif
int rc = -1;
@@ -532,6 +549,7 @@
bootutil_ecdh_p256_init(&ecdh_p256);
rc = bootutil_ecdh_p256_shared_secret(&ecdh_p256, &buf[EC_PUBK_INDEX], private_key, shared);
+
bootutil_ecdh_p256_drop(&ecdh_p256);
if (rc != 0) {
return -1;
@@ -573,10 +591,31 @@
* Expand shared secret to create keys for AES-128-CTR + HMAC-SHA256
*/
+ /* Prepare for default encryption scheme with zero salt and AES IVs */
+ memset(counter, 0, BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE);
+ memset(salt, 0, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
+
len = BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE;
- rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16,
- derived_key, &len);
- if (rc != 0 || len != (BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE)) {
+
+ if (sz > EXPECTED_ENC_LEN) {
+ /* Use new enhanced encryption scheme with randomly generated salt and AES IVs */
+ len += BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE * 2;
+
+ my_salt = &buf[EXPECTED_ENC_LEN];
+
+ my_key_iv = &derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE +
+ BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
+
+ my_counter = &derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE +
+ BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE +
+ BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE];
+ }
+
+ out_len = len;
+ rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE,
+ my_salt, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE, derived_key, &out_len);
+
+ if (rc != 0 || len != out_len) {
return -1;
}
@@ -586,13 +625,13 @@
bootutil_hmac_sha256_init(&hmac);
- rc = bootutil_hmac_sha256_set_key(&hmac, &derived_key[16], 32);
+ rc = bootutil_hmac_sha256_set_key(&hmac, &derived_key[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE], BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
if (rc != 0) {
(void)bootutil_hmac_sha256_drop(&hmac);
return -1;
}
- rc = bootutil_hmac_sha256_update(&hmac, &buf[EC_CIPHERKEY_INDEX], 16);
+ rc = bootutil_hmac_sha256_update(&hmac, &buf[EC_CIPHERKEY_INDEX], BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE);
if (rc != 0) {
(void)bootutil_hmac_sha256_drop(&hmac);
return -1;
@@ -605,22 +644,20 @@
return -1;
}
- if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], 32) != 0) {
+ if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE) != 0) {
(void)bootutil_hmac_sha256_drop(&hmac);
return -1;
}
bootutil_hmac_sha256_drop(&hmac);
+ (void)memcpy(enciv, my_counter, BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE);
+
/*
* Finally decrypt the received ciphered key
*/
bootutil_aes_ctr_init(&aes_ctr);
- if (rc != 0) {
- bootutil_aes_ctr_drop(&aes_ctr);
- return -1;
- }
rc = bootutil_aes_ctr_set_key(&aes_ctr, derived_key);
if (rc != 0) {
@@ -628,8 +665,7 @@
return -1;
}
- memset(counter, 0, BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE);
- rc = bootutil_aes_ctr_decrypt(&aes_ctr, counter, &buf[EC_CIPHERKEY_INDEX], BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, 0, enckey);
+ rc = bootutil_aes_ctr_decrypt(&aes_ctr, my_key_iv, &buf[EC_CIPHERKEY_INDEX], BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, 0, enckey);
if (rc != 0) {
bootutil_aes_ctr_drop(&aes_ctr);
return -1;
@@ -658,7 +694,7 @@
#if MCUBOOT_SWAP_SAVE_ENCTLV
uint8_t *buf;
#else
- uint8_t buf[EXPECTED_ENC_LEN];
+ uint8_t buf[EXPECTED_ENC_EXT_LEN];
#endif
uint8_t slot;
int rc;
@@ -687,7 +723,7 @@
return rc;
}
- if (len != EXPECTED_ENC_LEN) {
+ if ((len < EXPECTED_ENC_LEN) || (len > EXPECTED_ENC_EXT_LEN)) {
return -1;
}
@@ -696,12 +732,12 @@
memset(buf, 0xff, BOOT_ENC_TLV_ALIGN_SIZE);
#endif
- rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN);
+ rc = flash_area_read(fap, off, buf, len);
if (rc) {
return -1;
}
- return boot_enc_decrypt(buf, bs->enckey[slot]);
+ return (boot_enc_decrypt(buf, bs->enckey[slot], len, enc_state[slot].aes_iv));
}
bool
@@ -726,7 +762,7 @@
uint32_t blk_off, uint8_t *buf)
{
struct enc_key_data *enc;
- uint8_t nonce[16];
+ uint8_t *nonce;
int rc;
/* boot_copy_region will call boot_encrypt with sz = 0 when skipping over
@@ -735,13 +771,6 @@
return;
}
- memset(nonce, 0, 12);
- off >>= 4;
- nonce[12] = (uint8_t)(off >> 24);
- nonce[13] = (uint8_t)(off >> 16);
- nonce[14] = (uint8_t)(off >> 8);
- nonce[15] = (uint8_t)off;
-
rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
if (rc < 0) {
assert(0);
@@ -750,6 +779,15 @@
enc = &enc_state[rc];
assert(enc->valid == 1);
+
+ nonce = enc->aes_iv;
+
+ off >>= 4;
+ nonce[12] = (uint8_t)(off >> 24);
+ nonce[13] = (uint8_t)(off >> 16);
+ nonce[14] = (uint8_t)(off >> 8);
+ nonce[15] = (uint8_t)off;
+
bootutil_aes_ctr_encrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf);
}
diff --git a/boot/bootutil/src/image_ec256.c b/boot/bootutil/src/image_ec256.c
index 5d73c9f..bdd72c6 100644
--- a/boot/bootutil/src/image_ec256.c
+++ b/boot/bootutil/src/image_ec256.c
@@ -97,7 +97,7 @@
}
return 0;
}
-#endif /* CY_MBEDTLS_HW_ACCELERATION */
+#else /* !CY_MBEDTLS_HW_ACCELERATION */
static int
bootutil_import_key(uint8_t **cp, uint8_t *end)
{
@@ -139,6 +139,7 @@
return 0;
}
+#endif /* CY_MBEDTLS_HW_ACCELERATION */
#ifndef MCUBOOT_ECDSA_NEED_ASN1_SIG
/*
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index f066add..18967c5 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -490,6 +490,7 @@
fih_rc = fih_int_encode_zero_equality(img_security_cnt <
fih_int_decode(security_cnt));
if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
+ /* The image's security counter is not accepted. */
goto out;
}
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index e1f1e54..e4c0a3f 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -106,7 +106,7 @@
* Compute the total size of the given image. Includes the size of
* the TLVs.
*/
-#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST)
+#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST)
static int
boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size)
{
@@ -166,6 +166,7 @@
#endif /* !MCUBOOT_OVERWRITE_ONLY */
#if !defined(MCUBOOT_RAM_LOAD)
+
static uint32_t
boot_write_sz(struct boot_loader_state *state)
{
@@ -241,6 +242,11 @@
out_sectors = state->scratch.sectors;
out_num_sectors = &state->scratch.num_sectors;
#endif
+#if MCUBOOT_SWAP_USING_STATUS
+ } else if (flash_area == FLASH_AREA_IMAGE_SWAP_STATUS) {
+ out_sectors = state->status.sectors;
+ out_num_sectors = &state->status.num_sectors;
+#endif
} else {
return BOOT_EFLASH;
}
@@ -278,6 +284,13 @@
return BOOT_EFLASH;
}
+#if MCUBOOT_SWAP_USING_STATUS
+ rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SWAP_STATUS);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+#endif
+
#if MCUBOOT_SWAP_USING_SCRATCH
rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SCRATCH);
if (rc != 0) {
@@ -318,6 +331,7 @@
bs->state == BOOT_STATUS_STATE_0);
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
/**
* Writes the supplied boot status to the flash file system. The boot status
* contains the current state of an in-progress image copy operation.
@@ -381,6 +395,9 @@
return rc;
}
#endif /* !MCUBOOT_RAM_LOAD */
+
+#endif /* MCUBOOT_SWAP_USING_STATUS */
+
#endif /* !MCUBOOT_DIRECT_XIP */
/*
@@ -525,6 +542,8 @@
#if (BOOT_IMAGE_NUMBER > 1) || \
defined(MCUBOOT_DIRECT_XIP) || \
defined(MCUBOOT_RAM_LOAD) || \
+ defined(MCUBOOT_SWAP_USING_SCRATCH) || \
+ defined(MCUBOOT_SWAP_USING_MOVE) || \
(defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION))
/**
* Compare image version numbers not including the build number
@@ -620,6 +639,8 @@
FIH_RET(fih_rc);
}
+ BOOT_LOG_DBG("> boot_validate_slot: fa_id = %d", fap->fa_id);
+
hdr = boot_img_hdr(state, slot);
if (boot_check_header_erased(state, slot) == 0 ||
(hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) {
@@ -635,11 +656,14 @@
* through mcumgr; so we just get rid of the trailer here, if the header
* is erased.
*/
+ BOOT_LOG_DBG(" * Fix the secondary slot when image is invalid.");
if (slot != BOOT_PRIMARY_SLOT) {
+ BOOT_LOG_DBG(" * Erase secondary image trailer.");
swap_erase_trailer_sectors(state, fap);
}
#endif
+ BOOT_LOG_DBG(" * No bootable image in slot(%d); continue booting from the primary slot.", slot);
/* No bootable image in slot; continue booting from the primary slot. */
fih_rc = fih_int_encode(1);
goto out;
@@ -666,6 +690,7 @@
FIH_CALL(boot_image_check, fih_rc, state, hdr, fap, bs);
if (!boot_is_header_valid(hdr, fap) || fih_not_eq(fih_rc, FIH_SUCCESS)) {
if ((slot != BOOT_PRIMARY_SLOT) || ARE_SLOTS_EQUIVALENT()) {
+ BOOT_LOG_DBG(" * Image in the secondary slot is invalid. Erase the image");
flash_area_erase(fap, 0, fap->fa_size);
/* Image is invalid, erase it to prevent further unnecessary
* attempts to validate and boot it.
@@ -679,9 +704,11 @@
goto out;
}
+ /* Image in the secondary slot is valid. */
+
out:
flash_area_close(fap);
-
+ BOOT_LOG_DBG("< boot_validate_slot = %d", fih_int_decode(fih_rc));
FIH_RET(fih_rc);
}
@@ -834,23 +861,27 @@
#ifdef MCUBOOT_ENC_IMAGES
image_index = BOOT_CURR_IMG(state);
- if ((fap_src->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index) ||
- fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) &&
+ if ((fap_src->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index) ||
+ fap_dst->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) &&
+
+ !(fap_src->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index) &&
+ fap_dst->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) &&
!(fap_src->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index) &&
- fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index))) {
- /* assume the secondary slot as src, needs decryption */
- hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
+ fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)))
+ {
+ /* assume the primary slot as src, needs encryption */
+ hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
#if !defined(MCUBOOT_SWAP_USING_MOVE)
off = off_src;
- if (fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
- /* might need encryption (metadata from the primary slot) */
- hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
+ if (fap_dst->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) {
+ /* might need decryption (metadata from the secondary slot) */
+ hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
off = off_dst;
}
#else
off = off_dst;
- if (fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
- hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
+ if (fap_dst->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) {
+ hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
}
#endif
if (IS_ENCRYPTED(hdr)) {
@@ -859,8 +890,13 @@
if (off + bytes_copied < hdr->ih_hdr_size) {
/* do not decrypt header */
blk_off = 0;
- blk_sz = chunk_sz - hdr->ih_hdr_size;
- idx = hdr->ih_hdr_size;
+ if(chunk_sz > hdr->ih_hdr_size) {
+ blk_sz = chunk_sz - hdr->ih_hdr_size;
+ idx = hdr->ih_hdr_size - (off + bytes_copied);
+ } else {
+ /* still in header-area, no need to decrypt */
+ blk_sz = 0;
+ }
} else {
blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
}
@@ -873,9 +909,11 @@
blk_sz = tlv_off - (off + bytes_copied);
}
}
- boot_encrypt(BOOT_CURR_ENC(state), image_index, fap_src,
+ if(0 != blk_sz) {
+ boot_encrypt(BOOT_CURR_ENC(state), image_index, fap_src,
(off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz,
blk_off, &buf[idx]);
+ }
}
}
#endif
@@ -949,8 +987,12 @@
assert (rc == 0);
sect_count = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
+ BOOT_LOG_DBG(" * primary slot sectors: %d", sect_count);
for (sect = 0, size = 0; sect < sect_count; sect++) {
this_size = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sect);
+ /* BOOT_LOG_DBG(" * primary slot erase region (0x%0lx / 0x%0lx): sector = %d, off = %0x, size = %d",
+ fap_primary_slot->fa_off, fap_primary_slot->fa_size,
+ sect, size, this_size); */
rc = boot_erase_region(fap_primary_slot, size, this_size);
assert(rc == 0);
@@ -994,7 +1036,7 @@
}
#endif
- BOOT_LOG_INF("Copying the secondary slot to the primary slot: 0x%zx bytes",
+ BOOT_LOG_INF("Copying the secondary slot to the primary slot: 0x%x bytes",
size);
rc = boot_copy_region(state, fap_secondary_slot, fap_primary_slot, 0, 0, size);
if (rc != 0) {
@@ -1326,7 +1368,7 @@
if (rc == 0) {
/* All dependencies've been satisfied, continue with next image. */
BOOT_CURR_IMG(state)++;
- } else {
+ } else if (rc == BOOT_EBADVERSION) {
/* Cannot upgrade due to non-met dependencies, so disable all
* image upgrades.
*/
@@ -1335,6 +1377,9 @@
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE;
}
break;
+ } else {
+ /* Other error happened, images are inconsistent */
+ return rc;
}
}
return rc;
@@ -1356,6 +1401,8 @@
uint8_t swap_type;
#endif
+ BOOT_LOG_DBG("> boot_perform_update: bs->idx = %d", bs->idx);
+
/* At this point there are no aborted swaps. */
#if defined(MCUBOOT_OVERWRITE_ONLY)
rc = boot_copy_image(state, bs);
@@ -1551,6 +1598,8 @@
int rc;
fih_int fih_rc = FIH_FAILURE;
+ BOOT_LOG_DBG("> boot_prepare_image_for_update: image = %d", BOOT_CURR_IMG(state));
+
/* Determine the sector layout of the image slots and scratch area. */
rc = boot_read_sectors(state);
if (rc != 0) {
@@ -1565,6 +1614,7 @@
/* Attempt to read an image header from each slot. */
rc = boot_read_image_headers(state, false, NULL);
+ BOOT_LOG_DBG(" * Read an image (%d) header from each slot: rc = %d", BOOT_CURR_IMG(state), rc);
if (rc != 0) {
/* Continue with next image if there is one. */
BOOT_LOG_WRN("Failed reading image headers; Image=%u",
@@ -1590,12 +1640,13 @@
}
#endif
-#ifdef MCUBOOT_SWAP_USING_MOVE
+#if defined (MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH)
/*
* Must re-read image headers because the boot status might
* have been updated in the previous function call.
*/
rc = boot_read_image_headers(state, !boot_status_is_reset(bs), bs);
+ BOOT_LOG_DBG(" * re-read image(%d) headers: rc = %d.", BOOT_CURR_IMG(state), rc);
#ifdef MCUBOOT_BOOTSTRAP
/* When bootstrapping it's OK to not have image magic in the primary slot */
if (rc != 0 && (BOOT_CURR_IMG(state) != BOOT_PRIMARY_SLOT ||
@@ -1603,7 +1654,6 @@
#else
if (rc != 0) {
#endif
-
/* Continue with next image if there is one. */
BOOT_LOG_WRN("Failed reading image headers; Image=%u",
BOOT_CURR_IMG(state));
@@ -1642,6 +1692,8 @@
/* Swap has finished set to NONE */
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE;
} else {
+ BOOT_LOG_DBG(" * There was no partial swap, determine swap type.");
+
/* There was no partial swap, determine swap type. */
if (bs->swap_type == BOOT_SWAP_TYPE_NONE) {
BOOT_SWAP_TYPE(state) = boot_validated_swap_type(state, bs);
@@ -1693,6 +1745,7 @@
/* In that case if slots are not compatible. */
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE;
}
+ BOOT_LOG_DBG("< boot_prepare_image_for_update");
}
fih_int
@@ -1716,6 +1769,9 @@
#if MCUBOOT_SWAP_USING_SCRATCH
TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
#endif
+#if MCUBOOT_SWAP_USING_STATUS
+ TARGET_STATIC boot_sector_t status_sectors[BOOT_MAX_SWAP_STATUS_SECTORS];
+#endif
memset(state, 0, sizeof(struct boot_loader_state));
has_upgrade = false;
@@ -1747,6 +1803,9 @@
#if MCUBOOT_SWAP_USING_SCRATCH
state->scratch.sectors = scratch_sectors;
#endif
+#if MCUBOOT_SWAP_USING_STATUS
+ state->status.sectors = status_sectors;
+#endif
/* Open primary and secondary image areas for the duration
* of this call.
@@ -1762,6 +1821,7 @@
assert(rc == 0);
#endif
+ BOOT_LOG_DBG(" * boot_prepare_image_for_update...");
/* Determine swap type and complete swap if it has been aborted. */
boot_prepare_image_for_update(state, &bs);
@@ -1810,6 +1870,8 @@
/* Set the previously determined swap type */
bs.swap_type = BOOT_SWAP_TYPE(state);
+ BOOT_LOG_DBG(" * process swap_type = %d", bs.swap_type);
+
switch (BOOT_SWAP_TYPE(state)) {
case BOOT_SWAP_TYPE_NONE:
break;
@@ -1817,6 +1879,7 @@
case BOOT_SWAP_TYPE_TEST: /* fallthrough */
case BOOT_SWAP_TYPE_PERM: /* fallthrough */
case BOOT_SWAP_TYPE_REVERT:
+ BOOT_LOG_DBG(" * perform update...", bs.swap_type);
rc = boot_perform_update(state, &bs);
assert(rc == 0);
break;
@@ -1827,6 +1890,7 @@
* pretending we just reverted back to primary slot.
*/
#ifndef MCUBOOT_OVERWRITE_ONLY
+ BOOT_LOG_DBG(" * update failed! Set image_ok manually for image(%d)", BOOT_CURR_IMG(state));
/* image_ok needs to be explicitly set to avoid a new revert. */
rc = swap_set_image_ok(BOOT_CURR_IMG(state));
if (rc != 0) {
diff --git a/boot/bootutil/src/swap_misc.c b/boot/bootutil/src/swap_misc.c
index 940d646..42ae7a4 100644
--- a/boot/bootutil/src/swap_misc.c
+++ b/boot/bootutil/src/swap_misc.c
@@ -31,6 +31,9 @@
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
+
+#ifndef MCUBOOT_SWAP_USING_STATUS
+
int
swap_erase_trailer_sectors(const struct boot_loader_state *state,
const struct flash_area *fap)
@@ -164,7 +167,7 @@
rc = swap_read_status_bytes(fap, state, bs);
if (rc == 0) {
off = boot_swap_info_off(fap);
- rc = flash_area_read(fap, off, &swap_info, sizeof swap_info);
+ rc = flash_area_read(fap, off, &swap_info, sizeof swap_info);
if (rc != 0) {
return BOOT_EFLASH;
}
@@ -182,6 +185,7 @@
return rc;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
int
swap_set_copy_done(uint8_t image_index)
@@ -228,5 +232,4 @@
return rc;
}
-
#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */
diff --git a/boot/bootutil/src/swap_move.c b/boot/bootutil/src/swap_move.c
index f2883f6..8fe0b44 100644
--- a/boot/bootutil/src/swap_move.c
+++ b/boot/bootutil/src/swap_move.c
@@ -24,6 +24,9 @@
#include "bootutil/bootutil.h"
#include "bootutil_priv.h"
#include "swap_priv.h"
+#ifdef MCUBOOT_SWAP_USING_STATUS
+#include "swap_status.h"
+#endif
#include "bootutil/bootutil_log.h"
#include "mcuboot_config/mcuboot_config.h"
@@ -115,6 +118,8 @@
return rc;
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
+
int
swap_read_status_bytes(const struct flash_area *fap,
struct boot_loader_state *state, struct boot_status *bs)
@@ -181,7 +186,7 @@
} else if (found_idx < move_entries) {
bs->op = BOOT_STATUS_OP_MOVE;
bs->idx = (found_idx / BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_IDX_0;
- bs->state = (found_idx % BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_STATE_0;;
+ bs->state = (found_idx % BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_STATE_0;
} else {
bs->op = BOOT_STATUS_OP_SWAP;
bs->idx = ((found_idx - move_entries) / BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_IDX_0;
@@ -208,6 +213,8 @@
return off;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
+
int
boot_slots_compatible(struct boot_loader_state *state)
{
@@ -219,6 +226,12 @@
num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
+
+ if (num_sectors_sec == 0) {
+ BOOT_LOG_WRN("Upgrade disabled for image %d", BOOT_CURR_IMG(state));
+ return 0;
+ }
+
if ((num_sectors_pri != num_sectors_sec) &&
(num_sectors_pri != (num_sectors_sec + 1))) {
BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors");
@@ -442,8 +455,6 @@
uint32_t sz;
uint32_t sector_sz;
uint32_t idx;
- uint32_t trailer_sz;
- uint32_t first_trailer_idx;
uint8_t image_index;
const struct flash_area *fap_pri;
const struct flash_area *fap_sec;
@@ -462,6 +473,10 @@
}
}
+#ifndef MCUBOOT_SWAP_USING_STATUS
+ uint32_t trailer_sz;
+ uint32_t first_trailer_idx;
+
/*
* When starting a new swap upgrade, check that there is enough space.
*/
@@ -484,6 +499,7 @@
return;
}
}
+#endif
image_index = BOOT_CURR_IMG(state);
diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c
index 55fa61f..9b9228a 100644
--- a/boot/bootutil/src/swap_scratch.c
+++ b/boot/bootutil/src/swap_scratch.c
@@ -24,13 +24,16 @@
#include "bootutil/bootutil.h"
#include "bootutil_priv.h"
#include "swap_priv.h"
+#ifdef MCUBOOT_SWAP_USING_STATUS
+#include "swap_status.h"
+#endif
#include "bootutil/bootutil_log.h"
#include "mcuboot_config/mcuboot_config.h"
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
-#if !defined(MCUBOOT_SWAP_USING_MOVE)
+#ifndef MCUBOOT_SWAP_USING_MOVE
#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
/*
@@ -45,7 +48,7 @@
} while (0)
#else
#define BOOT_STATUS_ASSERT(x) ASSERT(x)
-#endif
+#endif /* defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) */
int
boot_read_image_header(struct boot_loader_state *state, int slot,
@@ -55,12 +58,30 @@
int area_id;
int rc;
+ int saved_slot = slot;
+
(void)bs;
#if (BOOT_IMAGE_NUMBER == 1)
(void)state;
#endif
+ if (bs != NULL) {
+ if (bs->state == BOOT_STATUS_STATE_1) {
+ if (slot == 1) {
+ slot = 2;
+ }
+ }
+ else if (bs->state == BOOT_STATUS_STATE_2) {
+ if (slot == 0) {
+ slot = 1;
+ }
+ else {
+ slot = 2;
+ }
+ }
+ }
+
area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
rc = flash_area_open(area_id, &fap);
if (rc != 0) {
@@ -68,12 +89,43 @@
goto done;
}
- rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
- if (rc != 0) {
+ rc = flash_area_read_is_empty(fap, 0, out_hdr, sizeof *out_hdr);
+ if (rc < 0) {
rc = BOOT_EFLASH;
goto done;
}
+ if (rc == 1) {
+ memset(out_hdr, 0, sizeof(*out_hdr));
+ }
+
+ /* We only know where the headers are located when bs is valid */
+ if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
+
+ if (bs->state != BOOT_STATUS_STATE_0) {
+
+ flash_area_close(fap);
+
+ area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), saved_slot);
+ rc = flash_area_open(area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
+ if (rc < 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ if (out_hdr->ih_magic != IMAGE_MAGIC) {
+ rc = -1;
+ goto done;
+ }
+ }
+ }
+
rc = 0;
done:
@@ -82,6 +134,8 @@
}
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
+
+#ifndef MCUBOOT_SWAP_USING_STATUS
/**
* Reads the status of a partially-completed swap, if any. This is necessary
* to recover in case the boot lodaer was reset in the middle of a swap
@@ -165,6 +219,7 @@
return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
(bs->state - BOOT_STATUS_STATE_0) * elem_sz;
}
+#endif /* !MCUBOOT_SWAP_USING_STATUS */
/*
* Slots are compatible when all sectors that store up to to size of the image
@@ -187,9 +242,17 @@
num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
+
+ if (num_sectors_secondary == 0) {
+ BOOT_LOG_WRN("Upgrade disabled for image %d", BOOT_CURR_IMG(state));
+ return 0;
+ }
+
if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
(num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
+ BOOT_LOG_DBG("sectors_primary (%d) or sectors_secondary (%d) > BOOT_MAX_IMG_SECTORS (%d)",
+ num_sectors_primary, num_sectors_secondary, BOOT_MAX_IMG_SECTORS);
return 0;
}
@@ -399,7 +462,7 @@
source = table->bst_status_source;
#if (BOOT_IMAGE_NUMBER > 1)
- /* In case of multi-image boot it can happen that if boot status
+ /* In case of multi image boot it can happen that if boot status
* info is found on scratch area then it does not belong to the
* currently examined image.
*/
@@ -500,7 +563,8 @@
img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
copy_sz = sz;
- trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
+ // trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); // TODO: fixme for status use case
+ trailer_sz = BOOT_WRITE_SZ(state);
/* sz in this function is always sized on a multiple of the sector size.
* The check against the start offset of the last sector
@@ -557,8 +621,13 @@
assert(rc == 0);
/* Erase the temporary trailer from the scratch area. */
+#ifndef MCUBOOT_SWAP_USING_STATUS
rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
assert(rc == 0);
+#else
+ rc = swap_erase_trailer_sectors(state, fap_scratch);
+ assert(rc == 0);
+#endif
}
}
@@ -655,8 +724,16 @@
BOOT_STATUS_ASSERT(rc == 0);
if (erase_scratch) {
+#ifndef MCUBOOT_SWAP_USING_STATUS
rc = boot_erase_region(fap_scratch, 0, sz);
assert(rc == 0);
+#else
+ rc = swap_erase_trailer_sectors(state, fap_scratch);
+ assert(rc == 0);
+
+ rc = swap_erase_trailer_sectors(state, fap_secondary_slot); // TODO: check if needed and fix
+ assert(rc == 0);
+#endif
}
}
@@ -709,6 +786,8 @@
last_idx_secondary_slot++;
}
+ bs->op = BOOT_STATUS_OP_SWAP;
+
swap_idx = 0;
while (last_sector_idx >= 0) {
sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
diff --git a/boot/bootutil/src/swap_status.c b/boot/bootutil/src/swap_status.c
new file mode 100644
index 0000000..6b9d33b
--- /dev/null
+++ b/boot/bootutil/src/swap_status.c
@@ -0,0 +1,164 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2017-2019 Linaro LTD
+ * Copyright (c) 2016-2019 JUUL Labs
+ * Copyright (c) 2019-2020 Arm Limited
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+#include "swap_priv.h"
+#include "swap_status.h"
+#include "bootutil/bootutil_log.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
+
+#ifdef MCUBOOT_SWAP_USING_STATUS
+
+#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
+int
+swap_read_status_bytes(const struct flash_area *fap,
+ struct boot_loader_state *state, struct boot_status *bs)
+{
+ uint32_t off;
+ uint8_t status = 0;
+ uint8_t last_status = 0xff;
+ int max_entries;
+ int32_t found_idx;
+ bool found;
+ bool invalid;
+ int rc;
+ int i;
+ (void)state;
+
+ BOOT_LOG_DBG("> STATUS: swap_read_status_bytes: fa_id = %d", fap->fa_id);
+
+ if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) {
+ max_entries = 1;
+ } else {
+ max_entries = BOOT_STATUS_MAX_ENTRIES;
+ }
+
+ off = boot_status_off(fap);
+
+ found = false;
+ found_idx = -1;
+ invalid = false;
+
+ for (i = 0; i < max_entries; i++) {
+ rc = swap_status_retrieve(fap->fa_id, off + i, &status, 1);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+
+ // if (status != flash_area_erased_val(fap)) { // TODO: fixup for external memory fap's
+ if (status == 0) {
+ if (found && (found_idx == -1)) {
+ found_idx = i;
+ }
+ } else {
+ last_status = status;
+
+ if (!found) {
+ found = true;
+ } else if (found_idx > 0) {
+ invalid = true;
+ break;
+ }
+ }
+ }
+
+ if (invalid) {
+ /* This means there was an error writing status on the last
+ * swap. Tell user and move on to validation!
+ */
+#if !defined(__BOOTSIM__)
+ BOOT_LOG_ERR("Detected inconsistent status!");
+#endif
+
+#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
+ /* With validation of the primary slot disabled, there is no way
+ * to be sure the swapped primary slot is OK, so abort!
+ */
+ assert(0);
+#endif
+ }
+
+ if (found_idx == -1) {
+ /* no swap status found; nothing to do */
+ }
+ else {
+ uint8_t image_index = BOOT_CURR_IMG(state);
+ rc = boot_read_swap_size((int32_t)image_index, &bs->swap_size);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+
+#ifdef MCUBOOT_SWAP_USING_MOVE
+ /* get image size in blocks */
+ uint32_t move_entries = bs->swap_size / state->write_sz + (uint32_t)(bs->swap_size % state->write_sz != 0u);
+
+ if (found_idx < (int32_t)move_entries) {
+ /* continue move sector up operation */
+ bs->op = (uint8_t)BOOT_STATUS_OP_MOVE;
+ bs->idx = (uint32_t)found_idx;
+ bs->state = (uint8_t)last_status;
+ } else
+#endif /* MCUBOOT_SWAP_USING_MOVE */
+ {
+ /* resume swap sectors operation */
+ last_status++;
+ if (last_status > BOOT_STATUS_STATE_COUNT) {
+ last_status = BOOT_STATUS_STATE_0;
+ found_idx++;
+ }
+
+ bs->op = (uint8_t)BOOT_STATUS_OP_SWAP;
+ bs->idx = (uint32_t)found_idx;
+ bs->state = (uint8_t)last_status;
+ }
+ }
+
+ return 0;
+}
+
+/* this is internal offset in swap status area */
+uint32_t
+boot_status_internal_off(const struct boot_status *bs, int elem_sz)
+{
+ uint32_t off = (bs->idx - BOOT_STATUS_IDX_0) * (uint32_t)elem_sz;
+
+ return off;
+}
+#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
+
+#endif /* MCUBOOT_SWAP_USING_STATUS */
diff --git a/boot/bootutil/src/swap_status.h b/boot/bootutil/src/swap_status.h
new file mode 100644
index 0000000..dc27d64
--- /dev/null
+++ b/boot/bootutil/src/swap_status.h
@@ -0,0 +1,167 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2017-2019 Linaro LTD
+ * Copyright (c) 2016-2019 JUUL Labs
+ * Copyright (c) 2019-2020 Arm Limited
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_SWAP_STATUS_H_
+#define H_SWAP_STATUS_H_
+
+#include <stdint.h>
+#include "sysflash/sysflash.h"
+#include "bootutil_priv.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+#ifdef MCUBOOT_SWAP_USING_STATUS
+
+#define BOOT_LOG_SWAP_STATE_M(area, state) \
+ BOOT_LOG_DBG("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
+ "image_ok=0x%x", \
+ (area), \
+ ((state)->magic == (uint8_t)BOOT_MAGIC_GOOD ? "good" :\
+ (state)->magic == (uint8_t)BOOT_MAGIC_UNSET ? "unset" :\
+ "bad"), \
+ (state)->swap_type, \
+ (state)->copy_done, \
+ (state)->image_ok)
+
+#define BOOT_SET_SWAP_INFO_M(swap_info, image, type) { \
+ assert((int)((image) < 0xFu)); \
+ assert((int)((type) < 0xFu)); \
+ (swap_info) = (image) << 4u \
+ | (type); \
+ }
+
+#define BOOT_GET_SWAP_TYPE_M(swap_info) ((swap_info) & 0x0Fu)
+#define BOOT_GET_IMAGE_NUM_M(swap_info) ((swap_info) >> 4u)
+
+extern const uint32_t stat_part_magic[1];
+
+#define BOOT_SWAP_STATUS_MAGIC (0xDEADBEAFu)
+
+#define BOOT_SWAP_STATUS_ENCK1_SZ 16UL
+#define BOOT_SWAP_STATUS_ENCK2_SZ 16UL
+
+struct image_status_trailer {
+ uint8_t enc_key1[BOOT_SWAP_STATUS_ENCK1_SZ];
+ uint8_t enc_key2[BOOT_SWAP_STATUS_ENCK2_SZ];
+ uint32_t swap_size;
+ uint8_t swap_type;
+ uint8_t copy_done;
+ uint8_t image_ok;
+ uint8_t magic[BOOT_MAGIC_SZ];
+};
+
+#define BOOT_SWAP_STATUS_SWAPSZ_SZ 4UL
+#define BOOT_SWAP_STATUS_SWAPINF_SZ 1UL
+#define BOOT_SWAP_STATUS_COPY_DONE_SZ 1UL
+#define BOOT_SWAP_STATUS_IMG_OK_SZ 1UL
+
+#define BOOT_SWAP_STATUS_MAGIC_SZ BOOT_MAGIC_SZ
+
+#define BOOT_SWAP_STATUS_MGCREC_SZ 4UL
+#define BOOT_SWAP_STATUS_CNT_SZ 4UL
+#define BOOT_SWAP_STATUS_CRC_SZ 4UL
+
+#define BOOT_SWAP_STATUS_ROW_SZ CY_FLASH_ALIGN
+
+/* agreed to name it "a record" */
+#define BOOT_SWAP_STATUS_PAYLD_SZ (BOOT_SWAP_STATUS_ROW_SZ -\
+ BOOT_SWAP_STATUS_MGCREC_SZ - \
+ BOOT_SWAP_STATUS_CNT_SZ - \
+ BOOT_SWAP_STATUS_CRC_SZ)
+#define BOOT_SWAP_STATUS_ROW_SZ_MIN 16UL
+
+/* INFO: defining record structure for better understanding */
+struct status_part_record{
+ uint8_t payload[BOOT_SWAP_STATUS_PAYLD_SZ];
+ uint8_t magic[BOOT_SWAP_STATUS_MGCREC_SZ];
+ uint8_t counter[BOOT_SWAP_STATUS_CNT_SZ];
+ uint8_t crc[BOOT_SWAP_STATUS_CRC_SZ];
+};
+
+#if (BOOT_SWAP_STATUS_ROW_SZ % BOOT_SWAP_STATUS_ROW_SZ_MIN != 0)
+ #error "BOOT_SWAP_STATUS_ROW_SZ size is less then min value of 16 bytes"
+#endif
+
+/* number of rows sector-status area should fit into */
+#define BOOT_SWAP_STATUS_SECT_ROWS_NUM (((BOOT_MAX_IMG_SECTORS-1u)/BOOT_SWAP_STATUS_PAYLD_SZ) + 1u)
+
+/*
+ Number of flash rows used to store swap info. It consists
+ of following fields. 16 bytes is a minimum required row size,
+ thus 64 bytes required at minimum size of swap info size.
+
+ 16 bytes - uint8_t enc_key1[BOOT_SWAP_STATUS_ENCK1_SZ];
+ 16 bytes - uint8_t enc_key2[BOOT_SWAP_STATUS_ENCK2_SZ];
+ 4 bytes - uint32_t swap_size;
+ 1 byte - uint8_t swap_type;
+ 1 byte - uint8_t copy_done;
+ 1 byte - uint8_t image_ok;
+ 16 bytes - uint8_t magic[BOOT_MAGIC_SZ];
+ = 55 bytes
+ */
+#define BOOT_SWAP_STATUS_TRAILER_SIZE 64UL
+// TODO: check if min write size is 64 or larger
+// TODO: small-magic, coutner and crc aren't coutned here
+
+/* number of rows trailer data should fit into */
+#define BOOT_SWAP_STATUS_TRAIL_ROWS_NUM (((BOOT_SWAP_STATUS_TRAILER_SIZE - 1u)/BOOT_SWAP_STATUS_PAYLD_SZ) + 1u)
+
+/* the size of one copy of status area */
+#define BOOT_SWAP_STATUS_D_SIZE (BOOT_SWAP_STATUS_ROW_SZ * \
+ (BOOT_SWAP_STATUS_SECT_ROWS_NUM + \
+ BOOT_SWAP_STATUS_TRAIL_ROWS_NUM))
+
+/* the size of one copy of status area without cnt and crc fields */
+#define BOOT_SWAP_STATUS_D_SIZE_RAW (BOOT_SWAP_STATUS_PAYLD_SZ * \
+ (BOOT_SWAP_STATUS_SECT_ROWS_NUM + \
+ BOOT_SWAP_STATUS_TRAIL_ROWS_NUM))
+
+/* multiplier which defines how many blocks will be used to reduce Flash wear
+ * 1 is for single write wear, 2 - twice less wear, 3 - three times less wear, etc */
+#define BOOT_SWAP_STATUS_MULT 2UL
+
+#define BOOT_SWAP_STATUS_SIZE (BOOT_SWAP_STATUS_MULT * BOOT_SWAP_STATUS_D_SIZE)
+
+#define BOOT_SWAP_STATUS_SZ_PRIM BOOT_SWAP_STATUS_SIZE
+#define BOOT_SWAP_STATUS_SZ_SEC BOOT_SWAP_STATUS_SIZE
+#define BOOT_SWAP_STATUS_SZ_SCRATCH BOOT_SWAP_STATUS_SIZE
+
+#define BOOT_SWAP_STATUS_OFFS_PRIM 0UL
+#define BOOT_SWAP_STATUS_OFFS_SEC (BOOT_SWAP_STATUS_OFFS_PRIM + \
+ BOOT_SWAP_STATUS_SZ_PRIM)
+
+int32_t swap_status_init_offset(uint32_t area_id);
+int swap_status_update(uint32_t target_area_id, uint32_t offs, const void *data, uint32_t len);
+int swap_status_retrieve(uint32_t target_area_id, uint32_t offs, void *data, uint32_t len);
+
+int boot_write_trailer(const struct flash_area *fap, uint32_t off,
+ const uint8_t *inbuf, uint8_t inlen);
+
+#endif /* MCUBOOT_SWAP_USING_STATUS */
+
+#endif /* H_SWAP_STATUS_H_ */
diff --git a/boot/bootutil/src/swap_status_misc.c b/boot/bootutil/src/swap_status_misc.c
new file mode 100644
index 0000000..c8f8f95
--- /dev/null
+++ b/boot/bootutil/src/swap_status_misc.c
@@ -0,0 +1,718 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2017-2019 Linaro LTD
+ * Copyright (c) 2016-2019 JUUL Labs
+ * Copyright (c) 2019-2020 Arm Limited
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+#include "swap_priv.h"
+#include "bootutil/bootutil_log.h"
+
+#include "swap_status.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
+
+#if defined(MCUBOOT_SWAP_USING_STATUS)
+
+#define BOOT_MAGIC_ARR_SZ \
+ (sizeof boot_img_magic / sizeof boot_img_magic[0])
+
+static int
+boot_find_status(int image_index, const struct flash_area **fap);
+
+static int
+boot_magic_decode(const uint32_t *magic)
+{
+ if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0) {
+ return BOOT_MAGIC_GOOD;
+ }
+ return BOOT_MAGIC_BAD;
+}
+
+static int
+boot_flag_decode(uint8_t flag)
+{
+ if (flag != (uint8_t)BOOT_FLAG_SET) {
+ return BOOT_FLAG_BAD;
+ }
+ return BOOT_FLAG_SET;
+}
+
+static inline size_t
+boot_status_sector_size(const struct boot_loader_state *state, size_t sector)
+{
+ return state->status.sectors[sector].fs_size;
+}
+
+static inline uint32_t
+boot_status_sector_off(const struct boot_loader_state *state,
+ size_t sector)
+{
+ return state->status.sectors[sector].fs_off -
+ state->status.sectors[0].fs_off;
+}
+
+/* Offset Section */
+static inline uint32_t
+boot_magic_off(const struct flash_area *fap)
+{
+ (void)fap;
+ return ((uint32_t)BOOT_SWAP_STATUS_D_SIZE_RAW - (uint32_t)BOOT_MAGIC_SZ);
+}
+
+uint32_t
+boot_image_ok_off(const struct flash_area *fap)
+{
+ return (uint32_t)(boot_magic_off(fap) - 1u);
+}
+
+uint32_t
+boot_copy_done_off(const struct flash_area *fap)
+{
+ return (uint32_t)(boot_image_ok_off(fap) - 1u);
+}
+
+uint32_t
+boot_swap_info_off(const struct flash_area *fap)
+{
+ return (uint32_t)(boot_copy_done_off(fap) - 1u);
+}
+
+uint32_t
+boot_swap_size_off(const struct flash_area *fap)
+{
+ return (uint32_t)(boot_swap_info_off(fap) - 4u);
+}
+
+uint32_t
+boot_status_off(const struct flash_area *fap)
+{
+ (void)fap;
+ /* this offset is equal to 0, because swap status fields
+ in this implementation count from the start of partition */
+ return 0;
+}
+
+#ifdef MCUBOOT_ENC_IMAGES
+static inline uint32_t
+boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
+{
+#ifdef MCUBOOT_SWAP_SAVE_ENCTLV
+ /* suggest encryption key is also stored in status partition */
+ return (uint32_t)(boot_swap_size_off(fap) - (uint32_t)((slot + 1u) * (uint32_t)BOOT_ENC_TLV_SIZE));
+#else
+ return (uint32_t)(boot_swap_size_off(fap) - (uint32_t)((slot + 1u) * (uint32_t)BOOT_ENC_KEY_SIZE));
+#endif
+}
+#endif
+
+/**
+ * Write trailer data; status bytes, swap_size, etc
+ *
+ * @returns 0 on success, != 0 on error.
+ */
+int
+boot_write_trailer(const struct flash_area *fap, uint32_t off,
+ const uint8_t *inbuf, uint8_t inlen)
+{
+ int rc;
+
+ rc = swap_status_update(fap->fa_id, off, (uint8_t *)inbuf, inlen);
+
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ return rc;
+}
+
+#ifdef MCUBOOT_ENC_IMAGES
+int
+boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
+ const struct boot_status *bs)
+{
+ uint32_t off;
+ int rc;
+
+ off = boot_enc_key_off(fap, slot);
+#ifdef MCUBOOT_SWAP_SAVE_ENCTLV
+ rc = swap_status_update(fap->fa_id, off,
+ (uint8_t *) bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
+#else
+ rc = swap_status_update(fap->fa_id, off,
+ (uint8_t *) bs->enckey[slot], BOOT_ENC_KEY_SIZE);
+#endif
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ return 0;
+}
+
+int
+boot_read_enc_key(int image_index, uint8_t slot, struct boot_status *bs)
+{
+ uint32_t off;
+ const struct flash_area *fap;
+#ifdef MCUBOOT_SWAP_SAVE_ENCTLV
+ int i;
+#endif
+ int rc;
+
+ rc = boot_find_status(image_index, &fap);
+ if (rc == 0) {
+ off = boot_enc_key_off(fap, slot);
+#ifdef MCUBOOT_SWAP_SAVE_ENCTLV
+ rc = swap_status_retrieve(fap->fa_id, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
+ if (rc == 0) {
+ for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) {
+ if (bs->enctlv[slot][i] != 0xff) {
+ break;
+ }
+ }
+ /* Only try to decrypt non-erased TLV metadata */
+ if (i != BOOT_ENC_TLV_ALIGN_SIZE) {
+ rc = boot_enc_decrypt(bs->enctlv[slot], bs->enckey[slot]);
+ }
+ }
+#else
+ rc = swap_status_retrieve(fap->fa_id, off, bs->enckey[slot], BOOT_ENC_KEY_SIZE);
+#endif
+ flash_area_close(fap);
+ }
+
+ return rc;
+}
+#endif /* MCUBOOT_ENC_IMAGES */
+
+/* Write Section */
+int
+boot_write_magic(const struct flash_area *fap)
+{
+ uint32_t off;
+ int rc;
+
+ off = boot_magic_off(fap);
+
+ rc = swap_status_update(fap->fa_id, off,
+ (uint8_t *) boot_img_magic, BOOT_MAGIC_SZ);
+
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ return 0;
+}
+
+int boot_status_num_sectors(const struct boot_loader_state *state)
+{
+ return (int)(BOOT_SWAP_STATUS_SIZE / boot_status_sector_size(state, 0));
+}
+
+/**
+ * Writes the supplied boot status to the flash file system. The boot status
+ * contains the current state of an in-progress image copy operation.
+ *
+ * @param bs The boot status to write.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+boot_write_status(const struct boot_loader_state *state, struct boot_status *bs)
+{
+ const struct flash_area *fap = NULL;
+ uint32_t off;
+ int area_id;
+ int rc;
+ (void)state;
+
+ /* NOTE: The first sector copied (that is the last sector on slot) contains
+ * the trailer. Since in the last step the primary slot is erased, the
+ * first two status writes go to the scratch which will be copied to
+ * the primary slot!
+ */
+
+#ifdef MCUBOOT_SWAP_USING_SCRATCH
+ if (bs->use_scratch) {
+ /* Write to scratch status. */
+ area_id = FLASH_AREA_IMAGE_SCRATCH;
+ } else
+#endif
+ {
+ /* Write to the primary slot. */
+ area_id = (int)FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state));
+ }
+
+ rc = flash_area_open((uint8_t)area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+ off = boot_status_off(fap) + boot_status_internal_off(bs, 1);
+
+ uint8_t tmp_state = bs->state;
+
+ rc = swap_status_update(fap->fa_id, off, &tmp_state, 1);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+done:
+ flash_area_close(fap);
+
+ return rc;
+}
+
+int
+boot_read_data_empty(const struct flash_area *fap, void *data, uint32_t len)
+{
+ uint8_t *buf;
+
+ buf = (uint8_t *)data;
+ for (uint32_t i = 0; i < len; i++) {
+ if (buf[i] != flash_area_erased_val(fap)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+boot_read_swap_state(const struct flash_area *fap,
+ struct boot_swap_state *state)
+{
+ uint32_t magic[BOOT_MAGIC_ARR_SZ];
+ uint32_t off;
+ uint32_t trailer_off = 0U;
+ uint8_t swap_info = 0U;
+ int rc;
+ uint32_t erase_trailer = 0;
+
+ const struct flash_area *fap_stat = NULL;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ off = boot_magic_off(fap);
+ /* retrieve value for magic field from status partition area */
+ rc = swap_status_retrieve(fap->fa_id, off, magic, BOOT_MAGIC_SZ);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ rc = boot_read_data_empty(fap_stat, magic, BOOT_MAGIC_SZ);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ /* fill magic number value if equal to expected */
+ if (rc == 1) {
+
+ state->magic = BOOT_MAGIC_UNSET;
+
+ /* attempt to find magic in upgrade img slot trailer */
+ if (fap->fa_id == FLASH_AREA_IMAGE_1 ||
+ fap->fa_id == FLASH_AREA_IMAGE_3) {
+
+ trailer_off = fap->fa_size - BOOT_MAGIC_SZ;
+
+ rc = flash_area_read_is_empty(fap, trailer_off, magic, BOOT_MAGIC_SZ);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ if (rc == 1) {
+ state->magic = BOOT_MAGIC_UNSET;
+ } else {
+ state->magic = (uint8_t)boot_magic_decode(magic);
+ /* put magic to status partition for upgrade slot*/
+ if (state->magic == (uint32_t)BOOT_MAGIC_GOOD) {
+ rc = swap_status_update(fap->fa_id, off,
+ (uint8_t *) magic, BOOT_MAGIC_SZ);
+ }
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ } else {
+ erase_trailer = 1;
+ }
+ }
+ }
+ } else {
+ state->magic = (uint8_t)boot_magic_decode(magic);
+ }
+
+ off = boot_swap_info_off(fap);
+ rc = swap_status_retrieve(fap->fa_id, off, &swap_info, sizeof swap_info);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ rc = boot_read_data_empty(fap_stat, &swap_info, sizeof swap_info);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ if (rc == 1 || state->swap_type > (uint8_t)BOOT_SWAP_TYPE_REVERT) {
+ state->swap_type = (uint8_t)BOOT_SWAP_TYPE_NONE;
+ state->image_num = 0;
+ }
+ else {
+ /* Extract the swap type and image number */
+ state->swap_type = (uint8_t)BOOT_GET_SWAP_TYPE_M(swap_info);
+ state->image_num = (uint8_t)BOOT_GET_IMAGE_NUM_M(swap_info);
+ }
+
+ off = boot_copy_done_off(fap);
+ rc = swap_status_retrieve(fap->fa_id, off, &state->copy_done, sizeof state->copy_done);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ rc = boot_read_data_empty(fap_stat, &state->copy_done, sizeof state->copy_done);
+ /* need to check swap_info was empty */
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ if (rc == 1) {
+ state->copy_done = BOOT_FLAG_UNSET;
+ } else {
+ state->copy_done = (uint8_t)boot_flag_decode(state->copy_done);
+ }
+
+ off = boot_image_ok_off(fap);
+ rc = swap_status_retrieve(fap->fa_id, off, &state->image_ok, sizeof state->image_ok);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ rc = boot_read_data_empty(fap_stat, &state->image_ok, sizeof state->image_ok);
+ /* need to check swap_info was empty */
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ if (rc == 1) {
+ /* assign img_ok unset */
+ state->image_ok = BOOT_FLAG_UNSET;
+
+ /* attempt to read img_ok value in upgrade img slots trailer area
+ * it is set when image in slot for upgrade is signed for swap_type permanent
+ */
+ uint32_t process_image_ok = 0;
+ switch (fap->fa_id) {
+ case FLASH_AREA_IMAGE_0:
+ case FLASH_AREA_IMAGE_2:
+ {
+ if (state->copy_done == (uint8_t)BOOT_FLAG_SET)
+ process_image_ok = 1;
+ }
+ break;
+ case FLASH_AREA_IMAGE_1:
+ case FLASH_AREA_IMAGE_3:
+ {
+ process_image_ok = 1;
+ }
+ break;
+ case FLASH_AREA_IMAGE_SCRATCH:
+ {
+ BOOT_LOG_DBG(" * selected SCRATCH area, copy_done = %d", state->copy_done);
+ {
+ if (state->copy_done == (uint8_t)BOOT_FLAG_SET)
+ process_image_ok = 1;
+ }
+ }
+ break;
+ default:
+ {
+ return BOOT_EFLASH;
+ }
+ break;
+ }
+ if (process_image_ok != 0u) {
+ trailer_off = fap->fa_size - (uint8_t)BOOT_MAGIC_SZ - (uint8_t)BOOT_MAX_ALIGN;
+
+ rc = flash_area_read_is_empty(fap, trailer_off, &state->image_ok, sizeof state->image_ok);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ if (rc == 1) {
+ state->image_ok = BOOT_FLAG_UNSET;
+ } else {
+ state->image_ok = (uint8_t)boot_flag_decode(state->image_ok);
+
+ /* put img_ok to status partition for upgrade slot */
+ if (state->image_ok != (uint8_t)BOOT_FLAG_BAD) {
+ rc = swap_status_update(fap->fa_id, off,
+ &state->image_ok, sizeof state->image_ok);
+ }
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+
+ /* mark img trailer needs to be erased */
+ erase_trailer = 1;
+ }
+ }
+ } else {
+ state->image_ok = (uint8_t)boot_flag_decode(state->image_ok);
+ }
+
+ if ((erase_trailer != 0u) && (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH)) {
+ /* erase magic from upgrade img trailer */
+ rc = flash_area_erase(fap, trailer_off, BOOT_MAGIC_SZ);
+ if (rc != 0)
+ return rc;
+ }
+ return 0;
+}
+
+/**
+ * This functions tries to locate the status area after an aborted swap,
+ * by looking for the magic in the possible locations.
+ *
+ * If the magic is successfully found, a flash_area * is returned and it
+ * is the responsibility of the called to close it.
+ *
+ * @returns 0 on success, -1 on errors
+ */
+static int
+boot_find_status(int image_index, const struct flash_area **fap)
+{
+ uint32_t magic[BOOT_MAGIC_ARR_SZ] = {0};
+ uint32_t off;
+
+ /* the status is always in status partition */
+ uint8_t area = FLASH_AREA_IMAGE_PRIMARY((uint32_t)image_index);
+ int rc = -1;
+
+ /*
+ * In the middle a swap, tries to locate the area that is currently
+ * storing a valid magic, first on the primary slot, then on scratch.
+ * Both "slots" can end up being temporary storage for a swap and it
+ * is assumed that if magic is valid then other metadata is too,
+ * because magic is always written in the last step.
+ */
+ rc = flash_area_open(area, fap);
+ if (rc != 0) {
+ return rc;
+ }
+ off = boot_magic_off(*fap);
+ rc = swap_status_retrieve(area, off, magic, BOOT_MAGIC_SZ);
+
+ if (rc == 0) {
+ rc = memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ);
+ }
+
+ flash_area_close(*fap);
+ return rc;
+}
+
+int
+boot_read_swap_size(int image_index, uint32_t *swap_size)
+{
+ uint32_t off;
+ const struct flash_area *fap;
+ int rc;
+
+ rc = boot_find_status(image_index, &fap);
+ if (rc == 0) {
+ off = boot_swap_size_off(fap);
+
+ rc = swap_status_retrieve(fap->fa_id, off, swap_size, sizeof *swap_size);
+ }
+ return rc;
+}
+
+int
+swap_erase_trailer_sectors(const struct boot_loader_state *state,
+ const struct flash_area *fap)
+{
+ uint32_t sub_offs, trailer_offs;
+ uint32_t sz;
+ uint8_t fa_id_primary;
+ uint8_t fa_id_secondary;
+ uint8_t image_index;
+ int rc;
+ (void)state;
+
+ BOOT_LOG_INF("Erasing trailer; fa_id=%d", fap->fa_id);
+ /* trailer is located in status-partition */
+ const struct flash_area *fap_stat = NULL;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH)
+ {
+ image_index = BOOT_CURR_IMG(state);
+ fa_id_primary = (uint8_t)flash_area_id_from_multi_image_slot((int32_t)image_index,
+ BOOT_PRIMARY_SLOT);
+ fa_id_secondary = (uint8_t)flash_area_id_from_multi_image_slot((int32_t)image_index,
+ BOOT_SECONDARY_SLOT);
+
+ /* skip if Flash Area is not recognizable */
+ if ((fap->fa_id != fa_id_primary) && (fap->fa_id != fa_id_secondary)) {
+ return BOOT_EFLASH;
+ }
+ }
+
+ sub_offs = (uint32_t)swap_status_init_offset(fap->fa_id);
+
+ /* delete starting from last sector and moving to beginning */
+ /* calculate last sector of status sub-area */
+ sz = (uint32_t)BOOT_SWAP_STATUS_SIZE;
+
+ rc = flash_area_erase(fap_stat, sub_offs, sz);
+ assert((int)(rc == 0));
+
+ if (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH)
+ {
+ /*
+ * it is also needed to erase trailer area in slots since they may contain
+ * data, which is already cleared in corresponding status partition
+ */
+ trailer_offs = fap->fa_size - BOOT_SWAP_STATUS_TRAILER_SIZE;
+ rc = flash_area_erase(fap, trailer_offs, BOOT_SWAP_STATUS_TRAILER_SIZE);
+ }
+
+ flash_area_close(fap_stat);
+
+ return rc;
+}
+
+int
+swap_status_init(const struct boot_loader_state *state,
+ const struct flash_area *fap,
+ const struct boot_status *bs)
+{
+ struct boot_swap_state swap_state;
+ uint8_t image_index;
+ int rc;
+
+#if (BOOT_IMAGE_NUMBER == 1)
+ (void)state;
+#endif
+
+ image_index = BOOT_CURR_IMG(state);
+
+ BOOT_LOG_DBG("initializing status; fa_id=%d", fap->fa_id);
+
+ rc = boot_read_swap_state_by_id((int32_t)FLASH_AREA_IMAGE_SECONDARY(image_index),
+ &swap_state);
+ assert((int)(rc == 0));
+
+ if (bs->swap_type != (uint8_t)BOOT_SWAP_TYPE_NONE) {
+ rc = boot_write_swap_info(fap, bs->swap_type, image_index);
+ assert((int)(rc == 0));
+ }
+
+ if (swap_state.image_ok == (uint8_t)BOOT_FLAG_SET) {
+ rc = boot_write_image_ok(fap);
+ assert((int)(rc == 0));
+ }
+
+ rc = boot_write_swap_size(fap, bs->swap_size);
+ assert((int)(rc == 0));
+
+#ifdef MCUBOOT_ENC_IMAGES
+ rc = boot_write_enc_key(fap, 0, bs);
+ assert((int)(rc == 0));
+
+ rc = boot_write_enc_key(fap, 1, bs);
+ assert((int)(rc == 0));
+#endif
+
+ rc = boot_write_magic(fap);
+ assert((int)(rc == 0));
+
+ return 0;
+}
+
+int
+swap_read_status(struct boot_loader_state *state, struct boot_status *bs)
+{
+ const struct flash_area *fap = NULL;
+ const struct flash_area *fap_stat = NULL;
+ uint32_t off;
+ uint8_t swap_info = 0;
+ int area_id;
+ int rc = 0;
+
+ bs->source = swap_status_source(state);
+
+ if (bs->source == BOOT_STATUS_SOURCE_NONE) {
+ return 0;
+ }
+
+ if (bs->source == BOOT_STATUS_SOURCE_PRIMARY_SLOT) {
+ area_id = (int32_t)FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state));
+ } else if (bs->source == BOOT_STATUS_SOURCE_SCRATCH) {
+ area_id = FLASH_AREA_IMAGE_SCRATCH;
+ } else {
+ return BOOT_EBADARGS;
+ }
+
+ rc = flash_area_open((uint8_t)area_id, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = swap_read_status_bytes(fap, state, bs);
+ if (rc == 0) {
+ off = boot_swap_info_off(fap);
+ rc = swap_status_retrieve((uint8_t)area_id, off, &swap_info, sizeof swap_info);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+ rc = boot_read_data_empty(fap_stat, &swap_info, sizeof swap_info);
+ if (rc < 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (rc == 1) {
+ BOOT_SET_SWAP_INFO_M(swap_info, 0u, (uint8_t)BOOT_SWAP_TYPE_NONE);
+ rc = 0;
+ }
+
+ /* Extract the swap type info */
+ bs->swap_type = BOOT_GET_SWAP_TYPE_M(swap_info);
+ }
+
+ flash_area_close(fap);
+ flash_area_close(fap_stat);
+
+ return rc;
+}
+
+#endif /* MCUBOOT_SWAP_USING_STATUS */
diff --git a/boot/bootutil/src/swap_status_part.c b/boot/bootutil/src/swap_status_part.c
new file mode 100644
index 0000000..a2899a9
--- /dev/null
+++ b/boot/bootutil/src/swap_status_part.c
@@ -0,0 +1,404 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2017-2019 Linaro LTD
+ * Copyright (c) 2016-2019 JUUL Labs
+ * Copyright (c) 2019-2020 Arm Limited
+ * Copyright (c) 2020 Cypress Semiconductors
+ *
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include "crc32c.h"
+#include <string.h>
+#include "swap_status.h"
+
+#ifdef MCUBOOT_SWAP_USING_STATUS
+
+#define IMAGE_0_STATUS_OFFS 0
+#define IMAGE_0_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
+
+#define IMAGE_1_STATUS_OFFS (IMAGE_0_STATUS_OFFS + IMAGE_0_STATUS_SIZE)
+#define IMAGE_1_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
+
+#define SCRATCH_STATUS_OFFS (IMAGE_1_STATUS_OFFS + BOOT_SWAP_STATUS_SIZE)
+#ifdef MCUBOOT_SWAP_USING_SCRATCH
+#define SCRATCH_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
+#else
+#define SCRATCH_STATUS_SIZE 0
+#endif
+
+#if (MCUBOOT_IMAGE_NUMBER == 2)
+#define IMAGE_2_STATUS_OFFS (SCRATCH_STATUS_OFFS + SCRATCH_STATUS_SIZE)
+#define IMAGE_2_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
+
+#define IMAGE_3_STATUS_OFFS (IMAGE_2_STATUS_OFFS + IMAGE_2_STATUS_SIZE)
+#define IMAGE_3_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
+#endif
+
+const uint32_t stat_part_magic[] = {
+ BOOT_SWAP_STATUS_MAGIC
+};
+
+uint32_t calc_rec_idx(uint32_t value)
+{
+ uint32_t rec_idx;
+
+ rec_idx = value/BOOT_SWAP_STATUS_PAYLD_SZ;
+
+ return rec_idx;
+}
+
+uint32_t calc_record_offs(uint32_t offs)
+{
+ uint32_t rec_offs;
+
+ rec_offs = BOOT_SWAP_STATUS_ROW_SZ*calc_rec_idx(offs);
+
+ return rec_offs;
+}
+
+uint32_t calc_record_crc(const uint8_t *data, uint32_t length)
+{
+ uint32_t crc;
+
+ crc = crc32c_checksum(data, length);
+
+ return crc;
+}
+
+int32_t swap_status_init_offset(uint32_t area_id)
+{
+ int32_t offset = -1;
+ /* calculate an offset caused by area type: primary_x/secondary_x */
+ switch (area_id) {
+ case FLASH_AREA_IMAGE_0:
+ offset = (int)IMAGE_0_STATUS_OFFS;
+ break;
+ case FLASH_AREA_IMAGE_1:
+ offset = (int)IMAGE_1_STATUS_OFFS;
+ break;
+#ifdef MCUBOOT_SWAP_USING_SCRATCH
+ case FLASH_AREA_IMAGE_SCRATCH:
+ offset = (int)SCRATCH_STATUS_OFFS;
+ break;
+#endif
+#if (MCUBOOT_IMAGE_NUMBER == 2)
+ case FLASH_AREA_IMAGE_2:
+ offset = (int)IMAGE_2_STATUS_OFFS;
+ break;
+ case FLASH_AREA_IMAGE_3:
+ offset = (int)IMAGE_3_STATUS_OFFS;
+ break;
+#endif
+ default:
+ offset = -1;
+ break;
+ }
+ return offset;
+}
+
+int swap_status_read_record(uint32_t rec_offset, uint8_t *data, uint32_t *copy_counter)
+{ /* returns BOOT_SWAP_STATUS_PAYLD_SZ of data */
+ int rc = -1;
+
+ uint32_t fin_offset;
+ uint32_t data_offset = 0;
+ uint32_t counter, crc, magic;
+ uint32_t crc_fail = 0;
+ uint32_t magic_fail = 0;
+ uint32_t max_cnt = 0;
+
+ int32_t max_idx = 0;
+
+ uint8_t buff[BOOT_SWAP_STATUS_ROW_SZ];
+
+ const struct flash_area *fap_stat = NULL;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ /* loop over copies/duplicates */
+ for(uint32_t i = 0; i<BOOT_SWAP_STATUS_MULT; i++) {
+ /* calculate final duplicate offset */
+ fin_offset = rec_offset + i*BOOT_SWAP_STATUS_D_SIZE;
+
+ rc = flash_area_read(fap_stat, fin_offset, buff, sizeof(buff));
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ /* read magic value to know if area was pre-erased */
+ magic = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
+ BOOT_SWAP_STATUS_MGCREC_SZ -\
+ BOOT_SWAP_STATUS_CNT_SZ-\
+ BOOT_SWAP_STATUS_CRC_SZ]);
+ if (magic == BOOT_SWAP_STATUS_MAGIC) { /* read CRC */
+ crc = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
+ BOOT_SWAP_STATUS_CRC_SZ]);
+ /* check record data integrity first */
+ if (crc == calc_record_crc(buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ)) {
+ /* look for counter */
+ counter = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
+ BOOT_SWAP_STATUS_CNT_SZ - \
+ BOOT_SWAP_STATUS_CRC_SZ]);
+ /* find out counter max */
+ if (counter >= max_cnt) {
+ max_cnt = counter;
+ max_idx = (int32_t)i;
+ data_offset = fin_offset;
+ }
+ }
+ /* if crc != calculated() */
+ else {
+ crc_fail++;
+ }
+ }
+ else {
+ magic_fail++;
+ }
+ }
+ /* no magic found - status area is pre-erased, start from scratch */
+ if (magic_fail == BOOT_SWAP_STATUS_MULT) {
+ /* emulate last index was received, so next will start from beginning */
+ max_idx = (int32_t)(BOOT_SWAP_STATUS_MULT-1U);
+ *copy_counter = 0;
+ /* return all erased values */
+ (void)memset(data, (int32_t)flash_area_erased_val(fap_stat), BOOT_SWAP_STATUS_PAYLD_SZ);
+ }
+ else {
+ /* no valid CRC found - status pre-read failure */
+ if (crc_fail == BOOT_SWAP_STATUS_MULT) {
+ max_idx = -1;
+ }
+ else {
+ *copy_counter = max_cnt;
+ /* read payload data */
+ rc = flash_area_read(fap_stat, data_offset, data, BOOT_SWAP_STATUS_PAYLD_SZ);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ }
+ }
+ flash_area_close(fap_stat);
+
+ /* return back duplicate index */
+ return max_idx;
+}
+
+static int swap_status_write_record(uint32_t rec_offset, uint32_t copy_num, uint32_t copy_counter, const uint8_t *data)
+{ /* it receives explicitly BOOT_SWAP_STATUS_PAYLD_SZ of data */
+ int rc = -1;
+
+ uint32_t fin_offset;
+ /* increment counter field */
+ uint32_t next_counter = copy_counter + 1U;
+ uint32_t next_crc;
+
+ uint8_t buff[BOOT_SWAP_STATUS_ROW_SZ];
+
+ const struct flash_area *fap_stat = NULL;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ /* copy data into buffer */
+ (void)memcpy(buff, data, BOOT_SWAP_STATUS_PAYLD_SZ);
+ /* append next counter to whole record row */
+ (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CNT_SZ-BOOT_SWAP_STATUS_CRC_SZ], \
+ &next_counter, \
+ BOOT_SWAP_STATUS_CNT_SZ);
+ /* append record magic */
+ (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-\
+ BOOT_SWAP_STATUS_MGCREC_SZ-\
+ BOOT_SWAP_STATUS_CNT_SZ-\
+ BOOT_SWAP_STATUS_CRC_SZ], \
+ stat_part_magic, \
+ BOOT_SWAP_STATUS_MGCREC_SZ);
+
+ /* calculate CRC field*/
+ next_crc = calc_record_crc(buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ);
+
+ /* append new CRC to whole record row */
+ (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ], \
+ &next_crc, \
+ BOOT_SWAP_STATUS_CRC_SZ);
+
+ /* we already know what copy number was last and correct */
+ /* increment duplicate index */
+ /* calculate final duplicate offset */
+ if (copy_num == (BOOT_SWAP_STATUS_MULT - 1U)) {
+ copy_num = 0;
+ }
+ else {
+ copy_num++;
+ }
+ fin_offset = rec_offset + copy_num*BOOT_SWAP_STATUS_D_SIZE;
+
+ /* write prepared record into flash */
+ rc = flash_area_write(fap_stat, fin_offset, buff, sizeof(buff));
+
+ flash_area_close(fap_stat);
+
+ return rc;
+}
+
+/**
+ * Updates len bytes of status partition with values from *data-pointer.
+ *
+ * @param targ_area_id Target area id for which status is being written.
+ * Not a status-partition area id.
+ * @param offset Status byte offset inside status table. Should not include CRC and CNT.
+ * @param data Pointer to data status table to needs to be updated with.
+ * @param len Number of bytes to be written
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int swap_status_update(uint32_t targ_area_id, uint32_t offs, const void *data, uint32_t len)
+{
+ int rc = -1;
+
+ int32_t init_offs;
+ int32_t length = (int32_t)len;
+ int32_t copy_num;
+
+ uint32_t rec_offs;
+ uint32_t copy_sz;
+ uint32_t copy_counter;
+ uint32_t data_idx = 0;
+ uint32_t buff_idx = offs%BOOT_SWAP_STATUS_PAYLD_SZ;
+
+ uint8_t buff[BOOT_SWAP_STATUS_PAYLD_SZ];
+
+ /* check if end of data is still inside writable area */
+ assert ((int)((offs + len) <= BOOT_SWAP_STATUS_D_SIZE_RAW));
+
+ /* pre-calculate sub-area offset */
+ init_offs = swap_status_init_offset(targ_area_id);
+ assert ((int)(init_offs >= 0));
+
+ /* will start from it
+ * this will be write-aligned */
+ rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
+
+ /* go over all records to be updated */
+ while (length > 0) {
+ /* preserve record */
+ copy_num = swap_status_read_record(rec_offs, buff, ©_counter);
+ /* it returns copy number */
+ if (copy_num < 0)
+ { /* something went wrong while read, exit */
+ rc = -1;
+ break;
+ }
+ /* update record data */
+ if (length > (int)BOOT_SWAP_STATUS_PAYLD_SZ) {
+ copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
+ }
+ else {
+ copy_sz = (uint32_t)length;
+ }
+ (void)memcpy((void *)&buff[buff_idx], &((uint8_t *)data)[data_idx], copy_sz);
+ buff_idx = 0;
+
+ /* write record back */
+ rc = swap_status_write_record(rec_offs, (uint32_t)copy_num, copy_counter, buff);
+ assert ((int)(rc == 0));
+
+ /* proceed to next record */
+ length -= (int32_t)BOOT_SWAP_STATUS_PAYLD_SZ;
+ rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
+ data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
+ }
+ return rc;
+}
+
+/**
+ * Reads len bytes of status partition with values from *data-pointer.
+ *
+ * @param targ_area_id Target area id for which status is being read.
+ * Not a status-partition area id.
+ * @param offset Status byte offset inside status table. Should not include CRC and CNT.
+ * @param data Pointer to data where status table values will be written.
+ * @param len Number of bytes to be read from status table.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int swap_status_retrieve(uint32_t target_area_id, uint32_t offs, void *data, uint32_t len)
+{
+ int rc = 0;
+
+ int32_t init_offs;
+ int32_t length = (int32_t)len;
+ int32_t copy_num;
+
+ uint32_t rec_offs;
+ uint32_t copy_sz;
+ uint32_t copy_counter;
+ uint32_t data_idx = 0;
+ uint32_t buff_idx = offs % BOOT_SWAP_STATUS_PAYLD_SZ;
+
+ uint8_t buff[BOOT_SWAP_STATUS_PAYLD_SZ];
+
+ /* check if end of data is still inside writable area */
+ // TODO: update for multi image
+ assert ((int)((offs + len) <= BOOT_SWAP_STATUS_D_SIZE_RAW));
+
+ /* pre-calculate sub-area offset */
+ init_offs = swap_status_init_offset(target_area_id);
+ assert ((int)(init_offs >= 0));
+
+ /* will start from it
+ * this will be write-aligned */
+ rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
+
+ /* go over all records to be updated */
+ while (length > 0) {
+ /* preserve record */
+ copy_num = swap_status_read_record(rec_offs, buff, ©_counter);
+ /* it returns copy number */
+ if (copy_num < 0) {
+ /* something went wrong while read, exit */
+ rc = -1;
+ break;
+ }
+ /* update record data */
+ if (length > (int)BOOT_SWAP_STATUS_PAYLD_SZ) {
+ copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
+ }
+ else {
+ copy_sz = (uint32_t)length;
+ }
+ (void)memcpy(&((uint8_t *)data)[data_idx], &buff[buff_idx], copy_sz);
+ buff_idx = 0;
+
+ /* proceed to next record */
+ length -= (int32_t)BOOT_SWAP_STATUS_PAYLD_SZ;
+ rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
+ data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
+ }
+ return rc;
+}
+
+#endif /* MCUBOOT_SWAP_USING_STATUS */