You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by je...@apache.org on 2021/04/22 07:49:29 UTC

[mynewt-core] 02/05: mcu/nrf5340: Add virtual flash for net core

This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 208050779d57a3737e949614d4ff5b3753f4e167
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Fri Apr 9 11:52:24 2021 +0200

    mcu/nrf5340: Add virtual flash for net core
    
    Idea behind virtual flash is that net core bootloader
    can access image embedded inside app core image and
    used it just like the image was put in a normal slot.
    One image (with embedded image) is a way to make it
    easier to manage upgrades just one image to use instead
    of two.
    
    For this to work vflash uses IPC GPMEM registers that application
    side IPC sets up with image size and location.
    Then it just reads data from application flash and present them
    to bootloader when needed.
    Since embedded image is not stored in normal slot writes and erases are
    not effective.
    Additionally end of the slot is simulated so MCUBOOT can see that
    image is there to be used.
    
    For this to work MCUBOOT build for network core needs to have:
        BOOTUTIL_OVERWRITE_ONLY: 1
    
    This driver also has some workaround needed for optimization.
    It checks if current image in slot 1 has same hash as image present
    in virtual slot 2 if so it make mcuboot to believe that slot is empty
    without actually touching data in application flash.
    This would not be needed if application starting on app core was aware that
    it is running from just swapped image.
    Without this workaround network image would be upgraded at each boot.
---
 hw/mcu/nordic/nrf5340_net/src/hal_vflash.c | 253 +++++++++++++++++++++++++++++
 1 file changed, 253 insertions(+)

diff --git a/hw/mcu/nordic/nrf5340_net/src/hal_vflash.c b/hw/mcu/nordic/nrf5340_net/src/hal_vflash.c
new file mode 100644
index 0000000..30be5a3
--- /dev/null
+++ b/hw/mcu/nordic/nrf5340_net/src/hal_vflash.c
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+
+#if MCUBOOT_MYNEWT
+#include <string.h>
+#include <assert.h>
+#include <nrf.h>
+#include <mcu/nrf5340_net_hal.h>
+#include <hal/hal_flash_int.h>
+#include <nrfx_ipc.h>
+#include <bootutil/bootutil.h>
+#include <bootutil/image.h>
+
+#define NRF5340_NET_VFLASH_SECTOR_SZ 2048
+
+#define NRF_APP_IPC_NS                  ((NRF_IPC_Type *)0x4002A000)
+#define NRF_APP_IPC_S                   ((NRF_IPC_Type *)0x5002A000)
+
+struct swap_data {
+    uint32_t encryption_key0[4];
+    uint32_t encryption_key1[4];
+    uint32_t swap_size;
+    struct image_trailer trailer;
+};
+
+static const struct swap_data swap_data_template = {
+    .encryption_key0 = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+    .encryption_key1 = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+    .swap_size = 0xffffffff,
+    .trailer = {
+        .swap_type = BOOT_SWAP_TYPE_PERM,
+        .pad1 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+        .copy_done = 0xff,
+        .pad2 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+        .image_ok = 0x01,
+        .pad3 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+        .magic = { 0x77, 0xc2, 0x95, 0xf3, 0x60, 0xd2, 0xef, 0x7f,
+                   0x35, 0x52, 0x50, 0x0f, 0x2c, 0xb6, 0x79, 0x80 },
+    }
+};
+
+int
+nrf5340_net_get_image_hash(uint8_t area_id, uint8_t hash[32])
+{
+    const struct flash_area *fa = NULL;
+    struct image_header header;
+    uint32_t addr;
+    uint32_t limit;
+    struct image_tlv_info info;
+    struct image_tlv tlv;
+    int rc = -1;
+
+    if (flash_area_open(area_id, &fa) != 0) {
+        goto end;
+    }
+
+    /* Does slot contain valid image */
+    if (flash_area_read(fa, 0, &header, sizeof(header)) != 0 ||
+        header.ih_magic != IMAGE_MAGIC) {
+        goto end;
+    }
+
+    addr = header.ih_hdr_size + header.ih_img_size;
+    /* Does image has TLV? (needed for image hash verification) */
+    if (flash_area_read(fa, addr, &info, sizeof(info)) != 0 ||
+        info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+        goto end;
+    }
+
+    addr += sizeof(info);
+    limit = addr + info.it_tlv_tot;
+    while (addr + sizeof(tlv) + 32 <= limit) {
+        if (flash_area_read(fa, addr, &tlv, sizeof(tlv)) != 0) {
+            goto end;
+        }
+        addr += sizeof(tlv);
+        if (tlv.it_type == IMAGE_TLV_SHA256) {
+            if (tlv.it_len != 32) {
+                goto end;
+            }
+            rc = flash_area_read(fa, addr, hash, 32);
+            break;
+        }
+        addr += tlv.it_len;
+    }
+end:
+    if (fa) {
+        flash_area_close(fa);
+    }
+    return rc;
+}
+
+bool
+nrf5340_net_images_match(void)
+{
+    uint8_t active_slot_hash[32];
+    uint8_t pending_slot_hash[32];
+
+    if (nrf5340_net_get_image_hash(FLASH_AREA_IMAGE_0, active_slot_hash) == 0 &&
+        nrf5340_net_get_image_hash(FLASH_AREA_IMAGE_1, pending_slot_hash) == 0 &&
+        memcmp(pending_slot_hash, active_slot_hash, 32) == 0) {
+        /* Same hash for current and pending slot, act as slot 2 was empty */
+        return true;
+    }
+    return false;
+}
+
+static int
+nrf5340_net_vflash_read(const struct hal_flash *dev, uint32_t address, void *dst,
+                        uint32_t num_bytes)
+{
+    struct nrf5340_vflash *vflash = (struct nrf5340_vflash *)dev;
+    uint32_t write_bytes = num_bytes;
+    uint8_t *wp = dst;
+    const struct flash_area *fa;
+    struct swap_data swap_data;
+
+    if ((vflash->nv_image_size > 0) && (vflash->nv_slot1 == NULL)) {
+        flash_area_open(FLASH_AREA_IMAGE_1, &vflash->nv_slot1);
+
+        /*
+         * If application side provided image check if same image was already
+         * provided before and if so mark virtual slot as empty
+         */
+        if (nrf5340_net_images_match()) {
+            /* Same hash for current and pending slot, act as slot 2 was empty */
+            vflash->nv_image_size = 0;
+        }
+    }
+    fa = vflash->nv_slot1;
+
+    /* Copy data from image */
+    if (address < vflash->nv_image_size) {
+        if (address + write_bytes > vflash->nv_image_size) {
+            write_bytes = vflash->nv_image_size - address;
+        }
+        memcpy(wp, vflash->nv_image_address + address, write_bytes);
+        address += write_bytes;
+        wp += write_bytes;
+        num_bytes -= write_bytes;
+    }
+    /* Copy data from image trailer */
+    if (num_bytes > 0 && fa != NULL && address >= (fa->fa_off + fa->fa_size - sizeof(swap_data))) {
+        swap_data = swap_data_template;
+        uint32_t off = address - (fa->fa_off + fa->fa_size - sizeof(swap_data));
+        write_bytes = num_bytes;
+        if (num_bytes > sizeof(swap_data) + off) {
+            write_bytes = sizeof(swap_data) - off;
+        }
+        memcpy(wp, ((uint8_t *)&swap_data) + off, write_bytes);
+        wp += write_bytes;
+        address += write_bytes;
+        num_bytes -= write_bytes;
+    }
+    /* Fill the rest with FF */
+    if (num_bytes > 0) {
+        memset(wp, 0xff, num_bytes);
+    }
+    return 0;
+}
+
+/*
+ * Flash write is done by writing 4 bytes at a time at a word boundary.
+ */
+static int
+nrf5340_net_vflash_write(const struct hal_flash *dev, uint32_t address,
+                         const void *src, uint32_t num_bytes)
+{
+    (void)dev;
+    (void)address;
+    (void)src;
+    (void)num_bytes;
+
+    return 0;
+}
+
+static int
+nrf5340_net_vflash_erase_sector(const struct hal_flash *dev, uint32_t sector_address)
+{
+    (void)dev;
+    (void)sector_address;
+
+    return 0;
+}
+
+static int
+nrf5340_net_vflash_sector_info(const struct hal_flash *dev, int idx,
+                               uint32_t *address, uint32_t *sz)
+{
+    (void)dev;
+
+    assert(idx < nrf5340_net_flash_dev.hf_sector_cnt);
+    *address = idx * NRF5340_NET_VFLASH_SECTOR_SZ;
+    *sz = NRF5340_NET_VFLASH_SECTOR_SZ;
+
+    return 0;
+}
+
+static int
+nrf5340_net_vflash_init(const struct hal_flash *dev)
+{
+    struct nrf5340_vflash *vflash = (struct nrf5340_vflash *)dev;
+
+    /*
+     * Application side IPC will set GPMEM registers to address and size of
+     * memory where net core image is present in application flash.
+     * If those values are 0, application image does not have embedded image,
+     * and there no need to provide any data.
+     * Set nv_image_size to 0 and all reads will return empty values (0xff)
+     */
+    vflash->nv_image_address = (const uint8_t *)NRF_APP_IPC_S->GPMEM[0];
+    vflash->nv_image_size = NRF_APP_IPC_S->GPMEM[1];
+
+    return 0;
+}
+
+static const struct hal_flash_funcs nrf5340_net_vflash_funcs = {
+    .hff_read = nrf5340_net_vflash_read,
+    .hff_write = nrf5340_net_vflash_write,
+    .hff_erase_sector = nrf5340_net_vflash_erase_sector,
+    .hff_sector_info = nrf5340_net_vflash_sector_info,
+    .hff_init = nrf5340_net_vflash_init
+};
+
+struct nrf5340_vflash nrf5340_net_vflash_dev = {
+    .nv_flash = {
+        .hf_itf = &nrf5340_net_vflash_funcs,
+        .hf_base_addr = 0x00000000,
+        .hf_size = 256 * 1024,
+        .hf_sector_cnt = 128,
+        .hf_align = 1,
+        .hf_erased_val = 0xff,
+    }
+};
+
+#endif