mcuboot - Initial migration.
diff --git a/boot_serial/include/boot_serial/boot_serial.h b/boot_serial/include/boot_serial/boot_serial.h
new file mode 100644
index 0000000..b93c28f
--- /dev/null
+++ b/boot_serial/include/boot_serial/boot_serial.h
@@ -0,0 +1,38 @@
+/*
+ * 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 __BOOT_SERIAL_H__
+#define __BOOT_SERIAL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Start processing newtmgr commands for uploading image0 over serial.
+ *
+ * Open console serial port and wait for download command.
+ */
+void boot_serial_start(int max_input);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOOT_SERIAL_H__ */
diff --git a/boot_serial/pkg.yml b/boot_serial/pkg.yml
new file mode 100644
index 0000000..5913877
--- /dev/null
+++ b/boot_serial/pkg.yml
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+pkg.name: boot/boot_serial
+pkg.description: The boot_serial library is used when downloading image over serial port.
+pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+ - boot
+ - bootloader
+
+pkg.deps:
+ - hw/hal
+ - kernel/os
+ - boot/bootutil
+ - encoding/tinycbor
+ - encoding/cborattr
+ - encoding/base64
+ - sys/flash_map
+ - util/crc
+
+pkg.req_apis:
+ - console
diff --git a/boot_serial/src/boot_serial.c b/boot_serial/src/boot_serial.c
new file mode 100644
index 0000000..ac935c1
--- /dev/null
+++ b/boot_serial/src/boot_serial.c
@@ -0,0 +1,445 @@
+/*
+ * 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 <inttypes.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "sysflash/sysflash.h"
+
+#include <bsp/bsp.h>
+
+#include <flash_map/flash_map.h>
+#include <hal/hal_flash.h>
+#include <hal/hal_system.h>
+
+#include <os/endian.h>
+#include <os/os.h>
+#include <os/os_malloc.h>
+#include <os/os_cputime.h>
+
+#include <console/console.h>
+
+#include <tinycbor/cbor.h>
+#include <tinycbor/cbor_buf_reader.h>
+#include <cborattr/cborattr.h>
+#include <base64/base64.h>
+#include <crc/crc16.h>
+
+#include <bootutil/image.h>
+
+#include "boot_serial/boot_serial.h"
+#include "boot_serial_priv.h"
+
+#define BOOT_SERIAL_OUT_MAX 48
+
+static uint32_t curr_off;
+static uint32_t img_size;
+static struct nmgr_hdr *bs_hdr;
+
+static char bs_obuf[BOOT_SERIAL_OUT_MAX];
+
+static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
+ int len);
+static void boot_serial_output(void);
+
+static struct cbor_encoder_writer bs_writer = {
+ .write = bs_cbor_writer
+};
+static CborEncoder bs_root;
+static CborEncoder bs_rsp;
+
+int
+bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+ memcpy(&bs_obuf[cew->bytes_written], data, len);
+ cew->bytes_written += len;
+
+ return 0;
+}
+
+/*
+ * Looks for 'name' from NULL-terminated json data in buf.
+ * Returns pointer to first character of value for that name.
+ * Returns NULL if 'name' is not found.
+ */
+char *
+bs_find_val(char *buf, char *name)
+{
+ char *ptr;
+
+ ptr = strstr(buf, name);
+ if (!ptr) {
+ return NULL;
+ }
+ ptr += strlen(name);
+
+ while (*ptr != '\0') {
+ if (*ptr != ':' && !isspace(*ptr)) {
+ break;
+ }
+ ++ptr;
+ }
+ if (*ptr == '\0') {
+ ptr = NULL;
+ }
+ return ptr;
+}
+
+/*
+ * List images.
+ */
+static void
+bs_list(char *buf, int len)
+{
+ CborEncoder images;
+ CborEncoder image;
+ struct image_header hdr;
+ uint8_t tmpbuf[64];
+ int i, area_id;
+ const struct flash_area *fap;
+
+ cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
+ cbor_encode_text_stringz(&bs_rsp, "images");
+ cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
+ for (i = 0; i < 2; i++) {
+ area_id = flash_area_id_from_image_slot(i);
+ if (flash_area_open(area_id, &fap)) {
+ continue;
+ }
+
+ flash_area_read(fap, 0, &hdr, sizeof(hdr));
+
+ if (hdr.ih_magic != IMAGE_MAGIC ||
+ bootutil_img_validate(&hdr, fap, tmpbuf, sizeof(tmpbuf),
+ NULL, 0, NULL)) {
+ flash_area_close(fap);
+ continue;
+ }
+ flash_area_close(fap);
+
+ cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
+ cbor_encode_text_stringz(&image, "slot");
+ cbor_encode_int(&image, i);
+ cbor_encode_text_stringz(&image, "version");
+
+ len = snprintf((char *)tmpbuf, sizeof(tmpbuf),
+ "%u.%u.%u.%u", hdr.ih_ver.iv_major, hdr.ih_ver.iv_minor,
+ hdr.ih_ver.iv_revision, (unsigned int)hdr.ih_ver.iv_build_num);
+ cbor_encode_text_stringz(&image, (char *)tmpbuf);
+ cbor_encoder_close_container(&images, &image);
+ }
+ cbor_encoder_close_container(&bs_rsp, &images);
+ cbor_encoder_close_container(&bs_root, &bs_rsp);
+ boot_serial_output();
+}
+
+/*
+ * Image upload request.
+ */
+static void
+bs_upload(char *buf, int len)
+{
+ CborParser parser;
+ struct cbor_buf_reader reader;
+ struct CborValue value;
+ uint8_t img_data[400];
+ long long unsigned int off = UINT_MAX;
+ size_t img_blen = 0;
+ long long unsigned int data_len = UINT_MAX;
+ const struct cbor_attr_t attr[4] = {
+ [0] = {
+ .attribute = "data",
+ .type = CborAttrByteStringType,
+ .addr.bytestring.data = img_data,
+ .addr.bytestring.len = &img_blen,
+ .len = sizeof(img_data)
+ },
+ [1] = {
+ .attribute = "off",
+ .type = CborAttrUnsignedIntegerType,
+ .addr.uinteger = &off,
+ .nodefault = true
+ },
+ [2] = {
+ .attribute = "len",
+ .type = CborAttrUnsignedIntegerType,
+ .addr.uinteger = &data_len,
+ .nodefault = true
+ }
+ };
+ const struct flash_area *fap = NULL;
+ int rc;
+
+ memset(img_data, 0, sizeof(img_data));
+ cbor_buf_reader_init(&reader, (uint8_t *)buf, len);
+ cbor_parser_init(&reader.r, 0, &parser, &value);
+ rc = cbor_read_object(&value, attr);
+ if (rc || off == UINT_MAX) {
+ rc = MGMT_ERR_EINVAL;
+ goto out;
+ }
+
+
+ rc = flash_area_open(flash_area_id_from_image_slot(0), &fap);
+ if (rc) {
+ rc = MGMT_ERR_EINVAL;
+ goto out;
+ }
+
+ if (off == 0) {
+ curr_off = 0;
+ if (data_len > fap->fa_size) {
+ rc = MGMT_ERR_EINVAL;
+ goto out;
+ }
+ rc = flash_area_erase(fap, 0, fap->fa_size);
+ if (rc) {
+ rc = MGMT_ERR_EINVAL;
+ goto out;
+ }
+ img_size = data_len;
+ }
+ if (off != curr_off) {
+ rc = 0;
+ goto out;
+ }
+ rc = flash_area_write(fap, curr_off, img_data, img_blen);
+ if (rc) {
+ rc = MGMT_ERR_EINVAL;
+ goto out;
+ }
+ curr_off += img_blen;
+
+out:
+ cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
+ cbor_encode_text_stringz(&bs_rsp, "rc");
+ cbor_encode_int(&bs_rsp, rc);
+ if (rc == 0) {
+ cbor_encode_text_stringz(&bs_rsp, "off");
+ cbor_encode_uint(&bs_rsp, curr_off);
+ }
+ cbor_encoder_close_container(&bs_root, &bs_rsp);
+
+ boot_serial_output();
+ flash_area_close(fap);
+}
+
+/*
+ * Console echo control. Send empty response, don't do anything.
+ */
+static void
+bs_echo_ctl(char *buf, int len)
+{
+ boot_serial_output();
+}
+
+/*
+ * Reset, and (presumably) boot to newly uploaded image. Flush console
+ * before restarting.
+ */
+static int
+bs_reset(char *buf, int len)
+{
+ cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
+ cbor_encode_text_stringz(&bs_rsp, "rc");
+ cbor_encode_int(&bs_rsp, 0);
+ cbor_encoder_close_container(&bs_root, &bs_rsp);
+
+ boot_serial_output();
+ os_cputime_delay_usecs(250000);
+ hal_system_reset();
+}
+
+/*
+ * Parse incoming line of input from console.
+ * Expect newtmgr protocol with serial transport.
+ */
+void
+boot_serial_input(char *buf, int len)
+{
+ struct nmgr_hdr *hdr;
+
+ hdr = (struct nmgr_hdr *)buf;
+ if (len < sizeof(*hdr) ||
+ (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) ||
+ (ntohs(hdr->nh_len) < len - sizeof(*hdr))) {
+ return;
+ }
+ bs_hdr = hdr;
+ hdr->nh_group = ntohs(hdr->nh_group);
+
+ buf += sizeof(*hdr);
+ len -= sizeof(*hdr);
+
+ bs_writer.bytes_written = 0;
+ cbor_encoder_init(&bs_root, &bs_writer, 0);
+
+ /*
+ * Limited support for commands.
+ */
+ if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
+ switch (hdr->nh_id) {
+ case IMGMGR_NMGR_OP_STATE:
+ bs_list(buf, len);
+ break;
+ case IMGMGR_NMGR_OP_UPLOAD:
+ bs_upload(buf, len);
+ break;
+ default:
+ break;
+ }
+ } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) {
+ switch (hdr->nh_id) {
+ case NMGR_ID_CONS_ECHO_CTRL:
+ bs_echo_ctl(buf, len);
+ break;
+ case NMGR_ID_RESET:
+ bs_reset(buf, len);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+boot_serial_output(void)
+{
+ char *data;
+ int len;
+ uint16_t crc;
+ uint16_t totlen;
+ char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 };
+ char buf[BOOT_SERIAL_OUT_MAX];
+ char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
+
+ data = bs_obuf;
+ len = bs_writer.bytes_written;
+
+ bs_hdr->nh_op++;
+ bs_hdr->nh_flags = NMGR_F_CBOR_RSP_COMPLETE;
+ bs_hdr->nh_len = htons(len);
+ bs_hdr->nh_group = htons(bs_hdr->nh_group);
+
+ crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr));
+ crc = crc16_ccitt(crc, data, len);
+ crc = htons(crc);
+
+ console_write(pkt_start, sizeof(pkt_start));
+
+ totlen = len + sizeof(*bs_hdr) + sizeof(crc);
+ totlen = htons(totlen);
+
+ memcpy(buf, &totlen, sizeof(totlen));
+ totlen = sizeof(totlen);
+ memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr));
+ totlen += sizeof(*bs_hdr);
+ memcpy(&buf[totlen], data, len);
+ totlen += len;
+ memcpy(&buf[totlen], &crc, sizeof(crc));
+ totlen += sizeof(crc);
+ totlen = base64_encode(buf, totlen, encoded_buf, 1);
+ console_write(encoded_buf, totlen);
+ console_write("\n", 1);
+}
+
+/*
+ * Returns 1 if full packet has been received.
+ */
+static int
+boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout)
+{
+ int rc;
+ uint16_t crc;
+ uint16_t len;
+
+ if (*out_off + base64_decode_len(in) >= maxout) {
+ return -1;
+ }
+ rc = base64_decode(in, &out[*out_off]);
+ if (rc < 0) {
+ return -1;
+ }
+ *out_off += rc;
+
+ if (*out_off > sizeof(uint16_t)) {
+ len = ntohs(*(uint16_t *)out);
+
+ len = min(len, *out_off - sizeof(uint16_t));
+ out += sizeof(uint16_t);
+ crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len);
+ if (crc || len <= sizeof(crc)) {
+ return 0;
+ }
+ *out_off -= sizeof(crc);
+ out[*out_off] = '\0';
+
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Task which waits reading console, expecting to get image over
+ * serial port.
+ */
+void
+boot_serial_start(int max_input)
+{
+ int rc;
+ int off;
+ char *buf;
+ char *dec;
+ int dec_off;
+ int full_line;
+
+ rc = console_init(NULL);
+ assert(rc == 0);
+ console_echo(0);
+
+ buf = os_malloc(max_input);
+ dec = os_malloc(max_input);
+ assert(buf && dec);
+
+ off = 0;
+ while (1) {
+ rc = console_read(buf + off, max_input - off, &full_line);
+ if (rc <= 0 && !full_line) {
+ continue;
+ }
+ off += rc;
+ if (!full_line) {
+ continue;
+ }
+ if (buf[0] == SHELL_NLIP_PKT_START1 &&
+ buf[1] == SHELL_NLIP_PKT_START2) {
+ dec_off = 0;
+ rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
+ } else if (buf[0] == SHELL_NLIP_DATA_START1 &&
+ buf[1] == SHELL_NLIP_DATA_START2) {
+ rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
+ }
+ if (rc == 1) {
+ boot_serial_input(&dec[2], dec_off - 2);
+ }
+ off = 0;
+ }
+}
diff --git a/boot_serial/src/boot_serial_priv.h b/boot_serial/src/boot_serial_priv.h
new file mode 100644
index 0000000..146aa6a
--- /dev/null
+++ b/boot_serial/src/boot_serial_priv.h
@@ -0,0 +1,74 @@
+/*
+ * 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 __BOOTUTIL_SERIAL_PRIV_H__
+#define __BOOTUTIL_SERIAL_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * From shell.h
+ */
+#define SHELL_NLIP_PKT_START1 6
+#define SHELL_NLIP_PKT_START2 9
+
+#define SHELL_NLIP_DATA_START1 4
+#define SHELL_NLIP_DATA_START2 20
+
+/*
+ * From newtmgr.h
+ */
+#define MGMT_ERR_EINVAL 3
+
+#define NMGR_OP_READ 0
+#define NMGR_OP_WRITE 2
+
+#define NMGR_F_CBOR_RSP_COMPLETE 0x01
+
+#define MGMT_GROUP_ID_DEFAULT 0
+#define MGMT_GROUP_ID_IMAGE 1
+
+#define NMGR_ID_CONS_ECHO_CTRL 1
+#define NMGR_ID_RESET 5
+
+struct nmgr_hdr {
+ uint8_t nh_op; /* NMGR_OP_XXX */
+ uint8_t nh_flags;
+ uint16_t nh_len; /* length of the payload */
+ uint16_t nh_group; /* NMGR_GROUP_XXX */
+ uint8_t nh_seq; /* sequence number */
+ uint8_t nh_id; /* message ID within group */
+};
+
+/*
+ * From imgmgr.h
+ */
+#define IMGMGR_NMGR_OP_STATE 0
+#define IMGMGR_NMGR_OP_UPLOAD 1
+
+
+void boot_serial_input(char *buf, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOOTUTIL_SERIAL_PRIV_H__ */
diff --git a/boot_serial/test/pkg.yml b/boot_serial/test/pkg.yml
new file mode 100644
index 0000000..6b4b52b
--- /dev/null
+++ b/boot_serial/test/pkg.yml
@@ -0,0 +1,30 @@
+# 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.
+#
+pkg.name: boot/boot_serial/test
+pkg.type: unittest
+pkg.description: "Boot serial unit tests."
+pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - boot/boot_serial
+ - test/testutil
+
+pkg.deps.SELFTEST:
+ - sys/console/stub
diff --git a/boot_serial/test/src/boot_test.c b/boot_serial/test/src/boot_test.c
new file mode 100644
index 0000000..94efbd6
--- /dev/null
+++ b/boot_serial/test/src/boot_test.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "syscfg/syscfg.h"
+#include "sysflash/sysflash.h"
+#include "os/endian.h"
+#include "base64/base64.h"
+#include "crc/crc16.h"
+#include "testutil/testutil.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+
+#include "boot_serial_priv.h"
+
+TEST_CASE_DECL(boot_serial_setup)
+TEST_CASE_DECL(boot_serial_empty_msg)
+TEST_CASE_DECL(boot_serial_empty_img_msg)
+TEST_CASE_DECL(boot_serial_img_msg)
+TEST_CASE_DECL(boot_serial_upload_bigger_image)
+
+void
+tx_msg(void *src, int len)
+{
+ boot_serial_input(src, len);
+}
+
+TEST_SUITE(boot_serial_suite)
+{
+ boot_serial_setup();
+ boot_serial_empty_msg();
+ boot_serial_empty_img_msg();
+ boot_serial_img_msg();
+ boot_serial_upload_bigger_image();
+}
+
+int
+boot_serial_test(void)
+{
+ boot_serial_suite();
+ return tu_any_failed;
+}
+
+#if MYNEWT_VAL(SELFTEST)
+int
+main(void)
+{
+ ts_config.ts_print_results = 1;
+ tu_init();
+
+ boot_serial_test();
+
+ return tu_any_failed;
+}
+#endif
diff --git a/boot_serial/test/src/boot_test.h b/boot_serial/test/src/boot_test.h
new file mode 100644
index 0000000..b517a04
--- /dev/null
+++ b/boot_serial/test/src/boot_test.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _BOOT_TEST_H
+#define _BOOT_TEST_H
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "syscfg/syscfg.h"
+#include "sysflash/sysflash.h"
+#include "os/endian.h"
+#include "base64/base64.h"
+#include "crc/crc16.h"
+#include "testutil/testutil.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+
+#include "boot_serial_priv.h"
+
+#ifdef __cplusplus
+#extern "C" {
+#endif
+
+void tx_msg(void *src, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BOOT_TEST_H */
diff --git a/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c b/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c
new file mode 100644
index 0000000..ff69324
--- /dev/null
+++ b/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c
@@ -0,0 +1,35 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_serial_empty_img_msg)
+{
+ char buf[sizeof(struct nmgr_hdr) + 32];
+ struct nmgr_hdr *hdr;
+
+ hdr = (struct nmgr_hdr *)buf;
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->nh_op = NMGR_OP_WRITE;
+ hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE);
+ hdr->nh_id = IMGMGR_NMGR_OP_UPLOAD;
+ hdr->nh_len = htons(2);
+ strcpy((char *)(hdr + 1), "{}");
+
+ tx_msg(buf, sizeof(*hdr) + 2);
+}
diff --git a/boot_serial/test/src/testcases/boot_serial_empty_msg.c b/boot_serial/test/src/testcases/boot_serial_empty_msg.c
new file mode 100644
index 0000000..a4bd470
--- /dev/null
+++ b/boot_serial/test/src/testcases/boot_serial_empty_msg.c
@@ -0,0 +1,39 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_serial_empty_msg)
+{
+ char buf[4];
+ struct nmgr_hdr hdr;
+
+ boot_serial_input(buf, 0);
+
+ tx_msg(buf, 0);
+
+ strcpy(buf, "--");
+ tx_msg(buf, 2);
+
+ memset(&hdr, 0, sizeof(hdr));
+ tx_msg(&hdr, sizeof(hdr));
+
+ hdr.nh_op = NMGR_OP_WRITE;
+
+ tx_msg(&hdr, sizeof(hdr));
+}
diff --git a/boot_serial/test/src/testcases/boot_serial_img_msg.c b/boot_serial/test/src/testcases/boot_serial_img_msg.c
new file mode 100644
index 0000000..c978650
--- /dev/null
+++ b/boot_serial/test/src/testcases/boot_serial_img_msg.c
@@ -0,0 +1,70 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_serial_img_msg)
+{
+ char img[16];
+ char enc_img[BASE64_ENCODE_SIZE(sizeof(img)) + 1];
+ char buf[sizeof(struct nmgr_hdr) + sizeof(enc_img) + 32];
+ int len;
+ int rc;
+ struct nmgr_hdr *hdr;
+ const struct flash_area *fap;
+
+ /* 00000000 a3 64 64 61 74 61 58 10 |.ddataX.|
+ * 00000008 a5 a5 a5 a5 a5 a5 a5 a5 |........|
+ * 00000010 a5 a5 a5 a5 a5 a5 a5 a5 |........|
+ * 00000018 63 6c 65 6e 1a 00 01 14 |clen....|
+ * 00000020 e8 63 6f 66 66 00 |.coff.|
+ */
+ static const uint8_t payload[] = {
+ 0xa3, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x10,
+ /* 16 bytes of image data starts here. */
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0x63, 0x6c, 0x65, 0x6e, 0x1a, 0x00, 0x01, 0x14,
+ 0xe8, 0x63, 0x6f, 0x66, 0x66, 0x00,
+ };
+
+ memset(img, 0xa5, sizeof(img));
+
+ hdr = (struct nmgr_hdr *)buf;
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->nh_op = NMGR_OP_WRITE;
+ hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE);
+ hdr->nh_id = IMGMGR_NMGR_OP_UPLOAD;
+
+ memcpy(hdr + 1, payload, sizeof payload);
+ hdr->nh_len = htons(sizeof payload);
+
+ len = sizeof(*hdr) + sizeof payload;
+ tx_msg(buf, len);
+
+ /*
+ * Validate contents inside image 0 slot
+ */
+ rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+ assert(rc == 0);
+
+ rc = flash_area_read(fap, 0, enc_img, sizeof(img));
+ assert(rc == 0);
+ assert(!memcmp(enc_img, img, sizeof(img)));
+}
diff --git a/boot_serial/test/src/testcases/boot_serial_setup.c b/boot_serial/test/src/testcases/boot_serial_setup.c
new file mode 100644
index 0000000..791e845
--- /dev/null
+++ b/boot_serial/test/src/testcases/boot_serial_setup.c
@@ -0,0 +1,24 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_serial_setup)
+{
+
+}
diff --git a/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c b/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c
new file mode 100644
index 0000000..9f326b8
--- /dev/null
+++ b/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c
@@ -0,0 +1,116 @@
+/*
+ * 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 <tinycbor/cborconstants_p.h>
+
+#include "boot_test.h"
+
+TEST_CASE(boot_serial_upload_bigger_image)
+{
+ char img[256];
+ char enc_img[64];
+ char buf[sizeof(struct nmgr_hdr) + 128];
+ int len;
+ int off;
+ int rc;
+ struct nmgr_hdr *hdr;
+ const struct flash_area *fap;
+ int i;
+
+ const int payload_off = sizeof *hdr;
+ const int img_data_off = payload_off + 8;
+
+ /* 00000000 a3 64 64 61 74 61 58 20 |.ddataX.|
+ * 00000008 00 00 00 00 00 00 00 00 |........|
+ * 00000010 00 00 00 00 00 00 00 00 |........|
+ * 00000018 00 00 00 00 00 00 00 00 |........|
+ * 00000020 00 00 00 00 00 00 00 00 |........|
+ * 00000028 63 6c 65 6e 1a 00 01 14 |clen....|
+ * 00000030 e8 63 6f 66 66 00 |.coff.|
+ */
+ static const uint8_t payload_first[] = {
+ 0xa3, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x20,
+ /* 32 bytes of image data starts here. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x6c, 0x65, 0x6e, 0x1a, 0x00, 0x01, 0x14,
+ 0xe8, 0x63, 0x6f, 0x66, 0x66, 0x00,
+ };
+
+ /* 00000000 a3 64 64 61 74 61 58 20 |.ddataX.|
+ * 00000008 00 00 00 00 00 00 00 00 |........|
+ * 00000010 00 00 00 00 00 00 00 00 |........|
+ * 00000018 00 00 00 00 00 00 00 00 |........|
+ * 00000020 00 00 00 00 00 00 00 00 |........|
+ * 00000028 63 6f 66 66 00 00 |coff..|
+ */
+ static const uint8_t payload_next[] = {
+ 0xa2, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x20,
+ /* 32 bytes of image data starts here. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x6f, 0x66, 0x66,
+ /* 2 bytes of offset value starts here. */
+ 0x00, 0x00
+ };
+
+ for (i = 0; i < sizeof(img); i++) {
+ img[i] = i;
+ }
+
+ for (off = 0; off < sizeof(img); off += 32) {
+ hdr = (struct nmgr_hdr *)buf;
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->nh_op = NMGR_OP_WRITE;
+ hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE);
+ hdr->nh_id = IMGMGR_NMGR_OP_UPLOAD;
+
+ if (off) {
+ memcpy(buf + payload_off, payload_next, sizeof payload_next);
+ len = sizeof payload_next;
+ buf[payload_off + len - 2] = Value8Bit;
+ buf[payload_off + len - 1] = off;
+ } else {
+ memcpy(buf + payload_off, payload_first, sizeof payload_first);
+ len = sizeof payload_first;
+ }
+ memcpy(buf + img_data_off, img + off, 32);
+ hdr->nh_len = htons(len);
+
+ len = sizeof(*hdr) + len;
+
+ tx_msg(buf, len);
+ }
+
+ /*
+ * Validate contents inside image 0 slot
+ */
+ rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+ assert(rc == 0);
+
+ for (off = 0; off < sizeof(img); off += sizeof(enc_img)) {
+ rc = flash_area_read(fap, off, enc_img, sizeof(enc_img));
+ assert(rc == 0);
+ assert(!memcmp(enc_img, &img[off], sizeof(enc_img)));
+ }
+}
diff --git a/boot_serial/test/syscfg.yml b/boot_serial/test/syscfg.yml
new file mode 100644
index 0000000..c982db5
--- /dev/null
+++ b/boot_serial/test/syscfg.yml
@@ -0,0 +1,21 @@
+# 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.
+#
+
+# Package: boot/boot_serial/test
+
+syscfg.vals:
diff --git a/bootutil/design.txt b/bootutil/design.txt
new file mode 100644
index 0000000..47c4d28
--- /dev/null
+++ b/bootutil/design.txt
@@ -0,0 +1,535 @@
+#
+# 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.
+#
+
+****** BOOT LOADER
+
+*** SUMMARY
+
+The Mynewt bootloader comprises two packages:
+
+ * The bootutil library (boot/bootutil)
+ * The boot application (apps/boot)
+
+The bootutil library performs most of the functions of a boot loader. In
+particular, the piece that is missing is the final step of actually jumping to
+the main image. This last step is instead implemented by the boot application.
+Boot loader functionality is separated in this manner to enable unit testing of
+the boot loader. A library can be unit tested, but an application can't.
+Therefore, functionality is delegated to the bootutil library when possible.
+
+*** LIMITATIONS
+
+The boot loader currently only supports images with the following
+characteristics:
+ * Built to run from flash.
+ * Build to run from a fixed location (i.e., not position-independent).
+
+*** IMAGE FORMAT
+
+The following definitions describe the image format.
+
+#define IMAGE_MAGIC 0x96f3b83c
+
+#define IMAGE_HEADER_SIZE 32
+
+struct image_version {
+ uint8_t iv_major;
+ uint8_t iv_minor;
+ uint16_t iv_revision;
+ uint32_t iv_build_num;
+};
+
+/** Image header. All fields are in little endian byte order. */
+struct image_header {
+ uint32_t ih_magic;
+ uint16_t ih_tlv_size; /* Combined size of trailing TLVs (bytes). */
+ uint8_t ih_key_id; /* Which key image is signed with (0xff=unsigned). */
+ uint8_t _pad1;
+ uint16_t ih_hdr_size; /* Size of image header (bytes). */
+ uint16_t _pad2;
+ uint32_t ih_img_size; /* Does not include header. */
+ uint32_t ih_flags; /* IMAGE_F_[...] */
+ struct image_version ih_ver;
+ uint32_t _pad3;
+};
+
+/** Image trailer TLV format. All fields in little endian. */
+struct image_tlv {
+ uint8_t it_type; /* IMAGE_TLV_[...]. */
+ uint8_t _pad;
+ uint16_t it_len /* Data length (not including TLV header). */
+};
+
+/*
+ * Image header flags.
+ */
+#define IMAGE_F_PIC 0x00000001 /* Not currently supported. */
+#define IMAGE_F_SHA256 0x00000002 /* Image contains hash TLV */
+#define IMAGE_F_PKCS15_RSA2048_SHA256 0x00000004 /* PKCS15 w/RSA and SHA */
+#define IMAGE_F_ECDSA224_SHA256 0x00000008 /* ECDSA256 over SHA256 */
+#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
+
+/*
+ * Image trailer TLV types.
+ */
+#define IMAGE_TLV_SHA256 1 /* SHA256 of image hdr and body */
+#define IMAGE_TLV_RSA2048 2 /* RSA2048 of hash output */
+#define IMAGE_TLV_ECDSA224 3 /* ECDSA of hash output */
+
+Optional type-length-value records (TLVs) containing image metadata are placed
+after the end of the image.
+
+The ih_hdr_size field indicates the length of the header, and therefore the
+offset of the image itself. This field provides for backwards compatibility in
+case of changes to the format of the image header.
+
+*** FLASH MAP
+
+A Mynewt device's flash is partitioned according to its _flash map_. At a high
+level, the flash map maps numeric IDs to _flash areas_. A flash area is a
+region of disk with the following properties:
+ (1) An area can be fully erased without affecting any other areas.
+ (2) A write to one area does not restrict writes to other areas.
+
+The boot loader uses the following flash areas:
+
+#define FLASH_AREA_BOOTLOADER 0
+#define FLASH_AREA_IMAGE_0 1
+#define FLASH_AREA_IMAGE_1 2
+#define FLASH_AREA_IMAGE_SCRATCH 3
+
+*** IMAGE SLOTS
+
+A portion of the flash memory is partitioned into two image slots: a primary
+slot (0) and a secondary slot (1). The boot loader will only run an image from
+the primary slot, so images must be built such that they can run from that
+fixed location in flash. If the boot loader needs to run the image resident in
+the secondary slot, it must swap the two images in flash prior to booting.
+
+In addition to the two image slots, the boot loader requires a scratch area to
+allow for reliable image swapping.
+
+*** BOOT VECTOR
+
+At startup, the boot loader determines which of the above three states the
+device is in by inspecting the boot vector. The boot vector consists of two
+records (called "image trailers"), one written at the end of each image slot.
+An image trailer has the following structure:
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ MAGIC (16 octets) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ ~
+ ~ Swap status (128 * min-write-size * 3) ~
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Copy done | 0xff padding (up to min-write-sz - 1) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Image OK | 0xff padding (up to min-write-sz - 1) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+These records are at the end of each image slot. The offset immediately
+following such a record represents the start of the next flash area.
+
+Note: "min-write-size" is a property of the flash hardware. If the hardware
+allows individual bytes to be written at arbitrary addresses, then
+min-write-size is 1. If the hardware only allows writes at even addresses,
+then min-write-size is 2, and so on.
+
+The fields are defined as follows:
+
+1. MAGIC: The following 16 bytes, written in host-byte-order:
+
+ const uint32_t boot_img_magic[4] = {
+ 0xf395c277,
+ 0x7fefd260,
+ 0x0f505235,
+ 0x8079b62c,
+ };
+
+2. Swap status: A series of single-byte records. Each record corresponds to a
+flash sector in an image slot. A swap status byte indicate the location of
+the corresponding sector data. During an image swap, image data is moved one
+sector at a time. The swap status is necessary for resuming a swap operation
+if the device rebooted before a swap operation completed.
+
+3. Copy done: A single byte indicating whether the image in this slot is
+complete (0x01=done; 0xff=not done).
+
+4. Image OK: A single byte indicating whether the image in this slot has been
+confirmed as good by the user (0x01=confirmed; 0xff=not confirmed).
+
+The boot vector records are structured around the limitations imposed by flash
+hardware. As a consequence, they do not have a very intuitive design, and it
+is difficult to get a sense of the state of the device just by looking at the
+boot vector. It is better to map all the possible vector states to the three
+states described above via a set of tables. These tables are reproduced below.
+In these tables, the "pending" and "confirmed" flags are shown for illustrative
+purposes; they are not actually present in the boot vector.
+
+
+ State I
+ | slot-0 | slot-1 |
+ -----------------+--------+--------|
+ magic | Unset | Unset |
+ image-ok | Any | N/A |
+ -----------------+--------+--------'
+ pending | | |
+ confirmed | X | |
+ -----------------+--------+--------'
+ swap: none |
+ -----------------------------------'
+
+
+ State II
+ | slot-0 | slot-1 |
+ -----------------+--------+--------|
+ magic | Any | Good |
+ image-ok | Any | N/A |
+ -----------------+--------+--------'
+ pending | | X |
+ confirmed | X | |
+ -----------------+--------+--------'
+ swap: test |
+ -----------------------------------'
+
+
+ State III
+ | slot-0 | slot-1 |
+ -----------------+--------+--------|
+ magic | Good | Unset |
+ image-ok | 0xff | N/A |
+ -----------------+--------+--------'
+ pending | | |
+ confirmed | | X |
+ -----------------+--------+--------'
+ swap: revert (test image running) |
+ -----------------------------------'
+
+
+ State IV
+ | slot-0 | slot-1 |
+ -----------------+--------+--------|
+ magic | Good | Unset |
+ image-ok | 0x01 | N/A |
+ -----------------+--------+--------'
+ pending | | |
+ confirmed | X | |
+ -----------------+--------+--------'
+ swap: none (confirmed test image) |
+ -----------------------------------'
+
+*** HIGH-LEVEL OPERATION
+
+With the terms defined, we can now explore the boot loader's operation. First,
+a high-level overview of the boot process is presented. Then, the following
+sections describe each step of the process in more detail.
+
+Procedure:
+
+A. Inspect swap status region; is an interrupted swap is being resumed?
+ Yes: Complete the partial swap operation; skip to step C.
+ No: Proceed to step B.
+
+B. Insect boot vector; is a swap requested?
+ Yes.
+ 1. Is the requested image valid (integrity and security check)?
+ Yes.
+ a. Perform swap operation.
+ b. Persist completion of swap procedure to boot vector.
+ c. Proceed to step C.
+ No.
+ a. Erase invalid image.
+ b. Persist failure of swap procedure to boot vector.
+ c. Proceed to step C.
+ No: Proceed to step C.
+
+C. Boot into image in slot 0.
+
+*** BOOT STATES
+
+Logically, you can think of a pair of flags associated with each image slot:
+pending and confirmed. On startup, the boot loader determines the state of the
+device by inspecting each pair of flags. These flags have the following
+meanings:
+
+* pending: image gets tested on next reboot; absent subsequent confirm command,
+ revert to original image on second reboot.
+* confirmed: always use image unless excluded by a test image.
+
+In English, when the user wants to run the secondary image, they set the
+pending flag for the second slot and reboot the device. On startup, the boot
+loader will swap the two images in flash, clear the secondary slot's pending
+flag, and run the newly-copied image in slot 0. This is a temporary state; if
+the device reboots again, the boot loader swaps the images back to their
+original slots and boots into the original image. If the user doesn't want to
+revert to the original state, they can make the current state permanent by
+setting the confirmed flag in slot 0.
+
+Switching to an alternate image is a two-step process (set + confirm) to
+prevent a device from becoming "bricked" by bad firmware. If the device
+crashes immediately upon booting the second image, the boot loader reverts to
+the working image, rather than repeatedly rebooting into the bad image.
+
+The following set of tables illustrate the three possible states that the
+device can be in:
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | |
+ confirmed | X | |
+ ---------------+--------+--------'
+ Image 0 confirmed; |
+ No change on reboot |
+ ---------------------------------'
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | X |
+ confirmed | X | |
+ ---------------+--------+--------'
+ Image 0 confirmed; |
+ Test image 1 on next reboot |
+ ---------------------------------'
+
+ | slot-0 | slot-1 |
+ ---------------+--------+--------|
+ pending | | |
+ confirmed | | X |
+ ---------------+--------+--------'
+ Testing image 0; |
+ Revert to image 1 on next reboot |
+ ---------------------------------'
+
+
+
+*** IMAGE SWAPPING
+
+The boot loader swaps the contents of the two image slots for two reasons:
+ * User has issued an "image test" operation; the image in slot-1 should be
+ run once (state II).
+ * Test image rebooted without being confirmed; the boot loader should
+ revert to the original image currently in slot-1 (state III).
+
+If the boot vector indicates that the image in the secondary slot should be
+run, the boot loader needs to copy it to the primary slot. The image currently
+in the primary slot also needs to be retained in flash so that it can be used
+later. Furthermore, both images need to be recoverable if the boot loader
+resets in the middle of the swap operation. The two images are swapped
+according to the following procedure:
+
+ 1. Determine how many flash sectors each image slot consists of. This
+ number must be the same for both slots.
+ 2. Iterate the list of sector indices in descending order (i.e., starting
+ with the greatest index); current element = "index".
+ b. Erase scratch area.
+ c. Copy slot0[index] to scratch area.
+ d. Write updated swap status (i).
+
+ e. Erase slot1[index]
+ f. Copy slot0[index] to slot1[index]
+ - If these are the last sectors (i.e., first swap being perfomed),
+ copy the full sector *except* the image trailer.
+ - Else, copy entire sector contents.
+ g. Write updated swap status (ii).
+
+ h. Erase slot0[index].
+ i. Copy scratch area slot0[index].
+ j. Write updated swap status (iii).
+
+ 3. Persist completion of swap procedure to slot 0 image trailer.
+
+The additional caveats in step 2f are necessary so that the slot 1 image trailer
+can be written by the user at a later time. With the image trailer unwritten,
+the user can test the image in slot 1 (i.e., transition to state II).
+
+The particulars of step 3 vary depending on whether an image is being tested or
+reverted:
+ * test:
+ o Write slot0.copy_done = 1
+ (should now be in state III)
+
+ * revert:
+ o Write slot0.magic = BOOT_MAGIC
+ o Write slot0.copy_done = 1
+ o Write slot0.image_ok = 1
+ (should now be in state IV)
+
+*** SWAP STATUS
+
+The swap status region allows the boot loader to recover in case it restarts in
+the middle of an image swap operation. The swap status region consists of a
+series of single-byte records. These records are written independently, and
+therefore must be padded according to the minimum write size imposed by the
+flash hardware. In the below figure, a min-write-size of 1 is assumed for
+simplicity. The structure of the swap status region is illustrated below. In
+this figure, a min-write-size of 1 is assumed for simplicity.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |sec127,state 0 |sec127,state 1 |sec127,state 2 |sec126,state 0 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |sec126,state 1 |sec126,state 2 |sec125,state 0 |sec125,state 1 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |sec125,state 2 | |
+ +-+-+-+-+-+-+-+-+ +
+ ~ ~
+ ~ [Records for indices 124 through 1 ~
+ ~ ~
+ ~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ |sec000,state 0 |sec000,state 1 |sec000,state 2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The above is probably not helpful at all; here is a description in English.
+
+Each image slot is partitioned into a sequence of flash sectors. If we were to
+enumerate the sectors in a single slot, starting at 0, we would have a list of
+sector indices. Since there are two image slots, each sector index would
+correspond to a pair of sectors. For example, sector index 0 corresponds to
+the first sector in slot 0 and the first sector in slot 1. Furthermore, we
+impose a limit of 128 indices. If an image slot consists of more than 128
+sectors, the flash layout is not compatible with this boot loader. Finally,
+reverse the list of indices such that the list starts with index 127 and ends
+with 0. The swap status region is a representation of this reversed list.
+
+During a swap operation, each sector index transitions through four separate
+states:
+ 0. slot 0: image 0, slot 1: image 1, scratch: N/A
+ 1. slot 0: image 0, slot 1: N/A, scratch: image 1 (1->s, erase 1)
+ 2. slot 0: N/A, slot 1: image 0, scratch: image 1 (0->1, erase 0)
+ 3. slot 0: image 1, slot 1: image 0, scratch: N/A (s->0)
+
+Each time a sector index transitions to a new state, the boot loader writes a
+record to the swap status region. Logically, the boot loader only needs one
+record per sector index to keep track of the current swap state. However, due
+to limitations imposed by flash hardware, a record cannot be overwritten when
+an index's state changes. To solve this problem, the boot loader uses three
+records per sector index rather than just one.
+
+Each sector-state pair is represented as a set of three records. The record
+values map to the above four states as follows
+
+ | rec0 | rec1 | rec2
+ --------+------+------+------
+ state 0 | 0xff | 0xff | 0xff
+ state 1 | 0x01 | 0xff | 0xff
+ state 2 | 0x01 | 0x02 | 0xff
+ state 3 | 0x01 | 0x02 | 0x03
+
+The swap status region can accommodate 128 sector indices. Hence, the size of
+the region, in bytes, is 128 * min-write-size * 3. The number 128 is chosen
+somewhat arbitrarily and will likely be made configurable. The only
+requirement for the index count is that is is great enough to account for a
+maximum-sized image (i.e., at least as great as the total sector count in an
+image slot). If a device's image slots use less than 128 sectors, the first
+record that gets written will be somewhere in the middle of the region. For
+example, if a slot uses 64 sectors, the first sector index that gets swapped is
+63, which corresponds to the exact halfway point within the region.
+
+
+*** RESET RECOVERY
+
+If the boot loader resets in the middle of a swap operation, the two images may
+be discontiguous in flash. Bootutil recovers from this condition by using the
+boot vector to determine how the image parts are distributed in flash.
+
+The first step is determine where the relevant swap status region is located.
+Because this region is embedded within the image slots, its location in flash
+changes during a swap operation. The below set of tables map boot vector
+contents to swap status location. In these tables, the "source" field
+indicates where the swap status region is located.
+
+ | slot-0 | scratch |
+ ----------+------------+------------|
+ magic | Good | Any |
+ copy-done | 0x01 | N/A |
+ ----------+------------+------------'
+ source: none |
+ ------------------------------------'
+
+ | slot-0 | scratch |
+ ----------+------------+------------|
+ magic | Good | Any |
+ copy-done | 0xff | N/A |
+ ----------+------------+------------'
+ source: slot 0 |
+ ------------------------------------'
+
+ | slot-0 | scratch |
+ ----------+------------+------------|
+ magic | Any | Good |
+ copy-done | Any | N/A |
+ ----------+------------+------------'
+ source: scratch |
+ ------------------------------------'
+
+ | slot-0 | scratch |
+ ----------+------------+------------|
+ magic | Unset | Any |
+ copy-done | 0xff | N/A |
+ ----------+------------+------------|
+ source: varies |
+ ------------------------------------+------------------------------+
+ This represents one of two cases: |
+ o No swaps ever (no status to read, so no harm in checking). |
+ o Mid-revert; status in slot 0. |
+ -------------------------------------------------------------------'
+
+
+If the swap status region indicates that the images are not contiguous,
+bootutil completes the swap operation that was in progress when the system was
+reset. In other words, it applies the procedure defined in the previous
+section, moving image 1 into slot 0 and image 0 into slot 1. If the boot
+status file indicates that an image part is present in the scratch area, this
+part is copied into the correct location by starting at step e or step h in the
+area-swap procedure, depending on whether the part belongs to image 0 or image
+1.
+
+After the swap operation has been completed, the boot loader proceeds as though
+it had just been started.
+
+*** INTEGRITY CHECK
+
+An image is checked for integrity immediately before it gets copied into the
+primary slot. If the boot loader doesn't perform an image swap, then it
+doesn't perform an integrity check.
+
+During the integrity check, the boot loader verifies the following aspects of
+an image:
+ * 32-bit magic number must be correct (0x96f3b83c).
+ * Image must contain a SHA256 TLV.
+ * Calculated SHA256 must matche SHA256 TLV contents.
+ * Image *may* contain a signature TLV. If it does, its contents must be
+ verifiable using a key embedded in the boot loader.
+
+*** SECURITY
+
+As indicated above, the final step of the integrity check is signature
+verification. The boot loader can have one or more public keys embedded in it
+at build time. During signature verification, the boot loader verifies that an
+image was signed with a private key that corresponds to one of its public keys.
+The image signature TLV indicates the index of the key that is has been signed
+with. The boot loader uses this index to identify the corresponding public
+key.
+
+For information on embedding public keys in the boot loader, as well as
+producing signed images, see: boot/bootutil/signed_images.md
diff --git a/bootutil/include/bootutil/bootutil.h b/bootutil/include/bootutil/bootutil.h
new file mode 100644
index 0000000..4473da9
--- /dev/null
+++ b/bootutil/include/bootutil/bootutil.h
@@ -0,0 +1,69 @@
+/*
+ * 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_BOOTUTIL_
+#define H_BOOTUTIL_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BOOT_SWAP_TYPE_NONE 1
+#define BOOT_SWAP_TYPE_TEST 2
+#define BOOT_SWAP_TYPE_REVERT 3
+#define BOOT_SWAP_TYPE_FAIL 4
+
+struct image_header;
+/**
+ * A response object provided by the boot loader code; indicates where to jump
+ * to execute the main image.
+ */
+struct boot_rsp {
+ /** A pointer to the header of the image to be executed. */
+ const struct image_header *br_hdr;
+
+ /**
+ * The flash offset of the image to execute. Indicates the position of
+ * the image header.
+ */
+ uint8_t br_flash_id;
+ uint32_t br_image_addr;
+};
+
+/* you must have pre-allocated all the entries within this structure */
+int boot_go(struct boot_rsp *rsp);
+
+int boot_swap_type(void);
+
+int boot_set_pending(void);
+int boot_set_confirmed(void);
+
+#define SPLIT_GO_OK (0)
+#define SPLIT_GO_NON_MATCHING (-1)
+#define SPLIT_GO_ERR (-2)
+int
+split_go(int loader_slot, int split_slot, void **entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bootutil/include/bootutil/bootutil_test.h b/bootutil/include/bootutil/bootutil_test.h
new file mode 100644
index 0000000..4188bb1
--- /dev/null
+++ b/bootutil/include/bootutil/bootutil_test.h
@@ -0,0 +1,33 @@
+/*
+ * 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_BOOTUTIL_TEST_
+#define H_BOOTUTIL_TEST_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int boot_test_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bootutil/include/bootutil/image.h b/bootutil/include/bootutil/image.h
new file mode 100644
index 0000000..ce98e0c
--- /dev/null
+++ b/bootutil/include/bootutil/image.h
@@ -0,0 +1,95 @@
+/*
+ * 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_IMAGE_
+#define H_IMAGE_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct flash_area;
+
+#define IMAGE_MAGIC 0x96f3b83c
+#define IMAGE_MAGIC_NONE 0xffffffff
+
+#define IMAGE_HEADER_SIZE 32
+
+/*
+ * Image header flags.
+ */
+#define IMAGE_F_PIC 0x00000001 /* Not currently supported. */
+#define IMAGE_F_SHA256 0x00000002 /* Image contains hash TLV */
+#define IMAGE_F_PKCS15_RSA2048_SHA256 0x00000004 /* PKCS15 w/RSA and SHA */
+#define IMAGE_F_ECDSA224_SHA256 0x00000008 /* ECDSA256 over SHA256 */
+#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
+
+/*
+ * Image trailer TLV types.
+ */
+#define IMAGE_TLV_SHA256 1 /* SHA256 of image hdr and body */
+#define IMAGE_TLV_RSA2048 2 /* RSA2048 of hash output */
+#define IMAGE_TLV_ECDSA224 3 /* ECDSA of hash output */
+
+struct image_version {
+ uint8_t iv_major;
+ uint8_t iv_minor;
+ uint16_t iv_revision;
+ uint32_t iv_build_num;
+};
+
+#define IMAGE_SIZE(hdr) \
+ ((hdr)->ih_tlv_size + (hdr)->ih_hdr_size + (hdr)->ih_img_size)
+
+/** Image header. All fields are in little endian byte order. */
+struct image_header {
+ uint32_t ih_magic;
+ uint16_t ih_tlv_size; /* Combined size of trailing TLVs (bytes). */
+ uint8_t ih_key_id; /* Which key image is signed with (0xff=unsigned). */
+ uint8_t _pad1;
+ uint16_t ih_hdr_size; /* Size of image header (bytes). */
+ uint16_t _pad2;
+ uint32_t ih_img_size; /* Does not include header. */
+ uint32_t ih_flags; /* IMAGE_F_[...]. */
+ struct image_version ih_ver;
+ uint32_t _pad3;
+};
+
+/** Image trailer TLV format. All fields in little endian. */
+struct image_tlv {
+ uint8_t it_type; /* IMAGE_TLV_[...]. */
+ uint8_t _pad;
+ uint16_t it_len; /* Data length (not including TLV header). */
+};
+
+_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE,
+ "struct image_header not required size");
+
+int bootutil_img_validate(struct image_header *hdr,
+ const struct flash_area *fap,
+ uint8_t *tmp_buf, uint32_t tmp_buf_sz,
+ uint8_t *seed, int seed_len, uint8_t *out_hash);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bootutil/include/bootutil/sign_key.h b/bootutil/include/bootutil/sign_key.h
new file mode 100644
index 0000000..47b2570
--- /dev/null
+++ b/bootutil/include/bootutil/sign_key.h
@@ -0,0 +1,41 @@
+/*
+ * 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 __BOOTUTIL_SIGN_KEY_H_
+#define __BOOTUTIL_SIGN_KEY_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bootutil_key {
+ const uint8_t *key;
+ const unsigned int *len;
+};
+
+extern const struct bootutil_key bootutil_keys[];
+extern const int bootutil_key_cnt;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOOTUTIL_SIGN_KEY_H_ */
diff --git a/bootutil/pkg.yml b/bootutil/pkg.yml
new file mode 100644
index 0000000..ca9dbf7
--- /dev/null
+++ b/bootutil/pkg.yml
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+pkg.name: boot/bootutil
+pkg.description: The bootutil library performs most of the functions of a boot loader.
+pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+ - boot
+ - bootloader
+
+pkg.deps:
+ - hw/hal
+ - crypto/mbedtls
+ - kernel/os
+ - sys/defs
+ - sys/flash_map
diff --git a/bootutil/signed_images.md b/bootutil/signed_images.md
new file mode 100644
index 0000000..64869c1
--- /dev/null
+++ b/bootutil/signed_images.md
@@ -0,0 +1,95 @@
+<!--
+#
+# 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.
+#
+-->
+
+## Image signing
+
+This signs the image by computing hash over the image, and then
+signing that hash. Signature is computed by newt tool when it's
+creating the image. This signature is placed in the image trailer.
+
+The public key of this keypair must be included in the bootloader,
+as it verifies it before allowing the image to run.
+
+This facility allows you to use multiple signing keys. This would
+be useful when you want to prevent production units from booting
+development images, but want development units to be able to boot
+both production images and development images.
+
+## Creating signing keys
+First you need a keypair to use for signing. You can create
+one with openssl command line tool.
+
+openssl genrsa -out image_sign.pem 2048
+
+This created a file which contains both the private and public key,
+and will be used when signing images.
+
+Then you need to extract the public key from this to include it
+in the bootloader. Bootloader need to keep key parsing minimal,
+so it expects simple key format.
+
+openssl rsa -in image_sign.pem -pubout -out image_sign_pub.der -outform DER -RSAPublicKey_out
+
+Now the public key is in file called image_sign_pub.der.
+
+## Creating a key package
+
+xxd -i image_sign_pub.der image_sign_pub.c.import
+
+Then you need to create a package containing this key, or keys.
+In the pkg.yml for this package, you advertise feature IMAGE_KEYS_RSA or
+IMAGE_KEYS_EC.
+Once this is done, bootloader will expect keys to be filled in
+'bootutil_keys', and the number of keys to be in 'bootutil_key_cnt'.
+
+## Sample pkg.yml
+This gets bootutil to turn on image signature validation.
+
+ pkg.name: libs/mykeys
+ pkg.deps:
+ - libs/bootutil
+
+## Sample source file
+This exports the keys.
+
+ #include <bootutil/sign_key.h>
+
+ #include "image_sign_pub.c.import"
+
+ const struct bootutil_key bootutil_keys[] = {
+ [0] = {
+ .key = image_sign_pub_der,
+ .len = &image_sign_pub_der_len,
+ }
+ };
+
+ const int bootutil_key_cnt = sizeof(bootutil_keys) / sizeof(bootutil_keys[0]);
+
+## Building bootloader
+
+Enable the BOOTUTIL_SIGN_RSA syscfg setting in your app or target syscfg.yml
+file
+
+ syscfg.vals:
+ BOOTUTIL_SIGN_RSA: 1
+
+After you've created the key package, you must include it in the build
+for bootloader. So modify the pkg.yml for apps/boot to include it.
diff --git a/bootutil/src/bootutil_misc.c b/bootutil/src/bootutil_misc.c
new file mode 100644
index 0000000..f3ab9d5
--- /dev/null
+++ b/bootutil/src/bootutil_misc.c
@@ -0,0 +1,456 @@
+/*
+ * 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 <string.h>
+#include <inttypes.h>
+
+#include "syscfg/syscfg.h"
+#include "sysflash/sysflash.h"
+#include "hal/hal_bsp.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+#include "os/os.h"
+#include "bootutil/image.h"
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+
+int boot_current_slot;
+
+const uint32_t boot_img_magic[4] = {
+ 0xf395c277,
+ 0x7fefd260,
+ 0x0f505235,
+ 0x8079b62c,
+};
+
+struct boot_swap_table {
+ /** * For each field, a value of 0 means "any". */
+ uint8_t bsw_magic_slot0;
+ uint8_t bsw_magic_slot1;
+ uint8_t bsw_image_ok_slot0;
+
+ uint8_t bsw_swap_type;
+};
+
+/**
+ * This set of tables maps image trailer contents to swap operation type.
+ * When searching for a match, these tables must be iterated sequentially.
+ */
+static const struct boot_swap_table boot_swap_tables[] = {
+ {
+ /* | slot-0 | slot-1 |
+ *----------+------------+------------|
+ * magic | Unset | Unset |
+ * image-ok | Any | N/A |
+ * ---------+------------+------------'
+ * swap: none |
+ * -----------------------------------'
+ */
+ .bsw_magic_slot0 = BOOT_MAGIC_UNSET,
+ .bsw_magic_slot1 = BOOT_MAGIC_UNSET,
+ .bsw_image_ok_slot0 = 0,
+ .bsw_swap_type = BOOT_SWAP_TYPE_NONE,
+ },
+
+ {
+ /* | slot-0 | slot-1 |
+ *----------+------------+------------|
+ * magic | Any | Good |
+ * image-ok | Any | N/A |
+ * ---------+------------+------------'
+ * swap: test |
+ * -----------------------------------'
+ */
+ .bsw_magic_slot0 = 0,
+ .bsw_magic_slot1 = BOOT_MAGIC_GOOD,
+ .bsw_image_ok_slot0 = 0,
+ .bsw_swap_type = BOOT_SWAP_TYPE_TEST,
+ },
+
+ {
+ /* | slot-0 | slot-1 |
+ *----------+------------+------------|
+ * magic | Good | Unset |
+ * image-ok | 0xff | N/A |
+ * ---------+------------+------------'
+ * swap: revert (test image running) |
+ * -----------------------------------'
+ */
+ .bsw_magic_slot0 = BOOT_MAGIC_GOOD,
+ .bsw_magic_slot1 = BOOT_MAGIC_UNSET,
+ .bsw_image_ok_slot0 = 0xff,
+ .bsw_swap_type = BOOT_SWAP_TYPE_REVERT,
+ },
+
+ {
+ /* | slot-0 | slot-1 |
+ *----------+------------+------------|
+ * magic | Good | Unset |
+ * image-ok | 0x01 | N/A |
+ * ---------+------------+------------'
+ * swap: none (confirmed test image) |
+ * -----------------------------------'
+ */
+ .bsw_magic_slot0 = BOOT_MAGIC_GOOD,
+ .bsw_magic_slot1 = BOOT_MAGIC_UNSET,
+ .bsw_image_ok_slot0 = 0x01,
+ .bsw_swap_type = BOOT_SWAP_TYPE_NONE,
+ },
+};
+
+#define BOOT_SWAP_TABLES_COUNT \
+ (sizeof boot_swap_tables / sizeof boot_swap_tables[0])
+
+int
+boot_magic_code(const uint32_t *magic)
+{
+ int i;
+
+ if (memcmp(magic, boot_img_magic, sizeof boot_img_magic) == 0) {
+ return BOOT_MAGIC_GOOD;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (magic[i] == 0xffffffff) {
+ return BOOT_MAGIC_UNSET;
+ }
+ }
+
+ return BOOT_MAGIC_BAD;
+}
+
+uint32_t
+boot_status_sz(uint8_t min_write_sz)
+{
+ return BOOT_STATUS_MAX_ENTRIES * BOOT_STATUS_STATE_COUNT * min_write_sz;
+}
+
+uint32_t
+boot_trailer_sz(uint8_t min_write_sz)
+{
+ return sizeof boot_img_magic +
+ boot_status_sz(min_write_sz) +
+ min_write_sz * 2;
+}
+
+static uint32_t
+boot_magic_off(const struct flash_area *fap)
+{
+ uint32_t off_from_end;
+ uint8_t elem_sz;
+
+ elem_sz = flash_area_align(fap);
+
+ off_from_end = boot_trailer_sz(elem_sz);
+
+ assert(off_from_end <= fap->fa_size);
+ return fap->fa_size - off_from_end;
+}
+
+uint32_t
+boot_status_off(const struct flash_area *fap)
+{
+ return boot_magic_off(fap) + sizeof boot_img_magic;
+}
+
+static uint32_t
+boot_copy_done_off(const struct flash_area *fap)
+{
+ return fap->fa_size - flash_area_align(fap) * 2;
+}
+
+static uint32_t
+boot_image_ok_off(const struct flash_area *fap)
+{
+ return fap->fa_size - flash_area_align(fap);
+}
+
+int
+boot_read_swap_state(const struct flash_area *fap,
+ struct boot_swap_state *state)
+{
+ uint32_t magic[4];
+ uint32_t off;
+ int rc;
+
+ off = boot_magic_off(fap);
+ rc = flash_area_read(fap, off, magic, sizeof magic);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ state->magic = boot_magic_code(magic);
+
+ off = boot_copy_done_off(fap);
+ rc = flash_area_read(fap, off, &state->copy_done, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ off = boot_image_ok_off(fap);
+ rc = flash_area_read(fap, off, &state->image_ok, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ return 0;
+}
+
+/**
+ * Reads the image trailer from the scratch area.
+ */
+int
+boot_read_swap_state_scratch(struct boot_swap_state *state)
+{
+ const struct flash_area *fap;
+ int rc;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
+ if (rc) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = boot_read_swap_state(fap, state);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+/**
+ * Reads the image trailer from a given image slot.
+ */
+int
+boot_read_swap_state_img(int slot, struct boot_swap_state *state)
+{
+ const struct flash_area *fap;
+ int area_id;
+ int rc;
+
+ area_id = flash_area_id_from_image_slot(slot);
+ rc = flash_area_open(area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = boot_read_swap_state(fap, state);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+int
+boot_write_magic(const struct flash_area *fap)
+{
+ uint32_t off;
+ int rc;
+
+ off = boot_magic_off(fap);
+
+ rc = flash_area_write(fap, off, boot_img_magic, sizeof boot_img_magic);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ return 0;
+}
+
+int
+boot_write_copy_done(const struct flash_area *fap)
+{
+ uint32_t off;
+ uint8_t val;
+ int rc;
+
+ off = boot_copy_done_off(fap);
+
+ val = 1;
+ rc = flash_area_write(fap, off, &val, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ return 0;
+}
+
+int
+boot_write_image_ok(const struct flash_area *fap)
+{
+ uint32_t off;
+ uint8_t val;
+ int rc;
+
+ off = boot_image_ok_off(fap);
+
+ val = 1;
+ rc = flash_area_write(fap, off, &val, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ return 0;
+}
+
+int
+boot_swap_type(void)
+{
+ const struct boot_swap_table *table;
+ struct boot_swap_state state_slot0;
+ struct boot_swap_state state_slot1;
+ int rc;
+ int i;
+
+ rc = boot_read_swap_state_img(0, &state_slot0);
+ assert(rc == 0);
+
+ rc = boot_read_swap_state_img(1, &state_slot1);
+ assert(rc == 0);
+
+ for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
+ table = boot_swap_tables + i;
+
+ if ((table->bsw_magic_slot0 == 0 ||
+ table->bsw_magic_slot0 == state_slot0.magic) &&
+ (table->bsw_magic_slot1 == 0 ||
+ table->bsw_magic_slot1 == state_slot1.magic) &&
+ (table->bsw_image_ok_slot0 == 0 ||
+ table->bsw_image_ok_slot0 == state_slot0.image_ok)) {
+
+ return table->bsw_swap_type;
+ }
+ }
+
+ assert(0);
+ return BOOT_SWAP_TYPE_NONE;
+}
+
+/**
+ * Marks the image in slot 1 as pending. On the next reboot, the system will
+ * perform a one-time boot of the slot 1 image.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+boot_set_pending(void)
+{
+ const struct flash_area *fap;
+ struct boot_swap_state state_slot1;
+ int area_id;
+ int rc;
+
+ rc = boot_read_swap_state_img(1, &state_slot1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (state_slot1.magic) {
+ case BOOT_MAGIC_GOOD:
+ /* Swap already scheduled. */
+ return 0;
+
+ case BOOT_MAGIC_UNSET:
+ area_id = flash_area_id_from_image_slot(1);
+ rc = flash_area_open(area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ } else {
+ rc = boot_write_magic(fap);
+ }
+
+ flash_area_close(fap);
+ return rc;
+
+ default:
+ /* XXX: Temporary assert. */
+ assert(0);
+ return -1;
+ }
+}
+
+/**
+ * Marks the image in slot 0 as confirmed. The system will continue booting into the image in slot 0 until told to boot from a different slot.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+boot_set_confirmed(void)
+{
+ const struct flash_area *fap;
+ struct boot_swap_state state_slot0;
+ int rc;
+
+ rc = boot_read_swap_state_img(0, &state_slot0);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (state_slot0.magic) {
+ case BOOT_MAGIC_GOOD:
+ /* Confirm needed; proceed. */
+ break;
+
+ case BOOT_MAGIC_UNSET:
+ /* Already confirmed. */
+ return 0;
+
+ case BOOT_MAGIC_BAD:
+ /* Unexpected state. */
+ return BOOT_EBADVECT;
+ }
+
+ if (state_slot0.copy_done == 0xff) {
+ /* Swap never completed. This is unexpected. */
+ return BOOT_EBADVECT;
+ }
+
+ if (state_slot0.image_ok != 0xff) {
+ /* Already confirmed. */
+ return 0;
+ }
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+ if (rc) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = boot_write_image_ok(fap);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
diff --git a/bootutil/src/bootutil_priv.h b/bootutil/src/bootutil_priv.h
new file mode 100644
index 0000000..79885ae
--- /dev/null
+++ b/bootutil/src/bootutil_priv.h
@@ -0,0 +1,109 @@
+/*
+ * 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_BOOTUTIL_PRIV_
+#define H_BOOTUTIL_PRIV_
+
+#include "syscfg/syscfg.h"
+#include "bootutil/image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct flash_area;
+
+#define BOOT_EFLASH 1
+#define BOOT_EFILE 2
+#define BOOT_EBADIMAGE 3
+#define BOOT_EBADVECT 4
+#define BOOT_EBADSTATUS 5
+#define BOOT_ENOMEM 6
+#define BOOT_EBADARGS 7
+
+#define BOOT_TMPBUF_SZ 256
+
+/*
+ * Maintain state of copy progress.
+ */
+struct boot_status {
+ uint32_t idx; /* Which area we're operating on */
+ uint8_t state; /* Which part of the swapping process are we at */
+};
+
+#define BOOT_MAGIC_GOOD 1
+#define BOOT_MAGIC_BAD 2
+#define BOOT_MAGIC_UNSET 3
+
+/**
+ * End-of-image slot structure.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ MAGIC (16 octets) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ ~
+ * ~ Swap status (variable, aligned) ~
+ * ~ ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Copy done | 0xff padding (up to min-write-sz - 1) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Image OK | 0xff padding (up to min-write-sz - 1) ~
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+extern const uint32_t boot_img_magic[4];
+
+struct boot_swap_state {
+ uint8_t magic; /* One of the BOOT_MAGIC_[...] values. */
+ uint8_t copy_done;
+ uint8_t image_ok;
+};
+
+#define BOOT_STATUS_STATE_COUNT 3
+#define BOOT_STATUS_MAX_ENTRIES 128
+
+#define BOOT_STATUS_SOURCE_NONE 0
+#define BOOT_STATUS_SOURCE_SCRATCH 1
+#define BOOT_STATUS_SOURCE_SLOT0 2
+
+int bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen,
+ uint8_t key_id);
+
+uint32_t boot_trailer_sz(uint8_t min_write_sz);
+uint32_t boot_status_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_img(int slot, struct boot_swap_state *state);
+int boot_read_swap_state_scratch(struct boot_swap_state *state);
+int boot_write_magic(const struct flash_area *fap);
+int boot_write_status(struct boot_status *bs);
+int boot_schedule_test_swap(void);
+int boot_write_copy_done(const struct flash_area *fap);
+int boot_write_image_ok(const struct flash_area *fap);
+
+uint32_t boot_status_sz(uint8_t min_write_sz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/bootutil/src/image_ec.c b/bootutil/src/image_ec.c
new file mode 100644
index 0000000..6ddac59
--- /dev/null
+++ b/bootutil/src/image_ec.c
@@ -0,0 +1,121 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+#include "bootutil/sign_key.h"
+
+#include "mbedtls/sha256.h"
+#include "mbedtls/ecdsa.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/asn1.h"
+
+#include "bootutil_priv.h"
+
+/*
+ * Declaring these like this adds NULL termination.
+ */
+static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
+static const uint8_t ec_secp224r1_oid[] = MBEDTLS_OID_EC_GRP_SECP224R1;
+
+/*
+ * Parse the public key used for signing. Simple RSA format.
+ */
+static int
+bootutil_parse_eckey(mbedtls_ecdsa_context *ctx, uint8_t **p, uint8_t *end)
+{
+ size_t len;
+ mbedtls_asn1_buf alg;
+ mbedtls_asn1_buf param;
+
+ if (mbedtls_asn1_get_tag(p, end, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) {
+ return -1;
+ }
+ end = *p + len;
+
+ if (mbedtls_asn1_get_alg(p, end, &alg, ¶m)) {
+ return -2;
+ }
+ if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
+ memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
+ return -3;
+ }
+ if (param.len != sizeof(ec_secp224r1_oid) - 1||
+ memcmp(param.p, ec_secp224r1_oid, sizeof(ec_secp224r1_oid) - 1)) {
+ return -4;
+ }
+
+ if (mbedtls_ecp_group_load_secp224r1(&ctx->grp)) {
+ return -5;
+ }
+
+ if (mbedtls_asn1_get_bitstring_null(p, end, &len)) {
+ return -6;
+ }
+ if (*p + len != end) {
+ return -7;
+ }
+
+ if (mbedtls_ecp_point_read_binary(&ctx->grp, &ctx->Q, *p, end - *p)) {
+ return -8;
+ }
+
+ if (mbedtls_ecp_check_pubkey(&ctx->grp, &ctx->Q)) {
+ return -9;
+ }
+ return 0;
+}
+
+static int
+bootutil_cmp_sig(mbedtls_ecdsa_context *ctx, uint8_t *hash, uint32_t hlen,
+ uint8_t *sig, int slen)
+{
+ return mbedtls_ecdsa_read_signature(ctx, hash, hlen, sig, slen);
+}
+
+int
+bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen,
+ uint8_t key_id)
+{
+ int rc;
+ uint8_t *cp;
+ uint8_t *end;
+ mbedtls_ecdsa_context ctx;
+
+ mbedtls_ecdsa_init(&ctx);
+
+ cp = (uint8_t *)bootutil_keys[key_id].key;
+ end = cp + *bootutil_keys[key_id].len;
+
+ rc = bootutil_parse_eckey(&ctx, &cp, end);
+ if (rc) {
+ return -1;
+ }
+
+ while (sig[slen - 1] == '\0') {
+ slen--;
+ }
+ rc = bootutil_cmp_sig(&ctx, hash, hlen, sig, slen);
+ mbedtls_ecdsa_free(&ctx);
+
+ return rc;
+}
+#endif /* MYNEWT_VAL(BOOTUTIL_SIGN_EC) */
diff --git a/bootutil/src/image_rsa.c b/bootutil/src/image_rsa.c
new file mode 100644
index 0000000..7b1f4d4
--- /dev/null
+++ b/bootutil/src/image_rsa.c
@@ -0,0 +1,144 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA)
+#include "bootutil/sign_key.h"
+
+#include "mbedtls/rsa.h"
+#include "mbedtls/asn1.h"
+
+#include "bootutil_priv.h"
+
+static const uint8_t sha256_oid[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20
+};
+
+/*
+ * Parse the public key used for signing. Simple RSA format.
+ */
+static int
+bootutil_parse_rsakey(mbedtls_rsa_context *ctx, uint8_t **p, uint8_t *end)
+{
+ int rc;
+ size_t len;
+
+ if ((rc = mbedtls_asn1_get_tag(p, end, &len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
+ return -1;
+ }
+
+ if (*p + len != end) {
+ return -2;
+ }
+
+ if ((rc = mbedtls_asn1_get_mpi(p, end, &ctx->N)) != 0 ||
+ (rc = mbedtls_asn1_get_mpi(p, end, &ctx->E)) != 0) {
+ return -3;
+ }
+
+ if (*p != end) {
+ return -4;
+ }
+
+ if ((rc = mbedtls_rsa_check_pubkey(ctx)) != 0) {
+ return -5;
+ }
+
+ ctx->len = mbedtls_mpi_size(&ctx->N);
+
+ return 0;
+}
+
+/*
+ * PKCS1.5 using RSA2048 computed over SHA256.
+ */
+static int
+bootutil_cmp_rsasig(mbedtls_rsa_context *ctx, uint8_t *hash, uint32_t hlen,
+ uint8_t *sig)
+{
+ uint8_t buf[MBEDTLS_MPI_MAX_SIZE];
+ uint8_t *p;
+
+ if (ctx->len != 256) {
+ return -1;
+ }
+
+ if (mbedtls_rsa_public(ctx, sig, buf)) {
+ return -1;
+ }
+
+ p = buf;
+
+ if (*p++ != 0 || *p++ != MBEDTLS_RSA_SIGN) {
+ return -1;
+ }
+
+ while (*p != 0) {
+ if (p >= buf + ctx->len - 1 || *p != 0xFF) {
+ return -1;
+ }
+ p++;
+ }
+ p++;
+
+ if ((p - buf) + sizeof(sha256_oid) + hlen != ctx->len) {
+ return -1;
+ }
+
+ if (memcmp(p, sha256_oid, sizeof(sha256_oid))) {
+ return -1;
+ }
+ p += sizeof(sha256_oid);
+
+ if (memcmp(p, hash, hlen)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen,
+ uint8_t key_id)
+{
+ mbedtls_rsa_context ctx;
+ int rc;
+ uint8_t *cp;
+ uint8_t *end;
+
+ mbedtls_rsa_init(&ctx, 0, 0);
+
+ cp = (uint8_t *)bootutil_keys[key_id].key;
+ end = cp + *bootutil_keys[key_id].len;
+
+ rc = bootutil_parse_rsakey(&ctx, &cp, end);
+ if (rc || slen != ctx.len) {
+ mbedtls_rsa_free(&ctx);
+ return rc;
+ }
+ rc = bootutil_cmp_rsasig(&ctx, hash, hlen, sig);
+ mbedtls_rsa_free(&ctx);
+
+ return rc;
+}
+#endif /* MYNEWT_VAL(BOOTUTIL_SIGN_RSA) */
diff --git a/bootutil/src/image_validate.c b/bootutil/src/image_validate.c
new file mode 100644
index 0000000..98fdc9b
--- /dev/null
+++ b/bootutil/src/image_validate.c
@@ -0,0 +1,200 @@
+/*
+ * 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 <inttypes.h>
+#include <string.h>
+
+#include "syscfg/syscfg.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+#include "bootutil/image.h"
+#include "bootutil/sign_key.h"
+
+#include "mbedtls/sha256.h"
+#include "mbedtls/rsa.h"
+#include "mbedtls/ecdsa.h"
+#include "mbedtls/asn1.h"
+
+#include "bootutil_priv.h"
+
+/*
+ * Compute SHA256 over the image.
+ */
+static int
+bootutil_img_hash(struct image_header *hdr, const struct flash_area *fap,
+ uint8_t *tmp_buf, uint32_t tmp_buf_sz,
+ uint8_t *hash_result, uint8_t *seed, int seed_len)
+{
+ mbedtls_sha256_context sha256_ctx;
+ uint32_t blk_sz;
+ uint32_t size;
+ uint32_t off;
+ int rc;
+
+ mbedtls_sha256_init(&sha256_ctx);
+ mbedtls_sha256_starts(&sha256_ctx, 0);
+
+ /* in some cases (split image) the hash is seeded with data from
+ * the loader image */
+ if(seed && (seed_len > 0)) {
+ mbedtls_sha256_update(&sha256_ctx, seed, seed_len);
+ }
+
+ size = hdr->ih_img_size + hdr->ih_hdr_size;
+
+ /*
+ * Hash is computed over image header and image itself. No TLV is
+ * included ATM.
+ */
+ size = hdr->ih_img_size + hdr->ih_hdr_size;
+ for (off = 0; off < size; off += blk_sz) {
+ blk_sz = size - off;
+ if (blk_sz > tmp_buf_sz) {
+ blk_sz = tmp_buf_sz;
+ }
+ rc = flash_area_read(fap, off, tmp_buf, blk_sz);
+ if (rc) {
+ return rc;
+ }
+ mbedtls_sha256_update(&sha256_ctx, tmp_buf, blk_sz);
+ }
+ mbedtls_sha256_finish(&sha256_ctx, hash_result);
+
+ return 0;
+}
+
+/*
+ * Verify the integrity of the image.
+ * Return non-zero if image could not be validated/does not validate.
+ */
+int
+bootutil_img_validate(struct image_header *hdr, const struct flash_area *fap,
+ uint8_t *tmp_buf, uint32_t tmp_buf_sz,
+ uint8_t *seed, int seed_len, uint8_t *out_hash)
+{
+ uint32_t off;
+ uint32_t size;
+ uint32_t sha_off = 0;
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+ uint32_t sig_off = 0;
+ uint32_t sig_len = 0;
+#endif
+ struct image_tlv tlv;
+ uint8_t buf[256];
+ uint8_t hash[32];
+ int rc;
+
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA)
+ if ((hdr->ih_flags & IMAGE_F_PKCS15_RSA2048_SHA256) == 0) {
+ return -1;
+ }
+#endif
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+ if ((hdr->ih_flags & IMAGE_F_ECDSA224_SHA256) == 0) {
+ return -1;
+ }
+#endif
+ if ((hdr->ih_flags & IMAGE_F_SHA256) == 0) {
+ return -1;
+ }
+
+ rc = bootutil_img_hash(hdr, fap, tmp_buf, tmp_buf_sz, hash,
+ seed, seed_len);
+ if (rc) {
+ return rc;
+ }
+
+ if (out_hash) {
+ memcpy(out_hash, hash, 32);
+ }
+
+ /* After image there are TLVs. */
+ off = hdr->ih_img_size + hdr->ih_hdr_size;
+ size = off + hdr->ih_tlv_size;
+
+ for (; off < size; off += sizeof(tlv) + tlv.it_len) {
+ rc = flash_area_read(fap, off, &tlv, sizeof tlv);
+ if (rc) {
+ return rc;
+ }
+ if (tlv.it_type == IMAGE_TLV_SHA256) {
+ if (tlv.it_len != sizeof(hash)) {
+ return -1;
+ }
+ sha_off = off + sizeof(tlv);
+ }
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA)
+ if (tlv.it_type == IMAGE_TLV_RSA2048) {
+ if (tlv.it_len != 256) { /* 2048 bits */
+ return -1;
+ }
+ sig_off = off + sizeof(tlv);
+ sig_len = tlv.it_len;
+ }
+#endif
+#if MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+ if (tlv.it_type == IMAGE_TLV_ECDSA224) {
+ if (tlv.it_len < 64) { /* oids + 2 * 28 bytes */
+ return -1;
+ }
+ sig_off = off + sizeof(tlv);
+ sig_len = tlv.it_len;
+ }
+#endif
+ }
+ if (hdr->ih_flags & IMAGE_F_SHA256) {
+ if (!sha_off) {
+ /*
+ * Header said there should be hash TLV, no TLV found.
+ */
+ return -1;
+ }
+ rc = flash_area_read(fap, sha_off, buf, sizeof hash);
+ if (rc) {
+ return rc;
+ }
+ if (memcmp(hash, buf, sizeof(hash))) {
+ return -1;
+ }
+ }
+#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) || MYNEWT_VAL(BOOTUTIL_SIGN_EC)
+ if (!sig_off) {
+ /*
+ * Header said there should be PKCS1.v5 signature, no TLV
+ * found.
+ */
+ return -1;
+ }
+ rc = flash_area_read(fap, sig_off, buf, sig_len);
+ if (rc) {
+ return -1;
+ }
+
+ if (hdr->ih_key_id >= bootutil_key_cnt) {
+ return -1;
+ }
+ rc = bootutil_verify_sig(hash, sizeof(hash), buf, sig_len, hdr->ih_key_id);
+ if (rc) {
+ return -1;
+ }
+#endif
+ return 0;
+}
diff --git a/bootutil/src/loader.c b/bootutil/src/loader.c
new file mode 100644
index 0000000..4d17291
--- /dev/null
+++ b/bootutil/src/loader.c
@@ -0,0 +1,1100 @@
+/*
+ * 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.
+ */
+
+/**
+ * This file provides an interface to the boot loader. Functions defined in
+ * this file should only be called while the boot loader is running.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sysflash/sysflash.h"
+#include "flash_map/flash_map.h"
+#include <hal/hal_flash.h>
+#include <os/os_malloc.h>
+#include "bootutil/bootutil.h"
+#include "bootutil/image.h"
+#include "bootutil_priv.h"
+
+#define BOOT_MAX_IMG_SECTORS 120
+
+/** Number of image slots in flash; currently limited to two. */
+#define BOOT_NUM_SLOTS 2
+
+static struct {
+ struct {
+ struct image_header hdr;
+ struct flash_area *sectors;
+ } imgs[BOOT_NUM_SLOTS];
+
+ int num_img_sectors;
+ struct flash_area scratch_sector;
+
+ uint8_t write_sz;
+} boot_data;
+
+struct boot_status_table {
+ /**
+ * For each field, a value of 0 means "any".
+ */
+ uint8_t bst_magic_slot0;
+ uint8_t bst_magic_scratch;
+ uint8_t bst_copy_done_slot0;
+ uint8_t bst_status_source;
+};
+
+/**
+ * This set of tables maps swap state contents to boot status location.
+ * When searching for a match, these tables must be iterated in order.
+ */
+static const struct boot_status_table boot_status_tables[] = {
+ {
+ /* | slot-0 | scratch |
+ * ----------+------------+------------|
+ * magic | Good | Any |
+ * copy-done | 0x01 | N/A |
+ * ----------+------------+------------'
+ * source: none |
+ * ------------------------------------'
+ */
+ .bst_magic_slot0 = BOOT_MAGIC_GOOD,
+ .bst_magic_scratch = 0,
+ .bst_copy_done_slot0 = 0x01,
+ .bst_status_source = BOOT_STATUS_SOURCE_NONE,
+ },
+
+ {
+ /* | slot-0 | scratch |
+ * ----------+------------+------------|
+ * magic | Good | Any |
+ * copy-done | 0xff | N/A |
+ * ----------+------------+------------'
+ * source: slot 0 |
+ * ------------------------------------'
+ */
+ .bst_magic_slot0 = BOOT_MAGIC_GOOD,
+ .bst_magic_scratch = 0,
+ .bst_copy_done_slot0 = 0xff,
+ .bst_status_source = BOOT_STATUS_SOURCE_SLOT0,
+ },
+
+ {
+ /* | slot-0 | scratch |
+ * ----------+------------+------------|
+ * magic | Any | Good |
+ * copy-done | Any | N/A |
+ * ----------+------------+------------'
+ * source: scratch |
+ * ------------------------------------'
+ */
+ .bst_magic_slot0 = 0,
+ .bst_magic_scratch = BOOT_MAGIC_GOOD,
+ .bst_copy_done_slot0 = 0,
+ .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
+ },
+
+ {
+ /* | slot-0 | scratch |
+ * ----------+------------+------------|
+ * magic | Unset | Any |
+ * copy-done | 0xff | N/A |
+ * ----------+------------+------------|
+ * source: varies |
+ * ------------------------------------+------------------------------+
+ * This represents one of two cases: |
+ * o No swaps ever (no status to read, so no harm in checking). |
+ * o Mid-revert; status in slot 0. |
+ * -------------------------------------------------------------------'
+ */
+ .bst_magic_slot0 = BOOT_MAGIC_UNSET,
+ .bst_magic_scratch = 0,
+ .bst_copy_done_slot0 = 0xff,
+ .bst_status_source = BOOT_STATUS_SOURCE_SLOT0,
+ },
+};
+
+#define BOOT_STATUS_TABLES_COUNT \
+ (sizeof boot_status_tables / sizeof boot_status_tables[0])
+
+/**
+ * This table indicates the next swap type that should be performed. The first
+ * column contains the current swap type. The second column contains the swap
+ * type that should be effected after the first completes.
+ */
+static const uint8_t boot_swap_trans_table[][2] = {
+ /* From To */
+ { BOOT_SWAP_TYPE_REVERT, BOOT_SWAP_TYPE_NONE },
+ { BOOT_SWAP_TYPE_TEST, BOOT_SWAP_TYPE_REVERT },
+};
+
+#define BOOT_SWAP_TRANS_TABLE_SIZE \
+ (sizeof boot_swap_trans_table / sizeof boot_swap_trans_table[0])
+
+/**
+ * Determines where in flash the most recent boot status is stored. The boot
+ * status is necessary for completing a swap that was interrupted by a boot
+ * loader reset.
+ *
+ * @return A BOOT_STATUS_SOURCE_[...] code indicating where * status should be read from.
+ */
+static int
+boot_status_source(void)
+{
+ const struct boot_status_table *table;
+ struct boot_swap_state state_scratch;
+ struct boot_swap_state state_slot0;
+ struct boot_swap_state state_slot1;
+ int rc;
+ int i;
+
+ rc = boot_read_swap_state_img(0, &state_slot0);
+ assert(rc == 0);
+
+ rc = boot_read_swap_state_img(1, &state_slot1);
+ assert(rc == 0);
+
+ rc = boot_read_swap_state_scratch(&state_scratch);
+ assert(rc == 0);
+
+ for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
+ table = boot_status_tables + i;
+
+ if ((table->bst_magic_slot0 == 0 ||
+ table->bst_magic_slot0 == state_slot0.magic) &&
+ (table->bst_magic_scratch == 0 ||
+ table->bst_magic_scratch == state_scratch.magic) &&
+ (table->bst_copy_done_slot0 == 0 ||
+ table->bst_copy_done_slot0 == state_slot0.copy_done)) {
+
+ return table->bst_status_source;
+ }
+ }
+
+ return BOOT_STATUS_SOURCE_NONE;
+}
+
+/**
+ * Calculates the type of swap that just completed.
+ */
+static int
+boot_previous_swap_type(void)
+{
+ int post_swap_type;
+ int i;
+
+ post_swap_type = boot_swap_type();
+
+ for (i = 0; i < BOOT_SWAP_TRANS_TABLE_SIZE; i++){
+ if (boot_swap_trans_table[i][1] == post_swap_type) {
+ return boot_swap_trans_table[i][0];
+ }
+ }
+
+ /* XXX: Temporary assert. */
+ assert(0);
+
+ return BOOT_SWAP_TYPE_REVERT;
+}
+
+static int
+boot_read_image_header(int slot, struct image_header *out_hdr)
+{
+ const struct flash_area *fap;
+ int area_id;
+ int rc;
+
+ area_id = flash_area_id_from_image_slot(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;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+static int
+boot_read_image_headers(void)
+{
+ int rc;
+ int i;
+
+ for (i = 0; i < BOOT_NUM_SLOTS; i++) {
+ rc = boot_read_image_header(i, &boot_data.imgs[i].hdr);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static uint8_t
+boot_write_sz(void)
+{
+ uint8_t elem_sz;
+ uint8_t align;
+
+ /* Figure out what size to write update status update as. The size depends
+ * on what the minimum write size is for scratch area, active image slot.
+ * We need to use the bigger of those 2 values.
+ */
+ elem_sz = hal_flash_align(boot_data.imgs[0].sectors[0].fa_device_id);
+ align = hal_flash_align(boot_data.scratch_sector.fa_device_id);
+ if (align > elem_sz) {
+ elem_sz = align;
+ }
+
+ return elem_sz;
+}
+
+/**
+ * Determines the sector layout of both image slots and the scratch area.
+ * This information is necessary for calculating the number of bytes to erase
+ * and copy during an image swap. The information collected during this
+ * function is used to populate the boot_data global.
+ */
+static int
+boot_read_sectors(void)
+{
+ const struct flash_area *sector0;
+ const struct flash_area *sector1;
+ const struct flash_area *scratch;
+ int num_sectors_slot0;
+ int num_sectors_slot1;
+ int rc;
+ int i;
+
+ num_sectors_slot0 = BOOT_MAX_IMG_SECTORS;
+ rc = flash_area_to_sectors(FLASH_AREA_IMAGE_0, &num_sectors_slot0,
+ boot_data.imgs[0].sectors);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ num_sectors_slot1 = BOOT_MAX_IMG_SECTORS;
+ rc = flash_area_to_sectors(FLASH_AREA_IMAGE_1, &num_sectors_slot1,
+ boot_data.imgs[1].sectors);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &scratch);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ boot_data.scratch_sector = *scratch;
+ boot_data.write_sz = hal_flash_align(scratch->fa_device_id);
+
+ /* Ensure both image slots have identical sector layouts. */
+ if (num_sectors_slot0 != num_sectors_slot1) {
+ return BOOT_EFLASH;
+ }
+ for (i = 0; i < num_sectors_slot0; i++) {
+ sector0 = boot_data.imgs[0].sectors + i;
+ sector1 = boot_data.imgs[1].sectors + i;
+ if (sector0->fa_size != sector1->fa_size) {
+ return BOOT_EFLASH;
+ }
+ }
+
+ boot_data.num_img_sectors = num_sectors_slot0;
+
+ boot_data.write_sz = boot_write_sz();
+
+ return 0;
+}
+
+static uint32_t
+boot_status_internal_off(int idx, int state, int elem_sz)
+{
+ int idx_sz;
+
+ idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
+
+ return idx * idx_sz + state * elem_sz;
+}
+
+/**
+ * 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
+ * operation.
+ */
+static int
+boot_read_status_bytes(const struct flash_area *fap, struct boot_status *bs)
+{
+ uint32_t off;
+ uint8_t status;
+ int found;
+ int rc;
+ int i;
+
+ off = boot_status_off(fap);
+
+ found = 0;
+ for (i = 0; i < BOOT_STATUS_MAX_ENTRIES; i++) {
+ rc = flash_area_read(fap, off + i * boot_data.write_sz, &status, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (status == 0xff) {
+ if (found) {
+ break;
+ }
+ } else if (!found) {
+ found = 1;
+ }
+ }
+
+ if (found) {
+ i--;
+ bs->idx = i / BOOT_STATUS_STATE_COUNT;
+ bs->state = i % BOOT_STATUS_STATE_COUNT;
+ }
+
+ return 0;
+}
+
+/**
+ * Reads the boot status from the flash. The boot status contains
+ * the current state of an interrupted image copy operation. If the boot
+ * status is not present, or it indicates that previous copy finished,
+ * there is no operation in progress.
+ */
+static int
+boot_read_status(struct boot_status *bs)
+{
+ const struct flash_area *fap;
+ int status_loc;
+ int area_id;
+ int rc;
+
+ memset(bs, 0, sizeof *bs);
+
+ status_loc = boot_status_source();
+ switch (status_loc) {
+ case BOOT_STATUS_SOURCE_NONE:
+ return 0;
+
+ case BOOT_STATUS_SOURCE_SCRATCH:
+ area_id = FLASH_AREA_IMAGE_SCRATCH;
+ break;
+
+ case BOOT_STATUS_SOURCE_SLOT0:
+ area_id = FLASH_AREA_IMAGE_0;
+ break;
+
+ default:
+ assert(0);
+ return BOOT_EBADARGS;
+ }
+
+ rc = flash_area_open(area_id, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = boot_read_status_bytes(fap, bs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 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(struct boot_status *bs)
+{
+ const struct flash_area *fap;
+ uint32_t off;
+ int area_id;
+ int rc;
+
+ if (bs->idx == 0) {
+ /* Write to scratch. */
+ area_id = FLASH_AREA_IMAGE_SCRATCH;
+ } else {
+ /* Write to slot 0. */
+ area_id = FLASH_AREA_IMAGE_0;
+ }
+
+ rc = flash_area_open(area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ off = boot_status_off(fap) +
+ boot_status_internal_off(bs->idx, bs->state, boot_data.write_sz);
+
+ rc = flash_area_write(fap, off, &bs->state, 1);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+/*
+ * Validate image hash/signature in a slot.
+ */
+static int
+boot_image_check(struct image_header *hdr, const struct flash_area *fap)
+{
+ static void *tmpbuf;
+
+ if (!tmpbuf) {
+ tmpbuf = malloc(BOOT_TMPBUF_SZ);
+ if (!tmpbuf) {
+ return BOOT_ENOMEM;
+ }
+ }
+ if (bootutil_img_validate(hdr, fap, tmpbuf, BOOT_TMPBUF_SZ,
+ NULL, 0, NULL)) {
+ return BOOT_EBADIMAGE;
+ }
+ return 0;
+}
+
+static int
+split_image_check(struct image_header *app_hdr,
+ const struct flash_area *app_fap,
+ struct image_header *loader_hdr,
+ const struct flash_area *loader_fap)
+{
+ static void *tmpbuf;
+ uint8_t loader_hash[32];
+
+ if (!tmpbuf) {
+ tmpbuf = malloc(BOOT_TMPBUF_SZ);
+ if (!tmpbuf) {
+ return BOOT_ENOMEM;
+ }
+ }
+
+ if (bootutil_img_validate(loader_hdr, loader_fap, tmpbuf, BOOT_TMPBUF_SZ,
+ NULL, 0, loader_hash)) {
+ return BOOT_EBADIMAGE;
+ }
+
+ if (bootutil_img_validate(app_hdr, app_fap, tmpbuf, BOOT_TMPBUF_SZ,
+ loader_hash, 32, NULL)) {
+ return BOOT_EBADIMAGE;
+ }
+
+ return 0;
+}
+
+static int
+boot_validate_slot1(void)
+{
+ const struct flash_area *fap;
+ int rc;
+
+ if (boot_data.imgs[1].hdr.ih_magic == 0xffffffff ||
+ boot_data.imgs[1].hdr.ih_flags & IMAGE_F_NON_BOOTABLE) {
+
+ /* No bootable image in slot 1; continue booting from slot 0. */
+ return -1;
+ }
+
+ /* Image in slot 1 is invalid. Erase the image and continue booting
+ * from slot 0.
+ */
+ rc = flash_area_open(FLASH_AREA_IMAGE_1, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (boot_data.imgs[1].hdr.ih_magic != IMAGE_MAGIC ||
+ boot_image_check(&boot_data.imgs[1].hdr, fap) != 0) {
+
+ /* Image in slot 1 is invalid. Erase the image and continue booting
+ * from slot 0.
+ */
+ flash_area_erase(fap, 0, fap->fa_size);
+ return -1;
+ }
+
+ flash_area_close(fap);
+
+ /* Image in slot 1 is valid. */
+ return 0;
+}
+
+/**
+ * Determines which swap operation to perform, if any. If it is determined
+ * that a swap operation is required, the image in the second slot is checked
+ * for validity. If the image in the second slot is invalid, it is erased, and
+ * a swap type of "none" is indicated.
+ *
+ * @return The type of swap to perform (BOOT_SWAP_TYPE...)
+ */
+static int
+boot_validated_swap_type(void)
+{
+ int swap_type;
+ int rc;
+
+ swap_type = boot_swap_type();
+ if (swap_type == BOOT_SWAP_TYPE_NONE) {
+ /* Continue using slot 0. */
+ return BOOT_SWAP_TYPE_NONE;
+ }
+
+ /* Boot loader wants to switch to slot 1. Ensure image is valid. */
+ rc = boot_validate_slot1();
+ if (rc != 0) {
+ return BOOT_SWAP_TYPE_FAIL;
+ }
+
+ return swap_type;
+}
+
+/**
+ * Calculates the number of sectors the scratch area can contain. A "last"
+ * source sector is specified because images are copied backwards in flash
+ * (final index to index number 0).
+ *
+ * @param last_sector_idx The index of the last source sector
+ * (inclusive).
+ * @param out_first_sector_idx The index of the first source sector
+ * (inclusive) gets written here.
+ *
+ * @return The number of bytes comprised by the
+ * [first-sector, last-sector] range.
+ */
+static uint32_t
+boot_copy_sz(int last_sector_idx, int *out_first_sector_idx)
+{
+ uint32_t new_sz;
+ uint32_t sz;
+ int i;
+
+ sz = 0;
+
+ for (i = last_sector_idx; i >= 0; i--) {
+ new_sz = sz + boot_data.imgs[0].sectors[i].fa_size;
+ if (new_sz > boot_data.scratch_sector.fa_size) {
+ break;
+ }
+ sz = new_sz;
+ }
+
+ /* i currently refers to a sector that doesn't fit or it is -1 because all
+ * sectors have been processed. In both cases, exclude sector i.
+ */
+ *out_first_sector_idx = i + 1;
+ return sz;
+}
+
+/**
+ * Erases a region of flash.
+ *
+ * @param flash_area_idx The ID of the flash area containing the region
+ * to erase.
+ * @param off The offset within the flash area to start the
+ * erase.
+ * @param sz The number of bytes to erase.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_erase_sector(int flash_area_id, uint32_t off, uint32_t sz)
+{
+ const struct flash_area *fap;
+ int rc;
+
+ rc = flash_area_open(flash_area_id, &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = flash_area_erase(fap, off, sz);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+/**
+ * Copies the contents of one flash region to another. You must erase the
+ * destination region prior to calling this function.
+ *
+ * @param flash_area_id_src The ID of the source flash area.
+ * @param flash_area_id_dst The ID of the destination flash area.
+ * @param off_src The offset within the source flash area to
+ * copy from.
+ * @param off_dst The offset within the destination flash area to
+ * copy to.
+ * @param sz The number of bytes to copy.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_copy_sector(int flash_area_id_src, int flash_area_id_dst,
+ uint32_t off_src, uint32_t off_dst, uint32_t sz)
+{
+ const struct flash_area *fap_src;
+ const struct flash_area *fap_dst;
+ uint32_t bytes_copied;
+ int chunk_sz;
+ int rc;
+
+ static uint8_t buf[1024];
+
+ fap_src = NULL;
+ fap_dst = NULL;
+
+ rc = flash_area_open(flash_area_id_src, &fap_src);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = flash_area_open(flash_area_id_dst, &fap_dst);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ bytes_copied = 0;
+ while (bytes_copied < sz) {
+ if (sz - bytes_copied > sizeof buf) {
+ chunk_sz = sizeof buf;
+ } else {
+ chunk_sz = sz - bytes_copied;
+ }
+
+ rc = flash_area_read(fap_src, off_src + bytes_copied, buf, chunk_sz);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = flash_area_write(fap_dst, off_dst + bytes_copied, buf, chunk_sz);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ bytes_copied += chunk_sz;
+ }
+
+ rc = 0;
+
+done:
+ flash_area_close(fap_src);
+ flash_area_close(fap_dst);
+ return rc;
+}
+
+/**
+ * Swaps the contents of two flash regions within the two image slots.
+ *
+ * @param idx The index of the first sector in the range of
+ * sectors being swapped.
+ * @param sz The number of bytes to swap.
+ * @param bs The current boot status. This struct gets
+ * updated according to the outcome.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_swap_sectors(int idx, uint32_t sz, struct boot_status *bs)
+{
+ uint32_t copy_sz;
+ uint32_t img_off;
+ int rc;
+
+ /* Calculate offset from start of image area. */
+ img_off = boot_data.imgs[0].sectors[idx].fa_off -
+ boot_data.imgs[0].sectors[0].fa_off;
+
+ if (bs->state == 0) {
+ rc = boot_erase_sector(FLASH_AREA_IMAGE_SCRATCH, 0, sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = boot_copy_sector(FLASH_AREA_IMAGE_1, FLASH_AREA_IMAGE_SCRATCH,
+ img_off, 0, sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ bs->state = 1;
+ (void)boot_write_status(bs);
+ }
+ if (bs->state == 1) {
+ rc = boot_erase_sector(FLASH_AREA_IMAGE_1, img_off, sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ copy_sz = sz;
+ if (boot_data.imgs[0].sectors[idx].fa_off + sz >=
+ boot_data.imgs[1].sectors[0].fa_off) {
+
+ /* This is the end of the area. Don't copy the image state into
+ * slot 1.
+ */
+ copy_sz -= boot_trailer_sz(boot_data.write_sz);
+ }
+
+ rc = boot_copy_sector(FLASH_AREA_IMAGE_0, FLASH_AREA_IMAGE_1,
+ img_off, img_off, copy_sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ bs->state = 2;
+ (void)boot_write_status(bs);
+ }
+ if (bs->state == 2) {
+ rc = boot_erase_sector(FLASH_AREA_IMAGE_0, img_off, sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = boot_copy_sector(FLASH_AREA_IMAGE_SCRATCH, FLASH_AREA_IMAGE_0,
+ 0, img_off, sz);
+ if (rc != 0) {
+ return rc;
+ }
+
+ bs->idx++;
+ bs->state = 0;
+ (void)boot_write_status(bs);
+ }
+
+ return 0;
+}
+
+/**
+ * Swaps the two images in flash. If a prior copy operation was interrupted
+ * by a system reset, this function completes that operation.
+ *
+ * @param bs The current boot status. This function reads
+ * this struct to determine if it is resuming
+ * an interrupted swap operation. This
+ * function writes the updated status to this
+ * function on return.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_copy_image(struct boot_status *bs)
+{
+ uint32_t sz;
+ int first_sector_idx;
+ int last_sector_idx;
+ int swap_idx;
+
+ swap_idx = 0;
+ last_sector_idx = boot_data.num_img_sectors - 1;
+ while (last_sector_idx >= 0) {
+ sz = boot_copy_sz(last_sector_idx, &first_sector_idx);
+ if (swap_idx >= bs->idx) {
+ boot_swap_sectors(first_sector_idx, sz, bs);
+ }
+
+ last_sector_idx = first_sector_idx - 1;
+ swap_idx++;
+ }
+
+ return 0;
+}
+
+/**
+ * Marks a test image in slot 0 as fully copied.
+ */
+static int
+boot_finalize_test_swap(void)
+{
+ const struct flash_area *fap;
+ int rc;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = boot_write_copy_done(fap);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Marks a reverted image in slot 0 as confirmed. This is necessary to ensure
+ * the status bytes from the image revert operation don't get processed on a
+ * subsequent boot.
+ */
+static int
+boot_finalize_revert_swap(void)
+{
+ const struct flash_area *fap;
+ struct boot_swap_state state_slot0;
+ int rc;
+
+ rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ rc = boot_read_swap_state(fap, &state_slot0);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (state_slot0.magic == BOOT_MAGIC_UNSET) {
+ rc = boot_write_magic(fap);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ if (state_slot0.copy_done == 0xff) {
+ rc = boot_write_copy_done(fap);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ if (state_slot0.image_ok == 0xff) {
+ rc = boot_write_image_ok(fap);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Prepares the booting process. This function moves images around in flash as
+ * appropriate, and tells you what address to boot from.
+ *
+ * @param rsp On success, indicates how booting should occur.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int
+boot_go(struct boot_rsp *rsp)
+{
+ struct boot_status bs;
+ int swap_type;
+ int slot;
+ int rc;
+
+ /* The array of slot sectors are defined here (as opposed to file scope) so
+ * that they don't get allocated for non-boot-loader apps. This is
+ * necessary because the gcc option "-fdata-sections" doesn't seem to have
+ * any effect for some reason.
+ */
+ static struct flash_area slot0_sectors[BOOT_MAX_IMG_SECTORS];
+ static struct flash_area slot1_sectors[BOOT_MAX_IMG_SECTORS];
+ boot_data.imgs[0].sectors = slot0_sectors;
+ boot_data.imgs[1].sectors = slot1_sectors;
+
+ /* Determine the sector layout of the image slots and scratch area. */
+ rc = boot_read_sectors();
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Attempt to read an image header from each slot. */
+ rc = boot_read_image_headers();
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Determine if we rebooted in the middle of an image swap operation. */
+ rc = boot_read_status(&bs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* If a partial swap was detected, complete it. */
+ if (bs.idx != 0 || bs.state != 0) {
+ rc = boot_copy_image(&bs);
+ assert(rc == 0);
+
+ /* Extrapolate the type of the partial swap. We need this information
+ * to know how to mark the swap complete in flash.
+ */
+ swap_type = boot_previous_swap_type();
+ } else {
+ swap_type = boot_validated_swap_type();
+ switch (swap_type) {
+ case BOOT_SWAP_TYPE_TEST:
+ case BOOT_SWAP_TYPE_REVERT:
+ rc = boot_copy_image(&bs);
+ assert(rc == 0);
+ break;
+ }
+ }
+
+ switch (swap_type) {
+ case BOOT_SWAP_TYPE_NONE:
+ slot = 0;
+ break;
+
+ case BOOT_SWAP_TYPE_TEST:
+ slot = 1;
+ boot_finalize_test_swap();
+ break;
+
+ case BOOT_SWAP_TYPE_REVERT:
+ slot = 1;
+ boot_finalize_revert_swap();
+ break;
+
+ case BOOT_SWAP_TYPE_FAIL:
+ /* The image in slot 1 was invalid and is now erased. Ensure we don't
+ * try to boot into it again on the next reboot. Do this by pretending
+ * we just reverted back to slot 0.
+ */
+ slot = 0;
+ boot_finalize_revert_swap();
+ break;
+
+ default:
+ assert(0);
+ slot = 0;
+ break;
+ }
+
+ /* Always boot from the primary slot. */
+ rsp->br_flash_id = boot_data.imgs[0].sectors[0].fa_device_id;
+ rsp->br_image_addr = boot_data.imgs[0].sectors[0].fa_off;
+ rsp->br_hdr = &boot_data.imgs[slot].hdr;
+
+ return 0;
+}
+
+int
+split_go(int loader_slot, int split_slot, void **entry)
+{
+ const struct flash_area *loader_fap;
+ const struct flash_area *app_fap;
+ struct flash_area *sectors;
+ uint32_t entry_val;
+ int loader_flash_id;
+ int app_flash_id;
+ int rc;
+
+ app_fap = NULL;
+ loader_fap = NULL;
+
+ sectors = malloc(BOOT_MAX_IMG_SECTORS * 2 * sizeof *sectors);
+ if (sectors == NULL) {
+ rc = SPLIT_GO_ERR;
+ goto done;
+ }
+ boot_data.imgs[0].sectors = sectors + 0;
+ boot_data.imgs[1].sectors = sectors + BOOT_MAX_IMG_SECTORS;
+
+ /* Determine the sector layout of the image slots and scratch area. */
+ rc = boot_read_sectors();
+ if (rc != 0) {
+ rc = SPLIT_GO_ERR;
+ goto done;
+ }
+
+ rc = boot_read_image_headers();
+ if (rc != 0) {
+ goto done;
+ }
+
+ app_flash_id = flash_area_id_from_image_slot(split_slot);
+ rc = flash_area_open(app_flash_id, &app_fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ loader_flash_id = flash_area_id_from_image_slot(loader_slot);
+ rc = flash_area_open(loader_flash_id, &loader_fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ /* Don't check the bootable image flag because we could really call a
+ * bootable or non-bootable image. Just validate that the image check
+ * passes which is distinct from the normal check.
+ */
+ rc = split_image_check(&boot_data.imgs[split_slot].hdr,
+ app_fap,
+ &boot_data.imgs[loader_slot].hdr,
+ loader_fap);
+ if (rc != 0) {
+ rc = SPLIT_GO_NON_MATCHING;
+ goto done;
+ }
+
+ entry_val = boot_data.imgs[split_slot].sectors[0].fa_off +
+ boot_data.imgs[split_slot].hdr.ih_hdr_size;
+ *entry = (void*) entry_val;
+ rc = SPLIT_GO_OK;
+
+done:
+ free(sectors);
+ flash_area_close(app_fap);
+ flash_area_close(loader_fap);
+ return rc;
+}
diff --git a/bootutil/syscfg.yml b/bootutil/syscfg.yml
new file mode 100644
index 0000000..2951c03
--- /dev/null
+++ b/bootutil/syscfg.yml
@@ -0,0 +1,27 @@
+# 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.
+#
+
+# Package: boot/bootutil
+
+syscfg.defs:
+ BOOTUTIL_SIGN_RSA:
+ description: 'TBD'
+ value: '0'
+ BOOTUTIL_SIGN_EC:
+ description: 'TBD'
+ value: '0'
diff --git a/bootutil/test/pkg.yml b/bootutil/test/pkg.yml
new file mode 100644
index 0000000..f602e31
--- /dev/null
+++ b/bootutil/test/pkg.yml
@@ -0,0 +1,30 @@
+# 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.
+#
+pkg.name: boot/bootutil/test
+pkg.type: unittest
+pkg.description: "Bootutil unit tests."
+pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - boot/bootutil
+ - test/testutil
+
+pkg.deps.SELFTEST:
+ - sys/console/stub
diff --git a/bootutil/test/src/boot_test.c b/bootutil/test/src/boot_test.c
new file mode 100644
index 0000000..a0e8069
--- /dev/null
+++ b/bootutil/test/src/boot_test.c
@@ -0,0 +1,98 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "syscfg/syscfg.h"
+#include "sysflash/sysflash.h"
+#include "testutil/testutil.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+#include "bootutil/image.h"
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+
+#include "mbedtls/sha256.h"
+
+TEST_CASE_DECL(boot_test_nv_ns_10)
+TEST_CASE_DECL(boot_test_nv_ns_01)
+TEST_CASE_DECL(boot_test_nv_ns_11)
+TEST_CASE_DECL(boot_test_vm_ns_10)
+TEST_CASE_DECL(boot_test_vm_ns_01)
+TEST_CASE_DECL(boot_test_vm_ns_11_a)
+TEST_CASE_DECL(boot_test_vm_ns_11_b)
+TEST_CASE_DECL(boot_test_vm_ns_11_2areas)
+TEST_CASE_DECL(boot_test_nv_bs_10)
+TEST_CASE_DECL(boot_test_nv_bs_11)
+TEST_CASE_DECL(boot_test_nv_bs_11_2areas)
+TEST_CASE_DECL(boot_test_vb_ns_11)
+TEST_CASE_DECL(boot_test_no_hash)
+TEST_CASE_DECL(boot_test_no_flag_has_hash)
+TEST_CASE_DECL(boot_test_invalid_hash)
+TEST_CASE_DECL(boot_test_revert)
+TEST_CASE_DECL(boot_test_revert_continue)
+
+TEST_SUITE(boot_test_main)
+{
+ boot_test_nv_ns_10();
+ boot_test_nv_ns_01();
+ boot_test_nv_ns_11();
+ boot_test_vm_ns_10();
+ boot_test_vm_ns_01();
+ boot_test_vm_ns_11_a();
+ boot_test_vm_ns_11_b();
+ boot_test_vm_ns_11_2areas();
+ boot_test_nv_bs_10();
+ boot_test_nv_bs_11();
+ boot_test_nv_bs_11_2areas();
+ boot_test_vb_ns_11();
+ boot_test_no_hash();
+ boot_test_no_flag_has_hash();
+ boot_test_invalid_hash();
+ boot_test_revert();
+ boot_test_revert_continue();
+}
+
+int
+boot_test_all(void)
+{
+ boot_test_main();
+ return tu_any_failed;
+}
+
+#if MYNEWT_VAL(SELFTEST)
+
+int
+main(int argc, char **argv)
+{
+ ts_config.ts_print_results = 1;
+ tu_parse_args(argc, argv);
+
+ tu_init();
+
+ boot_test_all();
+
+ return tu_any_failed;
+}
+
+#endif
diff --git a/bootutil/test/src/boot_test.h b/bootutil/test/src/boot_test.h
new file mode 100644
index 0000000..b10bec6
--- /dev/null
+++ b/bootutil/test/src/boot_test.h
@@ -0,0 +1,84 @@
+/*
+ * 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 _BOOT_TEST_H
+#define _BOOT_TEST_H
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "syscfg/syscfg.h"
+#include "sysflash/sysflash.h"
+#include "testutil/testutil.h"
+#include "hal/hal_flash.h"
+#include "flash_map/flash_map.h"
+#include "bootutil/image.h"
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+#include "testutil/testutil.h"
+
+#include "mbedtls/sha256.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BOOT_TEST_HEADER_SIZE 0x200
+
+/** Internal flash layout. */
+extern struct flash_area boot_test_area_descs[];
+
+/** Areas representing the beginning of image slots. */
+extern uint8_t boot_test_slot_areas[];
+
+/** Flash offsets of the two image slots. */
+struct boot_test_img_addrs {
+ uint8_t flash_id;
+ uint32_t address;
+};
+extern struct boot_test_img_addrs boot_test_img_addrs[];
+
+#define BOOT_TEST_AREA_IDX_SCRATCH 6
+
+uint8_t boot_test_util_byte_at(int img_msb, uint32_t image_offset);
+void boot_test_util_init_flash(void);
+void boot_test_util_copy_area(int from_area_idx, int to_area_idx);
+void boot_test_util_swap_areas(int area_idx1, int area_idx2);
+void boot_test_util_write_image(const struct image_header *hdr,
+ int slot);
+void boot_test_util_write_hash(const struct image_header *hdr, int slot);
+void boot_test_util_mark_revert(void);
+void boot_test_util_verify_area(const struct flash_area *area_desc,
+ const struct image_header *hdr,
+ uint32_t image_addr, int img_msb);
+void boot_test_util_verify_status_clear(void);
+void boot_test_util_verify_flash(const struct image_header *hdr0,
+ int orig_slot_0,
+ const struct image_header *hdr1,
+ int orig_slot_1);
+void boot_test_util_verify_all(int expected_swap_type,
+ const struct image_header *hdr0,
+ const struct image_header *hdr1);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_BOOT_TEST_H */
diff --git a/bootutil/test/src/boot_test_utils.c b/bootutil/test/src/boot_test_utils.c
new file mode 100644
index 0000000..d882101
--- /dev/null
+++ b/bootutil/test/src/boot_test_utils.c
@@ -0,0 +1,505 @@
+/*
+ * 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 "boot_test.h"
+
+/** Internal flash layout. */
+struct flash_area boot_test_area_descs[] = {
+ [0] = { .fa_off = 0x00020000, .fa_size = 128 * 1024 },
+ [1] = { .fa_off = 0x00040000, .fa_size = 128 * 1024 },
+ [2] = { .fa_off = 0x00060000, .fa_size = 128 * 1024 },
+ [3] = { .fa_off = 0x00080000, .fa_size = 128 * 1024 },
+ [4] = { .fa_off = 0x000a0000, .fa_size = 128 * 1024 },
+ [5] = { .fa_off = 0x000c0000, .fa_size = 128 * 1024 },
+ [6] = { .fa_off = 0x000e0000, .fa_size = 128 * 1024 },
+ [7] = { 0 },
+};
+
+/** Areas representing the beginning of image slots. */
+uint8_t boot_test_slot_areas[] = {
+ 0, 3,
+};
+
+/** Flash offsets of the two image slots. */
+struct boot_test_img_addrs boot_test_img_addrs[] = {
+ { 0, 0x20000 },
+ { 0, 0x80000 },
+};
+
+#define BOOT_TEST_AREA_IDX_SCRATCH 6
+
+uint8_t
+boot_test_util_byte_at(int img_msb, uint32_t image_offset)
+{
+ uint32_t u32;
+ uint8_t *u8p;
+
+ TEST_ASSERT(image_offset < 0x01000000);
+ u32 = image_offset + (img_msb << 24);
+ u8p = (void *)&u32;
+ return u8p[image_offset % 4];
+}
+
+
+void
+boot_test_util_init_flash(void)
+{
+ const struct flash_area *area_desc;
+ int rc;
+
+ rc = hal_flash_init();
+ TEST_ASSERT(rc == 0);
+
+ for (area_desc = boot_test_area_descs;
+ area_desc->fa_size != 0;
+ area_desc++) {
+
+ rc = flash_area_erase(area_desc, 0, area_desc->fa_size);
+ TEST_ASSERT(rc == 0);
+ }
+}
+
+void
+boot_test_util_copy_area(int from_area_idx, int to_area_idx)
+{
+ const struct flash_area *from_area_desc;
+ const struct flash_area *to_area_desc;
+ void *buf;
+ int rc;
+
+ from_area_desc = boot_test_area_descs + from_area_idx;
+ to_area_desc = boot_test_area_descs + to_area_idx;
+
+ TEST_ASSERT(from_area_desc->fa_size == to_area_desc->fa_size);
+
+ buf = malloc(from_area_desc->fa_size);
+ TEST_ASSERT(buf != NULL);
+
+ rc = flash_area_read(from_area_desc, 0, buf,
+ from_area_desc->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ rc = flash_area_erase(to_area_desc,
+ 0,
+ to_area_desc->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ rc = flash_area_write(to_area_desc, 0, buf,
+ to_area_desc->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ free(buf);
+}
+
+static uint32_t
+boot_test_util_area_write_size(int dst_idx, uint32_t off, uint32_t size)
+{
+ const struct flash_area *desc;
+ int64_t diff;
+ uint32_t trailer_start;
+
+ if (dst_idx != BOOT_TEST_AREA_IDX_SCRATCH - 1) {
+ return size;
+ }
+
+ /* Don't include trailer in copy to second slot. */
+ desc = boot_test_area_descs + dst_idx;
+ trailer_start = desc->fa_size - boot_trailer_sz(1);
+ diff = off + size - trailer_start;
+ if (diff > 0) {
+ if (diff > size) {
+ size = 0;
+ } else {
+ size -= diff;
+ }
+ }
+
+ return size;
+}
+
+void
+boot_test_util_swap_areas(int area_idx1, int area_idx2)
+{
+ const struct flash_area *area_desc1;
+ const struct flash_area *area_desc2;
+ uint32_t size;
+ void *buf1;
+ void *buf2;
+ int rc;
+
+ area_desc1 = boot_test_area_descs + area_idx1;
+ area_desc2 = boot_test_area_descs + area_idx2;
+
+ TEST_ASSERT(area_desc1->fa_size == area_desc2->fa_size);
+
+ buf1 = malloc(area_desc1->fa_size);
+ TEST_ASSERT(buf1 != NULL);
+
+ buf2 = malloc(area_desc2->fa_size);
+ TEST_ASSERT(buf2 != NULL);
+
+ rc = flash_area_read(area_desc1, 0, buf1, area_desc1->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ rc = flash_area_read(area_desc2, 0, buf2, area_desc2->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ rc = flash_area_erase(area_desc1, 0, area_desc1->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ rc = flash_area_erase(area_desc2, 0, area_desc2->fa_size);
+ TEST_ASSERT(rc == 0);
+
+ size = boot_test_util_area_write_size(area_idx1, 0, area_desc1->fa_size);
+ rc = flash_area_write(area_desc1, 0, buf2, size);
+ TEST_ASSERT(rc == 0);
+
+ size = boot_test_util_area_write_size(area_idx2, 0, area_desc2->fa_size);
+ rc = flash_area_write(area_desc2, 0, buf1, size);
+ TEST_ASSERT(rc == 0);
+
+ free(buf1);
+ free(buf2);
+}
+
+void
+boot_test_util_write_image(const struct image_header *hdr, int slot)
+{
+ uint32_t image_off;
+ uint32_t off;
+ uint8_t flash_id;
+ uint8_t buf[256];
+ int chunk_sz;
+ int rc;
+ int i;
+
+ TEST_ASSERT(slot == 0 || slot == 1);
+
+ flash_id = boot_test_img_addrs[slot].flash_id;
+ off = boot_test_img_addrs[slot].address;
+
+ rc = hal_flash_write(flash_id, off, hdr, sizeof *hdr);
+ TEST_ASSERT(rc == 0);
+
+ off += hdr->ih_hdr_size;
+
+ image_off = 0;
+ while (image_off < hdr->ih_img_size) {
+ if (hdr->ih_img_size - image_off > sizeof buf) {
+ chunk_sz = sizeof buf;
+ } else {
+ chunk_sz = hdr->ih_img_size - image_off;
+ }
+
+ for (i = 0; i < chunk_sz; i++) {
+ buf[i] = boot_test_util_byte_at(slot, image_off + i);
+ }
+
+ rc = hal_flash_write(flash_id, off + image_off, buf, chunk_sz);
+ TEST_ASSERT(rc == 0);
+
+ image_off += chunk_sz;
+ }
+}
+
+
+void
+boot_test_util_write_hash(const struct image_header *hdr, int slot)
+{
+ uint8_t tmpdata[1024];
+ uint8_t hash[32];
+ int rc;
+ uint32_t off;
+ uint32_t blk_sz;
+ uint32_t sz;
+ mbedtls_sha256_context ctx;
+ uint8_t flash_id;
+ uint32_t addr;
+ struct image_tlv tlv;
+
+ mbedtls_sha256_init(&ctx);
+ mbedtls_sha256_starts(&ctx, 0);
+
+ flash_id = boot_test_img_addrs[slot].flash_id;
+ addr = boot_test_img_addrs[slot].address;
+
+ sz = hdr->ih_hdr_size + hdr->ih_img_size;
+ for (off = 0; off < sz; off += blk_sz) {
+ blk_sz = sz - off;
+ if (blk_sz > sizeof(tmpdata)) {
+ blk_sz = sizeof(tmpdata);
+ }
+ rc = hal_flash_read(flash_id, addr + off, tmpdata, blk_sz);
+ TEST_ASSERT(rc == 0);
+ mbedtls_sha256_update(&ctx, tmpdata, blk_sz);
+ }
+ mbedtls_sha256_finish(&ctx, hash);
+
+ tlv.it_type = IMAGE_TLV_SHA256;
+ tlv._pad = 0;
+ tlv.it_len = sizeof(hash);
+
+ rc = hal_flash_write(flash_id, addr + off, &tlv, sizeof(tlv));
+ TEST_ASSERT(rc == 0);
+ off += sizeof(tlv);
+ rc = hal_flash_write(flash_id, addr + off, hash, sizeof(hash));
+ TEST_ASSERT(rc == 0);
+}
+
+static void
+boot_test_util_write_swap_state(int flash_area_id,
+ const struct boot_swap_state *state)
+{
+ const struct flash_area *fap;
+ int rc;
+
+ rc = flash_area_open(flash_area_id, &fap);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ switch (state->magic) {
+ case 0:
+ break;
+
+ case BOOT_MAGIC_GOOD:
+ rc = boot_write_magic(fap);
+ TEST_ASSERT_FATAL(rc == 0);
+ break;
+
+ default:
+ TEST_ASSERT_FATAL(0);
+ break;
+ }
+
+ if (state->copy_done != 0xff) {
+ rc = boot_write_copy_done(fap);
+ TEST_ASSERT_FATAL(rc == 0);
+ }
+
+ if (state->image_ok != 0xff) {
+ rc = boot_write_image_ok(fap);
+ TEST_ASSERT_FATAL(rc == 0);
+ }
+}
+
+void
+boot_test_util_mark_revert(void)
+{
+ struct boot_swap_state state_slot0 = {
+ .magic = BOOT_MAGIC_GOOD,
+ .copy_done = 0x01,
+ .image_ok = 0xff,
+ };
+
+ boot_test_util_write_swap_state(FLASH_AREA_IMAGE_0, &state_slot0);
+}
+
+void
+boot_test_util_verify_area(const struct flash_area *area_desc,
+ const struct image_header *hdr,
+ uint32_t image_addr, int img_msb)
+{
+ struct image_header temp_hdr;
+ uint32_t area_end;
+ uint32_t img_size;
+ uint32_t img_off;
+ uint32_t img_end;
+ uint32_t addr;
+ uint8_t buf[256];
+ int rem_area;
+ int past_image;
+ int chunk_sz;
+ int rem_img;
+ int rc;
+ int i;
+
+ addr = area_desc->fa_off;
+
+ if (hdr != NULL) {
+ img_size = hdr->ih_img_size;
+
+ if (addr == image_addr) {
+ rc = hal_flash_read(area_desc->fa_device_id, image_addr,
+ &temp_hdr, sizeof temp_hdr);
+ TEST_ASSERT(rc == 0);
+ TEST_ASSERT(memcmp(&temp_hdr, hdr, sizeof *hdr) == 0);
+
+ addr += hdr->ih_hdr_size;
+ }
+ } else {
+ img_size = 0;
+ }
+
+ area_end = area_desc->fa_off + area_desc->fa_size;
+ img_end = image_addr + img_size;
+ past_image = addr >= img_end;
+
+ while (addr < area_end) {
+ rem_area = area_end - addr;
+ rem_img = img_end - addr;
+
+ if (hdr != NULL) {
+ img_off = addr - image_addr - hdr->ih_hdr_size;
+ } else {
+ img_off = 0;
+ }
+
+ if (rem_area > sizeof buf) {
+ chunk_sz = sizeof buf;
+ } else {
+ chunk_sz = rem_area;
+ }
+
+ rc = hal_flash_read(area_desc->fa_device_id, addr, buf, chunk_sz);
+ TEST_ASSERT(rc == 0);
+
+ for (i = 0; i < chunk_sz; i++) {
+ if (rem_img > 0) {
+ TEST_ASSERT(buf[i] == boot_test_util_byte_at(img_msb,
+ img_off + i));
+ } else if (past_image) {
+#if 0
+ TEST_ASSERT(buf[i] == 0xff);
+#endif
+ }
+ }
+
+ addr += chunk_sz;
+ }
+}
+
+
+void
+boot_test_util_verify_status_clear(void)
+{
+ struct boot_swap_state state_slot0;
+ int rc;
+
+ rc = boot_read_swap_state_img(0, &state_slot0);
+ assert(rc == 0);
+
+ TEST_ASSERT(state_slot0.magic != BOOT_MAGIC_UNSET ||
+ state_slot0.copy_done != 0);
+}
+
+
+void
+boot_test_util_verify_flash(const struct image_header *hdr0, int orig_slot_0,
+ const struct image_header *hdr1, int orig_slot_1)
+{
+ const struct flash_area *area_desc;
+ int area_idx;
+
+ area_idx = 0;
+
+ while (1) {
+ area_desc = boot_test_area_descs + area_idx;
+ if (area_desc->fa_off == boot_test_img_addrs[1].address &&
+ area_desc->fa_device_id == boot_test_img_addrs[1].flash_id) {
+ break;
+ }
+
+ boot_test_util_verify_area(area_desc, hdr0,
+ boot_test_img_addrs[0].address, orig_slot_0);
+ area_idx++;
+ }
+
+ while (1) {
+ if (area_idx == BOOT_TEST_AREA_IDX_SCRATCH) {
+ break;
+ }
+
+ area_desc = boot_test_area_descs + area_idx;
+ boot_test_util_verify_area(area_desc, hdr1,
+ boot_test_img_addrs[1].address, orig_slot_1);
+ area_idx++;
+ }
+}
+
+void
+boot_test_util_verify_all(int expected_swap_type,
+ const struct image_header *hdr0,
+ const struct image_header *hdr1)
+{
+ const struct image_header *slot0hdr;
+ const struct image_header *slot1hdr;
+ struct boot_rsp rsp;
+ int orig_slot_0;
+ int orig_slot_1;
+ int num_swaps;
+ int rc;
+ int i;
+
+ TEST_ASSERT_FATAL(hdr0 != NULL || hdr1 != NULL);
+
+ num_swaps = 0;
+ for (i = 0; i < 3; i++) {
+ rc = boot_go(&rsp);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ if (expected_swap_type != BOOT_SWAP_TYPE_NONE) {
+ num_swaps++;
+ }
+
+ if (num_swaps % 2 == 0) {
+ if (hdr0 != NULL) {
+ slot0hdr = hdr0;
+ slot1hdr = hdr1;
+ } else {
+ slot0hdr = hdr1;
+ slot1hdr = hdr0;
+ }
+ orig_slot_0 = 0;
+ orig_slot_1 = 1;
+ } else {
+ if (hdr1 != NULL) {
+ slot0hdr = hdr1;
+ slot1hdr = hdr0;
+ } else {
+ slot0hdr = hdr0;
+ slot1hdr = hdr1;
+ }
+ orig_slot_0 = 1;
+ orig_slot_1 = 0;
+ }
+
+ TEST_ASSERT(memcmp(rsp.br_hdr, slot0hdr, sizeof *slot0hdr) == 0);
+ TEST_ASSERT(rsp.br_flash_id == boot_test_img_addrs[0].flash_id);
+ TEST_ASSERT(rsp.br_image_addr == boot_test_img_addrs[0].address);
+
+ boot_test_util_verify_flash(slot0hdr, orig_slot_0,
+ slot1hdr, orig_slot_1);
+ boot_test_util_verify_status_clear();
+
+ if (expected_swap_type != BOOT_SWAP_TYPE_NONE) {
+ switch (expected_swap_type) {
+ case BOOT_SWAP_TYPE_TEST:
+ expected_swap_type = BOOT_SWAP_TYPE_REVERT;
+ break;
+
+ case BOOT_SWAP_TYPE_REVERT:
+ expected_swap_type = BOOT_SWAP_TYPE_NONE;
+ break;
+
+ default:
+ TEST_ASSERT_FATAL(0);
+ break;
+ }
+ }
+ }
+}
diff --git a/bootutil/test/src/testcases/boot_test_invalid_hash.c b/bootutil/test/src/testcases/boot_test_invalid_hash.c
new file mode 100644
index 0000000..515ab45
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_invalid_hash.c
@@ -0,0 +1,59 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_invalid_hash)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = 0,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ struct image_tlv tlv = {
+ .it_type = IMAGE_TLV_SHA256,
+ .it_len = 32
+ };
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ rc = hal_flash_write(boot_test_img_addrs[1].flash_id,
+ boot_test_img_addrs[1].address + hdr1.ih_hdr_size + hdr1.ih_img_size,
+ &tlv, sizeof(tlv));
+ TEST_ASSERT(rc == 0);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c b/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c
new file mode 100644
index 0000000..61c8f40
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_no_flag_has_hash.c
@@ -0,0 +1,52 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_no_flag_has_hash)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = 0,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_no_hash.c b/bootutil/test/src/testcases/boot_test_no_hash.c
new file mode 100644
index 0000000..639a1fb
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_no_hash.c
@@ -0,0 +1,51 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_no_hash)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 0,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = 0,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_bs_10.c b/bootutil/test/src/testcases/boot_test_nv_bs_10.c
new file mode 100644
index 0000000..1d8d50c
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_bs_10.c
@@ -0,0 +1,39 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_bs_10)
+{
+ struct image_header hdr = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr, 0);
+ boot_test_util_write_hash(&hdr, 0);
+ boot_test_util_swap_areas(boot_test_slot_areas[1],
+ BOOT_TEST_AREA_IDX_SCRATCH);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_bs_11.c b/bootutil/test/src/testcases/boot_test_nv_bs_11.c
new file mode 100644
index 0000000..efa967e
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_bs_11.c
@@ -0,0 +1,59 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_bs_11)
+{
+ struct boot_status status;
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 17 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 1, 5, 5 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+ rc = boot_set_pending();
+ boot_test_util_copy_area(5, BOOT_TEST_AREA_IDX_SCRATCH);
+
+ status.idx = 0;
+ status.state = 1;
+
+ rc = boot_write_status(&status);
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c b/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c
new file mode 100644
index 0000000..eec22f8
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_bs_11_2areas.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_bs_11_2areas)
+{
+ struct boot_status status;
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 150 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 190 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+ rc = boot_set_pending();
+ TEST_ASSERT_FATAL(rc == 0);
+
+ boot_test_util_swap_areas(2, 5);
+
+ status.idx = 1;
+ status.state = 0;
+
+ rc = boot_write_status(&status);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_ns_01.c b/bootutil/test/src/testcases/boot_test_nv_ns_01.c
new file mode 100644
index 0000000..0481ef0
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_ns_01.c
@@ -0,0 +1,39 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_ns_01)
+{
+ struct image_header hdr = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 10 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr, 1);
+ boot_test_util_write_hash(&hdr, 1);
+
+ boot_set_pending();
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, NULL, &hdr);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_ns_10.c b/bootutil/test/src/testcases/boot_test_nv_ns_10.c
new file mode 100644
index 0000000..429416e
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_ns_10.c
@@ -0,0 +1,37 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_ns_10)
+{
+ struct image_header hdr = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr, 0);
+ boot_test_util_write_hash(&hdr, 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_nv_ns_11.c b/bootutil/test/src/testcases/boot_test_nv_ns_11.c
new file mode 100644
index 0000000..971a2cf
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_nv_ns_11.c
@@ -0,0 +1,48 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_nv_ns_11)
+{
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_revert.c b/bootutil/test/src/testcases/boot_test_revert.c
new file mode 100644
index 0000000..4b78fb6
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_revert.c
@@ -0,0 +1,50 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_revert)
+{
+ struct image_header hdr0 = { .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ /* Indicate that the image in slot 0 is being tested. */
+ boot_test_util_mark_revert();
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_revert_continue.c b/bootutil/test/src/testcases/boot_test_revert_continue.c
new file mode 100644
index 0000000..9982646
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_revert_continue.c
@@ -0,0 +1,62 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_revert_continue)
+{
+ struct boot_status status;
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ /* Indicate that the image in slot 0 is being tested. */
+ boot_test_util_mark_revert();
+
+ boot_test_util_swap_areas(2, 5);
+
+ status.idx = 1;
+ status.state = 0;
+
+ rc = boot_write_status(&status);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vb_ns_11.c b/bootutil/test/src/testcases/boot_test_vb_ns_11.c
new file mode 100644
index 0000000..c6f7b93
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vb_ns_11.c
@@ -0,0 +1,53 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vb_ns_11)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vm_ns_01.c b/bootutil/test/src/testcases/boot_test_vm_ns_01.c
new file mode 100644
index 0000000..3c89033
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vm_ns_01.c
@@ -0,0 +1,42 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vm_ns_01)
+{
+ int rc;
+
+ struct image_header hdr = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 10 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr, 1);
+ boot_test_util_write_hash(&hdr, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_REVERT, NULL, &hdr);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vm_ns_10.c b/bootutil/test/src/testcases/boot_test_vm_ns_10.c
new file mode 100644
index 0000000..d6d217e
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vm_ns_10.c
@@ -0,0 +1,37 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vm_ns_10)
+{
+ struct image_header hdr = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 12 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 2, 3, 4 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr, 0);
+ boot_test_util_write_hash(&hdr, 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr, NULL);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c b/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c
new file mode 100644
index 0000000..490a634
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vm_ns_11_2areas.c
@@ -0,0 +1,53 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vm_ns_11_2areas)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 196 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vm_ns_11_a.c b/bootutil/test/src/testcases/boot_test_vm_ns_11_a.c
new file mode 100644
index 0000000..433be25
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vm_ns_11_a.c
@@ -0,0 +1,48 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vm_ns_11_a)
+{
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_NONE, &hdr0, &hdr1);
+}
diff --git a/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c b/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c
new file mode 100644
index 0000000..914242b
--- /dev/null
+++ b/bootutil/test/src/testcases/boot_test_vm_ns_11_b.c
@@ -0,0 +1,53 @@
+/*
+ * 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 "boot_test.h"
+
+TEST_CASE(boot_test_vm_ns_11_b)
+{
+ int rc;
+
+ struct image_header hdr0 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 5 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 0, 5, 21, 432 },
+ };
+
+ struct image_header hdr1 = {
+ .ih_magic = IMAGE_MAGIC,
+ .ih_tlv_size = 4 + 32,
+ .ih_hdr_size = BOOT_TEST_HEADER_SIZE,
+ .ih_img_size = 32 * 1024,
+ .ih_flags = IMAGE_F_SHA256,
+ .ih_ver = { 1, 2, 3, 432 },
+ };
+
+ boot_test_util_init_flash();
+ boot_test_util_write_image(&hdr0, 0);
+ boot_test_util_write_hash(&hdr0, 0);
+ boot_test_util_write_image(&hdr1, 1);
+ boot_test_util_write_hash(&hdr1, 1);
+
+ rc = boot_set_pending();
+ TEST_ASSERT(rc == 0);
+
+ boot_test_util_verify_all(BOOT_SWAP_TYPE_TEST, &hdr0, &hdr1);
+}