boot: Add boot status record to shared data area

Implement the functions declared in boot_record.h and add
each BOOT_RECORD TLV's CBOR encoded binary data (one TLV entry per
image) to a shared data area between the bootloader and the runtime SW.
These data units are stored in a TLV format (in the shared area too) and
contain certain attributes of the given image / SW component such as:
- SW type (role of the software component)
- SW version
- Signer ID (identifies the signing authority)
- Measurement value (hash of the image)
- Measurement type (algorithm used to calculate the measurement value)

Preserving all these image attributes from the boot stage for use by
later runtime services is known as a measured boot. The list of the
shared attributes is based on the recommendations of Arm's Platform
Security Architecture (PSA).
The main purpose of this patch is to create the prerequisites of an
attestation service by providing these measurements.

The boot_record.c and boot_status.h (originally tfm_boot_status.h) files
were copied (with modifications) from the Trusted Firmware-M project
(https://www.trustedfirmware.org/about/).
Hash of the source commit: 08d5572b4bcee306d8cf709c2200359a22d5b72c.

Change-Id: I37a8e7b10d5bf80a581651ffaf65b3cba45eaff2
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/boot/bootutil/include/bootutil/boot_status.h b/boot/bootutil/include/bootutil/boot_status.h
new file mode 100644
index 0000000..ecf4683
--- /dev/null
+++ b/boot/bootutil/include/bootutil/boot_status.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018-2020 Arm Limited
+ *
+ * Licensed 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_STATUS_H__
+#define __BOOT_STATUS_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The shared data between boot loader and runtime SW is TLV encoded. The
+ * shared data is stored in a well known location in memory and this is a
+ * contract between boot loader and runtime SW.
+ *
+ * The structure of shared data must be the following:
+ *  - At the beginning there must be a header: struct shared_data_tlv_header
+ *    This contains a magic number and a size field which covers the entire
+ *    size of the shared data area including this header.
+ *  - After the header there come the entries which are composed from an entry
+ *    header structure: struct shared_data_tlv_entry and the data. In the entry
+ *    header is a type field (tly_type) which identify the consumer of the
+ *    entry in the runtime SW and specify the subtype of that data item. There
+ *    is a size field (tlv_len) which covers the size of the entry header and
+ *    the data. After this structure comes the actual data.
+ *  - Arbitrary number and size of data entry can be in the shared memory area.
+ *
+ * This table gives of overview about the tlv_type field in the entry header.
+ * The tlv_type always composed from a major and minor number. Major number
+ * identifies the addressee in runtime SW, who should process the data entry.
+ * Minor number used to encode more info about the data entry. The actual
+ * definition of minor number could change per major number.
+ *
+ * In case of boot status data, which can be processed by an attestation
+ * service the minor number is split further to two part: sw_module and claim.
+ * The sw_module identifies the SW component in the system which the data item
+ * belongs to and the claim part identifies the exact type of the data.
+ *
+ * |---------------------------------------|
+ * |            tlv_type (16)              |
+ * |---------------------------------------|
+ * | tlv_major(4)|      tlv_minor(12)      |
+ * |---------------------------------------|
+ * | MAJOR_IAS   | sw_module(6) | claim(6) |
+ * |---------------------------------------|
+ */
+
+/* General macros to handle TLV type */
+#define MAJOR_MASK 0xF     /* 4  bit */
+#define MAJOR_POS  12      /* 12 bit */
+#define MINOR_MASK 0xFFF   /* 12 bit */
+
+#define SET_TLV_TYPE(major, minor) \
+        ((((major) & MAJOR_MASK) << MAJOR_POS) | ((minor) & MINOR_MASK))
+#define GET_MAJOR(tlv_type) ((tlv_type) >> MAJOR_POS)
+#define GET_MINOR(tlv_type) ((tlv_type) &  MINOR_MASK)
+
+/* Magic value which marks the beginning of shared data area in memory */
+#define SHARED_DATA_TLV_INFO_MAGIC    0x2016
+
+/* Initial attestation specific macros */
+
+/**
+ * Major numbers (4 bit) to identify the
+ * consumer of shared data in runtime SW.
+ */
+#define TLV_MAJOR_IAS      0x1
+
+/* Initial attestation: Claim per SW components / SW modules */
+/* Bits: 0-2 */
+#define SW_VERSION       0x00
+#define SW_SIGNER_ID     0x01
+/* Reserved              0x02 */
+#define SW_TYPE          0x03
+/* Bits: 3-5 */
+#define SW_MEASURE_VALUE 0x08
+#define SW_MEASURE_TYPE  0x09
+#define SW_BOOT_RECORD   0x3F
+
+#define MODULE_POS 6               /* 6 bit */
+#define CLAIM_MASK 0x3F            /* 6 bit */
+#define MEASUREMENT_CLAIM_POS 3    /* 3 bit */
+
+#define GET_IAS_MODULE(tlv_type) (GET_MINOR(tlv_type) >> MODULE_POS)
+#define GET_IAS_CLAIM(tlv_type)  (GET_MINOR(tlv_type)  & CLAIM_MASK)
+#define SET_IAS_MINOR(sw_module, claim) (((sw_module) << 6) | (claim))
+
+/**
+ * Shared data TLV header.  All fields in little endian.
+ *
+ *    -----------------------------------
+ *    | tlv_magic(16) | tlv_tot_len(16) |
+ *    -----------------------------------
+ */
+struct shared_data_tlv_header {
+    uint16_t tlv_magic;
+    uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */
+};
+
+#define SHARED_DATA_HEADER_SIZE sizeof(struct shared_data_tlv_header)
+
+/**
+ * Shared data TLV entry header format. All fields in little endian.
+ *
+ *    -------------------------------
+ *    | tlv_type(16) |  tlv_len(16) |
+ *    -------------------------------
+ *    |         Raw data            |
+ *    -------------------------------
+ */
+struct shared_data_tlv_entry {
+    uint16_t tlv_type;
+    uint16_t tlv_len; /* size of single TLV entry (including this header). */
+};
+
+#define SHARED_DATA_ENTRY_HEADER_SIZE sizeof(struct shared_data_tlv_entry)
+#define SHARED_DATA_ENTRY_SIZE(size) (size + SHARED_DATA_ENTRY_HEADER_SIZE)
+
+/* Structure to store the boot data for the runtime SW. */
+struct shared_boot_data {
+    struct shared_data_tlv_header header;
+    uint8_t data[];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOOT_STATUS_H__ */
diff --git a/boot/bootutil/src/boot_record.c b/boot/bootutil/src/boot_record.c
new file mode 100644
index 0000000..655ee7e
--- /dev/null
+++ b/boot/bootutil/src/boot_record.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2018-2020 Arm Limited
+ *
+ * Licensed 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 <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "mcuboot_config/mcuboot_config.h"
+
+#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING)
+#include "bootutil/boot_record.h"
+#include "bootutil/boot_status.h"
+#include "bootutil_priv.h"
+#include "bootutil/image.h"
+#include "flash_map_backend/flash_map_backend.h"
+
+/* Error codes for using the shared memory area. */
+#define SHARED_MEMORY_OK            (0)
+#define SHARED_MEMORY_OVERFLOW      (1)
+#define SHARED_MEMORY_OVERWRITE     (2)
+#define SHARED_MEMORY_GEN_ERROR     (3)
+
+/**
+ * @var shared_memory_init_done
+ *
+ * @brief Indicates whether shared memory area was already initialized.
+ *
+ */
+static bool shared_memory_init_done;
+
+/**
+ * @brief Add a data item to the shared data area between bootloader and
+ *        runtime SW
+ *
+ * @param[in] major_type  TLV major type, identify consumer
+ * @param[in] minor_type  TLV minor type, identify TLV type
+ * @param[in] size        length of added data
+ * @param[in] data        pointer to data
+ *
+ * @return                0 on success; nonzero on failure.
+ */
+static int
+boot_add_data_to_shared_area(uint8_t        major_type,
+                             uint16_t       minor_type,
+                             size_t         size,
+                             const uint8_t *data)
+{
+    struct shared_data_tlv_entry tlv_entry = {0};
+    struct shared_boot_data *boot_data;
+    uint16_t boot_data_size;
+    uintptr_t tlv_end, offset;
+
+    boot_data = (struct shared_boot_data *)MCUBOOT_SHARED_DATA_BASE;
+
+    /* Check whether first time to call this function. If does then initialise
+     * shared data area.
+     */
+    if (!shared_memory_init_done) {
+        memset((void *)MCUBOOT_SHARED_DATA_BASE, 0, MCUBOOT_SHARED_DATA_SIZE);
+        boot_data->header.tlv_magic   = SHARED_DATA_TLV_INFO_MAGIC;
+        boot_data->header.tlv_tot_len = SHARED_DATA_HEADER_SIZE;
+        shared_memory_init_done = true;
+    }
+
+    /* Check whether TLV entry is already added.
+     * Get the boundaries of TLV section
+     */
+    tlv_end = MCUBOOT_SHARED_DATA_BASE + boot_data->header.tlv_tot_len;
+    offset  = MCUBOOT_SHARED_DATA_BASE + SHARED_DATA_HEADER_SIZE;
+
+    /* Iterates over the TLV section looks for the same entry if found then
+     * returns with error: SHARED_MEMORY_OVERWRITE
+     */
+    for (; offset < tlv_end; offset += tlv_entry.tlv_len) {
+        /* Create local copy to avoid unaligned access */
+        memcpy(&tlv_entry, (const void *)offset, SHARED_DATA_ENTRY_HEADER_SIZE);
+        if (GET_MAJOR(tlv_entry.tlv_type) == major_type &&
+            GET_MINOR(tlv_entry.tlv_type) == minor_type) {
+            return SHARED_MEMORY_OVERWRITE;
+        }
+    }
+
+    /* Add TLV entry */
+    tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
+    tlv_entry.tlv_len  = SHARED_DATA_ENTRY_SIZE(size);
+
+    if (!boot_u16_safe_add(&boot_data_size, boot_data->header.tlv_tot_len,
+                           tlv_entry.tlv_len)) {
+        return SHARED_MEMORY_GEN_ERROR;
+    }
+
+    /* Verify overflow of shared area */
+    if (boot_data_size > MCUBOOT_SHARED_DATA_SIZE) {
+        return SHARED_MEMORY_OVERFLOW;
+    }
+
+    offset = tlv_end;
+    memcpy((void *)offset, &tlv_entry, SHARED_DATA_ENTRY_HEADER_SIZE);
+
+    offset += SHARED_DATA_ENTRY_HEADER_SIZE;
+    memcpy((void *)offset, data, size);
+
+    boot_data->header.tlv_tot_len += tlv_entry.tlv_len;
+
+    return SHARED_MEMORY_OK;
+}
+#endif /* MCUBOOT_MEASURED_BOOT OR MCUBOOT_DATA_SHARING */
+
+#ifdef MCUBOOT_MEASURED_BOOT
+/* See in boot_record.h */
+int
+boot_save_boot_status(uint8_t sw_module,
+                      const struct image_header *hdr,
+                      const struct flash_area *fap)
+{
+
+    struct image_tlv_iter it;
+    uint32_t offset;
+    uint16_t len;
+    uint16_t type;
+    uint16_t ias_minor;
+    size_t record_len = 0;
+    uint8_t image_hash[32]; /* SHA256 - 32 Bytes */
+    uint8_t buf[MAX_BOOT_RECORD_SZ];
+    bool boot_record_found = false;
+    bool hash_found = false;
+    int rc;
+
+    /* Manifest data is concatenated to the end of the image.
+     * It is encoded in TLV format.
+     */
+
+    rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
+    if (rc) {
+        return -1;
+    }
+
+    /* Traverse through the TLV area to find the boot record
+     * and image hash TLVs.
+     */
+    while (true) {
+        rc = bootutil_tlv_iter_next(&it, &offset, &len, &type);
+        if (rc < 0) {
+            return -1;
+        } else if (rc > 0) {
+            break;
+        }
+
+        if (type == IMAGE_TLV_BOOT_RECORD) {
+            if (len > sizeof(buf)) {
+                return -1;
+            }
+            rc = flash_area_read(fap, offset, buf, len);
+            if (rc) {
+                return -1;
+            }
+
+            record_len = len;
+            boot_record_found = true;
+
+        } else if (type == IMAGE_TLV_SHA256) {
+            /* Get the image's hash value from the manifest section. */
+            if (len > sizeof(image_hash)) {
+                return -1;
+            }
+            rc = flash_area_read(fap, offset, image_hash, len);
+            if (rc) {
+                return -1;
+            }
+
+            hash_found = true;
+
+            /* The boot record TLV is part of the protected TLV area which is
+             * located before the other parts of the TLV area (including the
+             * image hash) so at this point it is okay to break the loop
+             * as the boot record TLV should have already been found.
+             */
+            break;
+        }
+    }
+
+
+    if (!boot_record_found || !hash_found) {
+        return -1;
+    }
+
+    /* Update the measurement value (hash of the image) data item in the
+     * boot record. It is always the last item in the structure to make
+     * it easy to calculate its position.
+     * The image hash is computed over the image header, the image itself and
+     * the protected TLV area (which should already include the image hash as
+     * part of the boot record TLV). For this reason this field has been
+     * filled with zeros during the image signing process.
+     */
+    offset = record_len - sizeof(image_hash);
+    /* The size of 'buf' has already been checked when
+     * the BOOT_RECORD TLV was read, it won't overflow.
+     */
+    memcpy(buf + offset, image_hash, sizeof(image_hash));
+
+    /* Add the CBOR encoded boot record to the shared data area. */
+    ias_minor = SET_IAS_MINOR(sw_module, SW_BOOT_RECORD);
+    rc = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
+                                      ias_minor,
+                                      record_len,
+                                      buf);
+    if (rc != SHARED_MEMORY_OK) {
+        return rc;
+    }
+
+    return 0;
+}
+#endif /* MCUBOOT_MEASURED_BOOT */