diff --git a/.gitmodules b/.gitmodules index f93882e..5ae687a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,9 @@ [submodule "freeRTOS-Freescale-K64F-https-TLS1.3/picotcp"] path = freeRTOS-Freescale-K64F-https-TLS1.3/picotcp url = https://gitlab.com/insane-adding-machines/picotcp.git +[submodule "contiki-ng-nrf52/contiki-ng"] + path = contiki-ng-nrf52/contiki-ng + url = https://github.com/contiki-ng/contiki-ng.git +[submodule "riotOS-nrf52840dk-ble/RIOT"] + path = riotOS-nrf52840dk-ble/RIOT + url = https://github.com/RIOT-OS/RIOT diff --git a/contiki-ng-nrf52/contiki-ng b/contiki-ng-nrf52/contiki-ng new file mode 160000 index 0000000..0f3956a --- /dev/null +++ b/contiki-ng-nrf52/contiki-ng @@ -0,0 +1 @@ +Subproject commit 0f3956a14eec28e9a8ca007dd95be8c0f832096f diff --git a/riotOS-nrf52840dk-ble/Makefile b/riotOS-nrf52840dk-ble/Makefile new file mode 100644 index 0000000..597b8c1 --- /dev/null +++ b/riotOS-nrf52840dk-ble/Makefile @@ -0,0 +1,54 @@ +CROSS_COMPILE:=arm-none-eabi- +OBJCOPY:=$(CROSS_COMPILE)objcopy +JLINK_OPTS = -Device NRF52840_xxAA -if swd -speed 1000 + + +APP_SRC:=$(PWD)/nrf52-gatt-service +BOOT_ELF:=nrf52-gatt-service/bin/nrf52840dk/nrf52-gatt-service.elf +BOOT_IMG:=nrf52-gatt-service/bin/nrf52840dk/nrf52-gatt-service_v1_signed.bin +UPDATE_IMG:=$(PWD)/nrf52-gatt-service/bin/nrf52840dk/nrf52-gatt-service_v2_signed.bin +WOLFBOOT=../wolfBoot +WOLFBOOT_BIN=$(WOLFBOOT)/wolfboot.bin + + +all: $(BOOT_IMG) $(WOLFBOOT_BIN) + +$(BOOT_IMG): + make wolfboot -C $(APP_SRC) IMAGE_VERSION=1 + + +$(WOLFBOOT_BIN): FORCE + @cp -f ./wolfboot-config $(WOLFBOOT)/.config + @make -C $(WOLFBOOT) clean + @make -C $(WOLFBOOT) wolfboot.bin + +clean: FORCE + make -C $(WOLFBOOT) clean + make -C $(APP_SRC) clean + rm -f $(APP_SRC)/*.bin + rm -f *.bin + rm -f tags + rm -rf linux-bluez/__pycache__ + +distclean: clean + rm -rf $(APP_SRC)/bin + + +flash: $(WOLFBOOT_BIN) $(BOOT_IMG) + JLinkExe $(JLINK_OPTS) -CommanderScript flash_all.jlink + +reset: FORCE + JLinkExe $(JLINK_OPTS) -CommanderScript reset.jlink + +update: FORCE + @make wolfboot -C $(APP_SRC) IMAGE_VERSION=2 + @echo UPDATE ready. Please execute + @echo linux-bluez/fwupdate.py $(UPDATE_IMG) + @echo as root. + + +erase: FORCE + JLinkExe $(JLINK_OPTS) -CommanderScript flash_erase.jlink + + +.PHONY: FORCE diff --git a/riotOS-nrf52840dk-ble/RIOT b/riotOS-nrf52840dk-ble/RIOT new file mode 160000 index 0000000..bb1a347 --- /dev/null +++ b/riotOS-nrf52840dk-ble/RIOT @@ -0,0 +1 @@ +Subproject commit bb1a3470d786b5b7e19c4fb59c5094d525862a4e diff --git a/riotOS-nrf52840dk-ble/flash_all.jlink b/riotOS-nrf52840dk-ble/flash_all.jlink new file mode 100644 index 0000000..50d9fbf --- /dev/null +++ b/riotOS-nrf52840dk-ble/flash_all.jlink @@ -0,0 +1,5 @@ +loadbin ../wolfBoot/wolfboot.bin 0x0 +loadbin nrf52-gatt-service/bin/nrf52840dk/nrf52-gatt-service_v1_signed.bin 0x20000 +r +g +q diff --git a/riotOS-nrf52840dk-ble/flash_erase.jlink b/riotOS-nrf52840dk-ble/flash_erase.jlink new file mode 100644 index 0000000..b6353af --- /dev/null +++ b/riotOS-nrf52840dk-ble/flash_erase.jlink @@ -0,0 +1,4 @@ +erase +r +q + diff --git a/riotOS-nrf52840dk-ble/linux-bluez/bluezutils.py b/riotOS-nrf52840dk-ble/linux-bluez/bluezutils.py new file mode 100644 index 0000000..3543ad9 --- /dev/null +++ b/riotOS-nrf52840dk-ble/linux-bluez/bluezutils.py @@ -0,0 +1,50 @@ +# bluez-utils +# SPDX-License-Identifier: LGPL-2.1-or-later + +import dbus + +SERVICE_NAME = "org.bluez" +ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1" +DEVICE_INTERFACE = SERVICE_NAME + ".Device1" + +def get_managed_objects(): + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object("org.bluez", "/"), + "org.freedesktop.DBus.ObjectManager") + return manager.GetManagedObjects() + +def find_adapter(pattern=None): + return find_adapter_in_objects(get_managed_objects(), pattern) + +def find_adapter_in_objects(objects, pattern=None): + bus = dbus.SystemBus() + for path, ifaces in objects.items(): + adapter = ifaces.get(ADAPTER_INTERFACE) + if adapter is None: + continue + if not pattern or pattern == adapter["Address"] or \ + path.endswith(pattern): + obj = bus.get_object(SERVICE_NAME, path) + return dbus.Interface(obj, ADAPTER_INTERFACE) + raise Exception("Bluetooth adapter not found") + +def find_device(device_address, adapter_pattern=None): + return find_device_in_objects(get_managed_objects(), device_address, + adapter_pattern) + +def find_device_in_objects(objects, device_address, adapter_pattern=None): + bus = dbus.SystemBus() + path_prefix = "" + if adapter_pattern: + adapter = find_adapter_in_objects(objects, adapter_pattern) + path_prefix = adapter.object_path + for path, ifaces in objects.items(): + device = ifaces.get(DEVICE_INTERFACE) + if device is None: + continue + if (device["Address"] == device_address and + path.startswith(path_prefix)): + obj = bus.get_object(SERVICE_NAME, path) + return dbus.Interface(obj, DEVICE_INTERFACE) + + raise Exception("Bluetooth device not found") diff --git a/riotOS-nrf52840dk-ble/linux-bluez/fwupdate.py b/riotOS-nrf52840dk-ble/linux-bluez/fwupdate.py new file mode 100755 index 0000000..2ae6242 --- /dev/null +++ b/riotOS-nrf52840dk-ble/linux-bluez/fwupdate.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +# +# fwupdate.py +# +# Copyright (C) 2021 wolfSSL Inc. +# +# wolfBoot fw-update example, Bluez client for Linux. +# +# wolfBoot is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# wolfBoot is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + +import os +import sys +import dbus +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject +import sys +import time + +from dbus.mainloop.glib import DBusGMainLoop + +import bluezutils +import struct +import keyboard + +# Settings +FWUPDATE_TARGET="nRF52_UPDATE" +HCI="hci1" + + +bus = None +mainloop = None + +BLUEZ_SERVICE_NAME = 'org.bluez' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' + +GATT_SERVICE_IFACE = 'org.bluez.GattService1' +GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1' + +FWUPDATE_SVC_UUID = '38f28386-3070-4f3b-ba38-27507e991760' + +FWUPDATE_CHR_WRITE_UUID = '38f28386-3070-4f3b-ba38-27507e991762' +FWUPDATE_CHR_READ_UUID = '38f28386-3070-4f3b-ba38-27507e991764' +FWUPDATE_BOOT_NOTIFY_UUID = '38f28386-3070-4f3b-ba38-27507e991766' +FWUPDATE_CHUNK_SIZE=128 + + + + +# The objects that we interact with. +fwupdate_service = None +fwupdate_ctrl_chrc = None +fwupdate_read_chrc = None +fwupdate_notify_chrc = None +bt_write_in_progress = False + +fwup_off = 0 +fwup_filename = None +fwup_file = None +fwup_filesize = 0 + +def bt_write(wbuf): + global bt_write_in_progress + bt_write_in_progress = True + fwupdate_ctrl_chrc[0].WriteValue(wbuf, {}, reply_handler=ctrl_write_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + #print("SND") + +def ctrl_write_cb(): + global bt_write_buffer + #print("ACK") + bt_write_in_progress = False + fwupdate_ctrl_chrc[0].ReadValue({}, reply_handler=update_off_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + +def generic_error_cb(error): + print('D-Bus call failed: ' + str(error)) + mainloop.quit() + +def update_off_cb(value): + global fwup_off + global fwup_filename + global fwup_file + global fwup_filesize + x = struct.unpack("I", bytes(value))[0] + fwup_off = x + print("Update offset: " + str(x)) + + if (fwup_file is None): + if (fwup_filename is None): + print ("No update filename provided. Terminating...\n"); + sys.exit(0) + fwup_filesize = os.path.getsize(fwup_filename) + try: + fwup_file = open(fwup_filename, "rb") + except: + print("Cannot open "+fwup_filename+". Exiting...") + sys.exit(1) + + fwup_file.seek(fwup_off, 0) + wbuf = struct.pack("I", fwup_off) + wbuf += fwup_file.read(FWUPDATE_CHUNK_SIZE) + bt_write(wbuf) + +def read_info_cb(value): + x = struct.unpack("I", bytes(value))[0] + print('Current version: '+ str(x)) + +def fwupdate_read_start_notify_cb(): + print('notification enabled') + + +def fwupdate_read_changed_cb(iface, changed_props, invalidated_props): + if iface != GATT_CHRC_IFACE: + return + + if not len(changed_props): + return + + value = changed_props.get('Value', None) + if not value: + return + fwupdate_read_chrc[0].ReadValue({}, reply_handler=read_info_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + +def sensors_start_notify_cb(): + print("Sensors notifications: enabled.") + + +def start_client(): + fwupdate_read_chrc[0].ReadValue({}, reply_handler=read_info_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + + fwupdate_read_prop_iface = dbus.Interface(fwupdate_notify_chrc[0], DBUS_PROP_IFACE) + fwupdate_read_prop_iface.connect_to_signal("PropertiesChanged", fwupdate_read_changed_cb) + + fwupdate_notify_chrc[0].StartNotify(reply_handler=sensors_start_notify_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + + fwupdate_ctrl_chrc[0].ReadValue({}, reply_handler=update_off_cb, + error_handler=generic_error_cb, + dbus_interface=GATT_CHRC_IFACE) + +def process_chrc(chrc_path): + chrc = bus.get_object(BLUEZ_SERVICE_NAME, chrc_path) + chrc_props = chrc.GetAll(GATT_CHRC_IFACE, + dbus_interface=DBUS_PROP_IFACE) + + uuid = chrc_props['UUID'] + + if uuid == FWUPDATE_CHR_WRITE_UUID: + global fwupdate_ctrl_chrc + fwupdate_ctrl_chrc = (chrc, chrc_props) + elif uuid == FWUPDATE_CHR_READ_UUID: + global fwupdate_read_chrc + fwupdate_read_chrc = (chrc, chrc_props) + elif uuid == FWUPDATE_BOOT_NOTIFY_UUID: + global fwupdate_notify_chrc + fwupdate_notify_chrc = (chrc, chrc_props) + else: + print('Unrecognized characteristic: ' + uuid) + + return True + + +def process_fwupdate_service(service_path, chrc_paths): + service = bus.get_object(BLUEZ_SERVICE_NAME, service_path) + service_props = service.GetAll(GATT_SERVICE_IFACE, + dbus_interface=DBUS_PROP_IFACE) + + uuid = service_props['UUID'] + + if uuid != FWUPDATE_SVC_UUID: + return False + + print('fwupdate GATT service found: ' + service_path) + + global fwupdate_service + fwupdate_service = (service, service_props, service_path) + + # Process the characteristics. + for chrc_path in chrc_paths: + process_chrc(chrc_path) + + return True + + +def interfaces_removed_cb(object_path, interfaces): + if not fwupdate_service: + return + + if object_path == fwupdate_service[2]: + print('Service was removed') + mainloop.quit() + + +def main(): + # Set up the main loop. + DBusGMainLoop(set_as_default=True) + global bus + bus = dbus.SystemBus() + global mainloop + mainloop = GObject.MainLoop() + address = None + + if (len(sys.argv) > 1): + global fwup_filename + fwup_filename = sys.argv[1] + + om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE) + om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb) + + print('Getting objects...') + objects = om.GetManagedObjects() + chrcs = [] + + # List devices found + for path, interfaces in objects.items(): + device = interfaces.get("org.bluez.Device1") + if (device is None): + continue + try: + if (device["Name"] == FWUPDATE_TARGET): + print("Found FWUPDATE TARGET!") + address = device["Address"] + break + except: + continue + + if address is None: + print("device not found.") + return + + device = bluezutils.find_device(address, HCI) + if (device is None): + print("Cannot 'find_device'") + else: + device.Connect() + print("Connected") + + + # List characteristics found + for path, interfaces in objects.items(): + if GATT_CHRC_IFACE not in interfaces.keys(): + continue + chrcs.append(path) + + # List sevices found + for path, interfaces in objects.items(): + if GATT_SERVICE_IFACE not in interfaces.keys(): + continue + + chrc_paths = [d for d in chrcs if d.startswith(path + "/")] + + if process_fwupdate_service(path, chrc_paths): + break + + if not fwupdate_service: + print('No FWUPDATE found.') + sys.exit(1) + + start_client() + mainloop.run() + + +if __name__ == '__main__': + main() diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/.gdbinit b/riotOS-nrf52840dk-ble/nrf52-gatt-service/.gdbinit new file mode 100644 index 0000000..75af5e8 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/.gdbinit @@ -0,0 +1,4 @@ +tar rem:3333 +file bin/nrf52840dk/nrf52-gatt-service.elf +foc c + diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/Makefile b/riotOS-nrf52840dk-ble/nrf52-gatt-service/Makefile new file mode 100644 index 0000000..962657e --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/Makefile @@ -0,0 +1,63 @@ +# name of your application +APPLICATION = nrf52-gatt-service + +#debug +DEBUG=0 + +# If no BOARD is found in the environment, use this default: +BOARD ?= nrf52840dk +#BOARD ?= arduino-nano-33-iot + +WOLFBOOT_OFFSET=131072 +WOLFBOOT_PARTITION_SIZE=262144 +IMAGE_VERSION=514 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../RIOT/ + +# Require uart module +FEATURES_REQUIRED += periph_uart + +EXTERNAL_MODULE_DIRS += $(CURDIR)/libwolfboot +USEMODULE += libwolfboot + +# Some RIOT modules needed for this example +USEMODULE += event_timeout + +# Network +USEMODULE += nimble +USEMODULE += nimble_addr +USEMODULE += nimble_scanner +USEMODULE += nimble_scanlist +USEMODULE += nimble_svc_gap +USEMODULE += nimble_svc_gatt +USEMODULE += nimble_drivers_nrf5x +USEMODULE += random + +USEMODULE += periph_flashpage + + + +#USEMODULE += usbus +#USEMODULE += stdio_cdc_acm +#USEMODULE += auto_init_usbus +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 0 +CFLAGS+=-Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -I$(CURDIR)/include -Wno-missing-include-dirs + +CFLAGS+=-DWOLFBOOT_HASH_SHA256 -DWOLFBOOT_SIGN_ECC256 +WOLFBOOT_DIR=$(abspath $(RIOTBASE)/../../wolfBoot) +USEMODULE_INCLUDES+=-I$(WOLFBOOT_DIR)/include + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include +include nimble.inc.mk +include ../wolfboot.mk diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/README.md b/riotOS-nrf52840dk-ble/nrf52-gatt-service/README.md new file mode 100644 index 0000000..499ff6f --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/README.md @@ -0,0 +1,41 @@ +## BLE Firmware update with wolfBoot on nRF52 + +### Components + + - BLE GATT server application, running RIOT-OS + - Python firmware update client, running on GNU/Linux + +### Usage + + - Compile wolfBoot and the GATT server application using: + +``` +make +``` + + + + - Connect the nRF52840 Development Kit and use the following command to upload the + bootloader and the version 1 of the signed application to the board: + +``` +make flash +``` + + + - Prepare an update package using: + +``` +make update +``` + +- You can check the current version running on the target, either from a USB-serial console on `/dev/ttyACM0` by typing `info` into the shell, or +by reading the BLE GATT characteristics of the device. + +- Use the fwupdate.py python application to transfer the update via BLE: + +``` +linux-bluez/fwupdate.py nrf52-gatt-service/bin/nrf52840dk/nrf52-gatt-service_v2_signed.bin +``` + + diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/gatt-srv.c b/riotOS-nrf52840dk-ble/nrf52-gatt-service/gatt-srv.c new file mode 100644 index 0000000..b4453f2 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/gatt-srv.c @@ -0,0 +1,482 @@ +/* gatt_srv.c + * + * Copyright (C) 2021 wolfSSL Inc. + * + * wolfBoot fw-update example RIOT application, running on nRF52840. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + */ + +#include +#include + +#include "assert.h" +#include "event/timeout.h" +#include "nimble_riot.h" +#include "net/bluetil/ad.h" +#include "periph/gpio.h" +#include "timex.h" +#include "wolfboot/wolfboot.h" +#include "hal.h" +#include "board.h" + +#include "host/ble_hs.h" +#include "host/ble_gatt.h" +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + + +static const char *_device_name = "nRF52_UPDATE"; +static const char *_manufacturer_name = "wolfSSL"; +static const char *_model_number = "PCA10056"; +static const char *_serial_number = "dab1b30a11-ed218-x1"; +static const char *_hw_ver = "1"; + +static event_queue_t _eq; +static event_t _update_evt; +static event_timeout_t _update_timeout_evt; + +static uint16_t _conn_handle; +static uint16_t _reboot_val_handle; + +static void reboot(void) +{ +# define SCB_AIRCR (*((volatile uint32_t *)(0xE000ED0C))) +# define AIRCR_VECTKEY (0x05FA0000) +# define SYSRESET (1 << 2) + SCB_AIRCR = AIRCR_VECTKEY | SYSRESET; +} + + +static int _devinfo_handler(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int _fwupdate_handler( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int _fwupdate_info_handler(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int _bas_handler(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static void _start_advertising(void); + + +/* UUID = 38f28386-3070-4f3b-ba38-27507e991760 */ +static const ble_uuid128_t gatt_svr_svc_fwupdate = BLE_UUID128_INIT( + 0x60, 0x17, 0x99, 0x7e, 0x50, 0x27, 0x38, 0xba, + 0x3b, 0x4f, 0x70, 0x30, 0x86, 0x83, 0xf2, 0x38 + ); + +/* UUID = 38f28386-3070-4f3b-ba38-27507e991762 */ +static const ble_uuid128_t gatt_svr_chr_fwupdate = BLE_UUID128_INIT( + 0x62, 0x17, 0x99, 0x7e, 0x50, 0x27, 0x38, 0xba, + 0x3b, 0x4f, 0x70, 0x30, 0x86, 0x83, 0xf2, 0x38 + ); + +/* UUID = 38f28386-3070-4f3b-ba38-27507e991764 */ +static const ble_uuid128_t gatt_svr_chr_fwupdate_info = BLE_UUID128_INIT( + 0x64, 0x17, 0x99, 0x7e, 0x50, 0x27, 0x38, 0xba, + 0x3b, 0x4f, 0x70, 0x30, 0x86, 0x83, 0xf2, 0x38 + ); + +/* UUID = 38f28386-3070-4f3b-ba38-27507e991766 */ +static const ble_uuid128_t gatt_svr_chr_fwupdate_info_notify = BLE_UUID128_INIT( + 0x66, 0x17, 0x99, 0x7e, 0x50, 0x27, 0x38, 0xba, + 0x3b, 0x4f, 0x70, 0x30, 0x86, 0x83, 0xf2, 0x38 + ); + +/* GATT service definitions */ +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Firmware update controls */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t*) &gatt_svr_svc_fwupdate.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: FWUPDATE */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_fwupdate.u, + .access_cb = _fwupdate_handler, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + /* Characteristic: Firmware info */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_fwupdate_info.u, + .access_cb = _fwupdate_info_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Reboot notification */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_fwupdate_info_notify.u, + .access_cb = _fwupdate_info_handler, + .val_handle = &_reboot_val_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + 0, /* no more characteristics in this service */ + }, } + }, + { + /* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BLE_GATT_SVC_DEVINFO), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_MANUFACTURER_NAME), + .access_cb = _devinfo_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_MODEL_NUMBER_STR), + .access_cb = _devinfo_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_SERIAL_NUMBER_STR), + .access_cb = _devinfo_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_FW_REV_STR), + .access_cb = _devinfo_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_HW_REV_STR), + .access_cb = _devinfo_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* no more characteristics in this service */ + }, } + }, + { + /* Battery Level Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BLE_GATT_SVC_BAS), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_CHAR_BATTERY_LEVEL), + .access_cb = _bas_handler, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* no more characteristics in this service */ + }, } + }, + { + 0, /* no more services */ + }, +}; + +static int _fwupdate_info_handler( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + + int rc = 0; + uint16_t om_len; + ble_uuid_t* read_uuid = (ble_uuid_t*) &gatt_svr_chr_fwupdate_info.u; + static uint8_t ch; + uint32_t ver = wolfBoot_current_firmware_version(); + (void) conn_handle; + (void) attr_handle; + (void) arg; + if (ble_uuid_cmp(ctxt->chr->uuid, read_uuid) == 0) { + puts("access to characteristic 'fwupdate_info'"); + rc = os_mbuf_append(ctxt->om, &ver, sizeof(uint32_t)); + return rc; + } + puts("unhandled uuid!"); + return 1; +} + +#define FWUP_CHUNK_SIZE 128 +#define FWUP_PKT_SIZE (FWUP_CHUNK_SIZE + sizeof(uint32_t)) + +static uint32_t fwup_cur_off = 0; +static uint32_t fwup_size = 0; +static uint8_t fwup_page_buffer[WOLFBOOT_SECTOR_SIZE]; +static uint8_t fwup_pkt_buffer[FWUP_PKT_SIZE]; +static uint32_t fwup_pkt_len = 0; + +static void parse_update(void) +{ + uint32_t *sz; + uint32_t *seq; + const uint32_t zero = 0; + if (fwup_size > 0) + printf("Update: %lu / %lu \n", fwup_cur_off, fwup_size); + else + printf("Update: first packet\n"); + if (fwup_cur_off == 0) { + if (memcmp(&zero, fwup_pkt_buffer, 4) != 0) { + printf("wrong packet recvd: %lu\n", *(uint32_t *)(fwup_pkt_buffer)); + return; + } + if (memcmp(fwup_pkt_buffer + 4, "WOLF", 4) != 0) { + puts("wrong packet hdr"); + return; + } + sz = (uint32_t *)(fwup_pkt_buffer + 8); + if ((*sz < 256) || (*sz > WOLFBOOT_PARTITION_SIZE)) { + printf("Wrong firmware size %lu\n", *sz); + return; + } + fwup_size = *sz + IMAGE_HEADER_SIZE; + printf("Total firmware len: %lu\n", fwup_size); + hal_flash_lock(); + hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); + hal_flash_unlock(); + memset(fwup_page_buffer, 0xFF, WOLFBOOT_SECTOR_SIZE); + } + if (fwup_size > 0) { + uint32_t in_page_off = fwup_cur_off % WOLFBOOT_SECTOR_SIZE; + seq = (uint32_t *)(fwup_pkt_buffer); + if (*seq != fwup_cur_off) { + printf("Wrong seq %lu expecting %lu \n", *seq, fwup_cur_off); + return; + } + memcpy(fwup_page_buffer + in_page_off, fwup_pkt_buffer + 4, fwup_pkt_len - 4); + fwup_cur_off += fwup_pkt_len - 4; + if (((fwup_cur_off % WOLFBOOT_SECTOR_SIZE) == 0) || (fwup_cur_off >= fwup_size)) { + hal_flash_unlock(); + if (fwup_cur_off >= fwup_size) { + uint32_t last_chunk_size = fwup_cur_off % WOLFBOOT_SECTOR_SIZE; + uint32_t last_chunk_addr = fwup_cur_off - last_chunk_size; + if (last_chunk_size > 0) { + hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + last_chunk_addr, fwup_page_buffer, last_chunk_size); + } + } else { + hal_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + (fwup_cur_off - WOLFBOOT_SECTOR_SIZE), fwup_page_buffer, WOLFBOOT_SECTOR_SIZE); + } + hal_flash_lock(); + memset(fwup_page_buffer, 0xFF, WOLFBOOT_SECTOR_SIZE); + if (fwup_cur_off + fwup_pkt_len >= fwup_size) { + printf("Update complete (%lu/%lu).\n", fwup_cur_off, fwup_size); + wolfBoot_update_trigger(); + reboot(); + while(1) + ; + } + } + } + return; +} + + +static int _fwupdate_handler( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + + int rc = 0; + uint16_t om_len; + ble_uuid_t* write_uuid = (ble_uuid_t*) &gatt_svr_chr_fwupdate.u; + (void) conn_handle; + (void) attr_handle; + (void) arg; + + if (ble_uuid_cmp(ctxt->chr->uuid, write_uuid) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &fwup_cur_off, sizeof(uint32_t)); + fwup_pkt_len = 0; + //printf("ACK offset: %lx\n", fwup_cur_off); + break; + case BLE_GATT_ACCESS_OP_WRITE_CHR: + om_len = OS_MBUF_PKTLEN(ctxt->om); + if (om_len <= FWUP_PKT_SIZE) { + rc = ble_hs_mbuf_to_flat(ctxt->om, fwup_pkt_buffer, FWUP_PKT_SIZE, &om_len); + fwup_pkt_len = om_len; + parse_update(); + } else { + printf("wrong OM LEN: %d !\n", om_len); + } + break; + case BLE_GATT_ACCESS_OP_READ_DSC: + puts("read from descriptor"); + break; + + case BLE_GATT_ACCESS_OP_WRITE_DSC: + puts("write to descriptor"); + break; + + default: + puts("unhandled operation!"); + rc = 1; + break; + } + return rc; + } + + puts("unhandled uuid!"); + return 1; +} + +static int _devinfo_handler(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + (void)conn_handle; + (void)attr_handle; + (void)arg; + char version[20] = ""; + const char *str; + + switch (ble_uuid_u16(ctxt->chr->uuid)) { + case BLE_GATT_CHAR_MANUFACTURER_NAME: + puts("[READ] device information service: manufacturer name value"); + str = _manufacturer_name; + break; + case BLE_GATT_CHAR_MODEL_NUMBER_STR: + puts("[READ] device information service: model number value"); + str = _model_number; + break; + case BLE_GATT_CHAR_SERIAL_NUMBER_STR: + puts("[READ] device information service: serial number value"); + str = _serial_number; + break; + case BLE_GATT_CHAR_FW_REV_STR: + puts("[READ] device information service: firmware revision value"); + snprintf(version, 20, "%08x", (unsigned int)wolfBoot_current_firmware_version()); + str = version; + break; + case BLE_GATT_CHAR_SW_REV_STR: + puts("[READ] device information service: software revision value"); + snprintf(version, 20, "%08x", (unsigned int)wolfBoot_update_firmware_version()); + str = version; + break; + case BLE_GATT_CHAR_HW_REV_STR: + puts("[READ] device information service: hardware revision value"); + str = _hw_ver; + break; + default: + return BLE_ATT_ERR_UNLIKELY; + } + + int res = os_mbuf_append(ctxt->om, str, strlen(str)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int _bas_handler(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + (void)conn_handle; + (void)attr_handle; + (void)arg; + + puts("[READ] battery level service: battery level value"); + + uint8_t level = 50; /* this battery will never drain :-) */ + int res = os_mbuf_append(ctxt->om, &level, sizeof(level)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg) +{ + (void)arg; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + printf("Ev: connect, Status: %d\n", event->connect.status); + if (event->connect.status) { + _start_advertising(); + LED2_OFF; + return 0; + } + LED2_ON; + _conn_handle = event->connect.conn_handle; + break; + + case BLE_GAP_EVENT_DISCONNECT: + _start_advertising(); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + if (event->subscribe.attr_handle == _reboot_val_handle) { + if (event->subscribe.cur_notify == 1) { + } + else { + } + } + break; + } + + return 0; +} + +static void _start_advertising(void) +{ + struct ble_gap_adv_params advp; + int res; + + memset(&advp, 0, sizeof advp); + advp.conn_mode = BLE_GAP_CONN_MODE_UND; + advp.disc_mode = BLE_GAP_DISC_MODE_GEN; + advp.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN; + advp.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MAX; + res = ble_gap_adv_start(nimble_riot_own_addr_type, NULL, BLE_HS_FOREVER, + &advp, gap_event_cb, NULL); + assert(res == 0); + (void)res; +} + +static void notify_boot(void) +{ + struct os_mbuf *om; + unsigned int version; + + version = (unsigned int) wolfBoot_current_firmware_version(); + printf("[GATT] Sending boot notification.\n"); + /* send data notification to GATT client */ + om = ble_hs_mbuf_from_flat(&version, sizeof(unsigned int)); + if (!om) { + printf("[GATT] Error incapsulating version data in frame \n"); + return; + } + ble_gattc_notify_custom(_conn_handle, _reboot_val_handle, om); +} + +void *gatt_srv(void *arg) +{ + puts("NimBLE GATT server starting"); + + int res = 0; + msg_t msg; + (void)res; + + + notify_boot(); + wolfBoot_success(); + + /* verify and add our custom services */ + res = ble_gatts_count_cfg(gatt_svr_svcs); + assert(res == 0); + res = ble_gatts_add_svcs(gatt_svr_svcs); + assert(res == 0); + + /* set the device name */ + ble_svc_gap_device_name_set(_device_name); + /* reload the GATT server to link our added services */ + ble_gatts_start(); + + /* configure and set the advertising data */ + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + bluetil_ad_t ad; + bluetil_ad_init_with_flags(&ad, buf, sizeof(buf), BLUETIL_AD_FLAGS_DEFAULT); + bluetil_ad_add_name(&ad, _device_name); + ble_gap_adv_set_data(ad.buf, ad.pos); + + /* start to advertise this node */ + _start_advertising(); + + while (1) { + if (msg_receive(&msg) >= 0) { + (void)msg; + } + } + return 0; +} diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gap.h b/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gap.h new file mode 100644 index 0000000..63ed0e5 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gap.h @@ -0,0 +1,56 @@ +/* ble_svc_gap.h + * + * Copyright (C) 2021 wolfSSL Inc. + * + * wolfBoot fw-update example RIOT application, running on nRF52840. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + */ +#ifndef H_BLE_SVC_GAP_ +#define H_BLE_SVC_GAP_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_SVC_GAP_UUID16 0x1800 +#define BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME 0x2a00 +#define BLE_SVC_GAP_CHR_UUID16_APPEARANCE 0x2a01 +#define BLE_SVC_GAP_CHR_UUID16_PERIPH_PREF_CONN_PARAMS 0x2a04 +#define BLE_SVC_GAP_CHR_UUID16_CENTRAL_ADDRESS_RESOLUTION 0x2aa6 + +#define BLE_SVC_GAP_APPEARANCE_GEN_UNKNOWN 0 +#define BLE_SVC_GAP_APPEARANCE_GEN_COMPUTER 128 +#define BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR 1157 + +typedef void (ble_svc_gap_chr_changed_fn) (uint16_t uuid); + +void ble_svc_gap_set_chr_changed_cb(ble_svc_gap_chr_changed_fn *cb); + +const char *ble_svc_gap_device_name(void); +int ble_svc_gap_device_name_set(const char *name); +uint16_t ble_svc_gap_device_appearance(void); +int ble_svc_gap_device_appearance_set(uint16_t appearance); + +void ble_svc_gap_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gatt.h b/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gatt.h new file mode 100644 index 0000000..ab66acb --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/include/ble_svc_gatt.h @@ -0,0 +1,42 @@ +/* ble_svc_gatt.h + * + * Copyright (C) 2021 wolfSSL Inc. + * + * wolfBoot fw-update example RIOT application, running on nRF52840. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + */ +#ifndef H_BLE_SVC_GATT_ +#define H_BLE_SVC_GATT_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; + +#define BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16 0x2a05 + +void ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle); +void ble_svc_gatt_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/Makefile b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/Makefile new file mode 100644 index 0000000..a9fdb97 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/Makefile @@ -0,0 +1,6 @@ +WOLFBOOT_DIR=$(abspath $(CURDIR)/../../../wolfBoot) +INCLUDES+=-I$(WOLFBOOT_DIR)/include +SRC+=libwolfboot.c nrf52.c +NO_AUTO_SRC = 1 + +include $(RIOTBASE)/Makefile.base diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/libwolfboot.c b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/libwolfboot.c new file mode 100644 index 0000000..52998eb --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/libwolfboot.c @@ -0,0 +1,798 @@ +/* libwolfboot.c + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include + +#include "hal.h" +#include "wolfboot/wolfboot.h" +#include "image.h" + +#ifdef UNIT_TEST +# define unit_dbg printf +#else +# define unit_dbg(...) do{}while(0) +#endif + +#ifndef TRAILER_SKIP +# define TRAILER_SKIP 0 +#endif + +#if defined(EXT_ENCRYPTED) + #if defined(__WOLFBOOT) + #include "encrypt.h" + #else + #include + #include + #define XMEMSET memset + #define XMEMCPY memcpy + #define XMEMCMP memcmp + #endif + #define ENCRYPT_TMP_SECRET_OFFSET (WOLFBOOT_PARTITION_SIZE - (TRAILER_SKIP + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)) + #define TRAILER_OVERHEAD (4 + 1 + (WOLFBOOT_PARTITION_SIZE / (2 * WOLFBOOT_SECTOR_SIZE))) /* MAGIC + PART_FLAG (1B) + (N_SECTORS / 2) */ + #define START_FLAGS_OFFSET (ENCRYPT_TMP_SECRET_OFFSET - TRAILER_OVERHEAD) +#else + #define XMEMCPY memcpy + #define ENCRYPT_TMP_SECRET_OFFSET (WOLFBOOT_PARTITION_SIZE - (TRAILER_SKIP)) +#endif + +#ifndef NULL +# define NULL (void *)0 +#endif + +#define NVM_CACHE_SIZE WOLFBOOT_SECTOR_SIZE + +#ifdef EXT_FLASH +static uint32_t ext_cache; +#endif + +static const uint32_t wolfboot_magic_trail = WOLFBOOT_MAGIC_TRAIL; +/* Top addresses for FLAGS field + * - PART_BOOT_ENDFLAGS = top of flags for BOOT partition + * - PART_UPDATE_ENDFLAGS = top of flags for UPDATE_PARTITION + */ + +#define PART_BOOT_ENDFLAGS (WOLFBOOT_PARTITION_BOOT_ADDRESS + ENCRYPT_TMP_SECRET_OFFSET) +#define FLAGS_BOOT_EXT() PARTN_IS_EXT(PART_BOOT) + +#ifdef FLAGS_HOME +/* + * In FLAGS_HOME mode, all FLAGS live at the end of the boot partition: + * / -12 /-8 /-4 / END + * |Sn| ... |S2|S1|S0|PU| MAGIC |X|X|X|PB| MAGIC | + * ^--sectors --^ ^--update ^---boot partition + * flags partition flag + * flag + * + * */ +#define PART_UPDATE_ENDFLAGS (PART_BOOT_ENDFLAGS - 8) +#define FLAGS_UPDATE_EXT() PARTN_IS_EXT(PART_BOOT) +#else +/* FLAGS are at the end of each partition */ +#define PART_UPDATE_ENDFLAGS (WOLFBOOT_PARTITION_UPDATE_ADDRESS + ENCRYPT_TMP_SECRET_OFFSET) +#define FLAGS_UPDATE_EXT() PARTN_IS_EXT(PART_UPDATE) +#endif + +#ifdef NVM_FLASH_WRITEONCE +#include +#include +static uint8_t NVM_CACHE[NVM_CACHE_SIZE] __attribute__((aligned(16))); + +int RAMFUNCTION hal_trailer_write(uint32_t addr, uint8_t val) { + uint32_t addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1)); + uint32_t addr_off = addr & (WOLFBOOT_SECTOR_SIZE - 1); + int ret = 0; + XMEMCPY(NVM_CACHE, (void *)addr_align, WOLFBOOT_SECTOR_SIZE); + ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); + if (ret != 0) + return ret; + NVM_CACHE[addr_off] = val; + ret = hal_flash_write(addr_align, NVM_CACHE, WOLFBOOT_SECTOR_SIZE); + return ret; +} + +int RAMFUNCTION hal_set_partition_magic(uint32_t addr) +{ + uint32_t off = addr % NVM_CACHE_SIZE; + uint32_t base = addr - off; + int ret; + XMEMCPY(NVM_CACHE, (void *)base, NVM_CACHE_SIZE); + ret = hal_flash_erase(base, WOLFBOOT_SECTOR_SIZE); + if (ret != 0) + return ret; + XMEMCPY(NVM_CACHE + off, &wolfboot_magic_trail, sizeof(uint32_t)); + ret = hal_flash_write(base, NVM_CACHE, WOLFBOOT_SECTOR_SIZE); + return ret; +} + +#else +# define hal_trailer_write(addr, val) hal_flash_write(addr, (void *)&val, 1) +# define hal_set_partition_magic(addr) hal_flash_write(addr, (void*)&wolfboot_magic_trail, sizeof(uint32_t)); +#endif + +#if defined EXT_FLASH + + +static uint8_t* RAMFUNCTION get_trailer_at(uint8_t part, uint32_t at) +{ + if (part == PART_BOOT) { + if (FLAGS_BOOT_EXT()){ + ext_flash_check_read(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at), (void *)&ext_cache, sizeof(uint32_t)); + return (uint8_t *)&ext_cache; + } else { + return (void *)(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at)); + } + } + else if (part == PART_UPDATE) { + if (FLAGS_UPDATE_EXT()) { + ext_flash_check_read(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at), (void *)&ext_cache, sizeof(uint32_t)); + return (uint8_t *)&ext_cache; + } else { + return (void *)(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at)); + } + } else + return NULL; +} + +static void RAMFUNCTION set_trailer_at(uint8_t part, uint32_t at, uint8_t val) +{ + if (part == PART_BOOT) { + if (FLAGS_BOOT_EXT()) { + ext_flash_check_write(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at), (void *)&val, 1); + } else { + hal_trailer_write(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at), val); + } + } + else if (part == PART_UPDATE) { + if (FLAGS_UPDATE_EXT()) { + ext_flash_check_write(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at), (void *)&val, 1); + } else { + hal_trailer_write(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at), val); + } + } +} + +static void RAMFUNCTION set_partition_magic(uint8_t part) +{ + if (part == PART_BOOT) { + if (FLAGS_BOOT_EXT()) { + ext_flash_check_write(PART_BOOT_ENDFLAGS - sizeof(uint32_t), (void *)&wolfboot_magic_trail, sizeof(uint32_t)); + } else { + hal_set_partition_magic(PART_BOOT_ENDFLAGS - sizeof(uint32_t)); + } + } + else if (part == PART_UPDATE) { + if (FLAGS_UPDATE_EXT()) { + ext_flash_check_write(PART_UPDATE_ENDFLAGS - sizeof(uint32_t), (void *)&wolfboot_magic_trail, sizeof(uint32_t)); + } else { + hal_set_partition_magic(PART_UPDATE_ENDFLAGS - sizeof(uint32_t)); + } + } +} + +#else +static uint8_t* RAMFUNCTION get_trailer_at(uint8_t part, uint32_t at) +{ + if (part == PART_BOOT) + return (void *)(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at)); + else if (part == PART_UPDATE) { + return (void *)(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at)); + } else + return NULL; +} + +static void RAMFUNCTION set_trailer_at(uint8_t part, uint32_t at, uint8_t val) +{ + if (part == PART_BOOT) { + hal_trailer_write(PART_BOOT_ENDFLAGS - (sizeof(uint32_t) + at), val); + } + else if (part == PART_UPDATE) { + hal_trailer_write(PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + at), val); + } +} + +static void RAMFUNCTION set_partition_magic(uint8_t part) +{ + if (part == PART_BOOT) { + hal_set_partition_magic(PART_BOOT_ENDFLAGS - sizeof(uint32_t)); + } + else if (part == PART_UPDATE) { + hal_set_partition_magic(PART_UPDATE_ENDFLAGS - sizeof(uint32_t)); + } +} +#endif /* EXT_FLASH */ + + + +static uint32_t* RAMFUNCTION get_partition_magic(uint8_t part) +{ + return (uint32_t *)get_trailer_at(part, 0); +} + +static uint8_t* RAMFUNCTION get_partition_state(uint8_t part) +{ + return (uint8_t *)get_trailer_at(part, 1); +} + + +static void RAMFUNCTION set_partition_state(uint8_t part, uint8_t val) +{ + set_trailer_at(part, 1, val); +} + +static void RAMFUNCTION set_update_sector_flags(uint32_t pos, uint8_t val) +{ + set_trailer_at(PART_UPDATE, 2 + pos, val); +} + +static uint8_t* RAMFUNCTION get_update_sector_flags(uint32_t pos) +{ + return (uint8_t *)get_trailer_at(PART_UPDATE, 2 + pos); +} + +int RAMFUNCTION wolfBoot_set_partition_state(uint8_t part, uint8_t newst) +{ + uint32_t *magic; + uint8_t *state; + magic = get_partition_magic(part); + if (*magic != WOLFBOOT_MAGIC_TRAIL) + set_partition_magic(part); + state = get_partition_state(part); + if (*state != newst) + set_partition_state(part, newst); + return 0; +} + +int RAMFUNCTION wolfBoot_set_update_sector_flag(uint16_t sector, uint8_t newflag) +{ + uint32_t *magic; + uint8_t *flags; + uint8_t fl_value; + uint8_t pos = sector >> 1; + + magic = get_partition_magic(PART_UPDATE); + if (*magic != wolfboot_magic_trail) + set_partition_magic(PART_UPDATE); + + flags = get_update_sector_flags(pos); + if (sector == (pos << 1)) + fl_value = (*flags & 0xF0) | (newflag & 0x0F); + else + fl_value = ((newflag & 0x0F) << 4) | (*flags & 0x0F); + if (fl_value != *flags) + set_update_sector_flags(pos, fl_value); + return 0; +} + +int RAMFUNCTION wolfBoot_get_partition_state(uint8_t part, uint8_t *st) +{ + uint32_t *magic; + uint8_t *state; + magic = get_partition_magic(part); + if (*magic != WOLFBOOT_MAGIC_TRAIL) + return -1; + state = get_partition_state(part); + *st = *state; + return 0; +} + +int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag) +{ + uint32_t *magic; + uint8_t *flags; + uint8_t pos = sector >> 1; + magic = get_partition_magic(PART_UPDATE); + if (*magic != WOLFBOOT_MAGIC_TRAIL) + return -1; + flags = get_update_sector_flags(pos); + if (sector == (pos << 1)) + *flag = *flags & 0x0F; + else + *flag = (*flags & 0xF0) >> 4; + return 0; +} + +void RAMFUNCTION wolfBoot_erase_partition(uint8_t part) +{ + if (part == PART_BOOT) { + if (PARTN_IS_EXT(PART_BOOT)) { + ext_flash_unlock(); + ext_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS, WOLFBOOT_PARTITION_SIZE); + ext_flash_lock(); + } else { + hal_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS, WOLFBOOT_PARTITION_SIZE); + } + } + if (part == PART_UPDATE) { + if (PARTN_IS_EXT(PART_UPDATE)) { + ext_flash_unlock(); + ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); + ext_flash_lock(); + } else { + hal_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE); + } + } + if (part == PART_SWAP) { + if (PARTN_IS_EXT(PART_SWAP)) { + ext_flash_unlock(); + ext_flash_erase(WOLFBOOT_PARTITION_SWAP_ADDRESS, WOLFBOOT_SECTOR_SIZE); + ext_flash_lock(); + } else { + hal_flash_erase(WOLFBOOT_PARTITION_SWAP_ADDRESS, WOLFBOOT_SECTOR_SIZE); + } + } +} + +void RAMFUNCTION wolfBoot_update_trigger(void) +{ + uint8_t st = IMG_STATE_UPDATING; + +#ifdef FLAGS_HOME + /* Erase last sector of boot partition prior to + * setting the partition state. + */ + uint32_t last_sector = PART_UPDATE_ENDFLAGS - (PART_UPDATE_ENDFLAGS % WOLFBOOT_SECTOR_SIZE); + hal_flash_unlock(); + hal_flash_erase(last_sector, WOLFBOOT_SECTOR_SIZE); + hal_flash_lock(); +#endif + + if (FLAGS_UPDATE_EXT()) + { + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, st); + ext_flash_lock(); + } else { + hal_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, st); + hal_flash_lock(); + } +} + +void RAMFUNCTION wolfBoot_success(void) +{ + uint8_t st = IMG_STATE_SUCCESS; + if (FLAGS_BOOT_EXT()) + { + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_BOOT, st); + ext_flash_lock(); + } else { + hal_flash_unlock(); + wolfBoot_set_partition_state(PART_BOOT, st); + hal_flash_lock(); + } +#ifdef EXT_ENCRYPTED + wolfBoot_erase_encrypt_key(); +#endif +} + +uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr) +{ + uint8_t *p = haystack; + uint16_t len; + const volatile uint8_t *max_p = (haystack - IMAGE_HEADER_OFFSET) + IMAGE_HEADER_SIZE; + *ptr = NULL; + if (p > max_p) { + unit_dbg("Illegal address (too high)\n"); + return 0; + } + while ((p + 4) < max_p) { + if ((p[0] == 0) && (p[1] == 0)) { + unit_dbg("Explicit end of options reached\n"); + break; + } + if (*p == HDR_PADDING) { + /* Padding byte (skip one position) */ + p++; + continue; + } + /* Sanity check to prevent dereferencing unaligned half-words */ + if ((((unsigned long)p) & 0x01) != 0) { + p++; + continue; + } + len = p[2] | (p[3] << 8); + if ((4 + len) > (uint16_t)(IMAGE_HEADER_SIZE - IMAGE_HEADER_OFFSET)) { + unit_dbg("This field is too large (bigger than the space available in the current header)\n"); + break; + } + if (p + 4 + len > max_p) { + unit_dbg("This field is too large and would overflow the image header\n"); + break; + } + if ((p[0] | (p[1] << 8)) == type) { + *ptr = (p + 4); + return len; + } + p += 4 + len; + } + return 0; +} + +#ifdef EXT_FLASH +static uint8_t hdr_cpy[IMAGE_HEADER_SIZE]; +static uint32_t hdr_cpy_done = 0; +#endif + +uint32_t wolfBoot_get_blob_version(uint8_t *blob) +{ + uint32_t *version_field = NULL; + uint32_t *magic = NULL; + magic = (uint32_t *)blob; + if (*magic != WOLFBOOT_MAGIC) + return 0; + if (wolfBoot_find_header(blob + IMAGE_HEADER_OFFSET, HDR_VERSION, (void *)&version_field) == 0) + return 0; + if (version_field) + return *version_field; + return 0; +} + +uint32_t wolfBoot_get_image_version(uint8_t part) +{ + uint8_t *image = (uint8_t *)0x00000000; + if(part == PART_UPDATE) { + if (PARTN_IS_EXT(PART_UPDATE)) + { + #ifdef EXT_FLASH + ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE); + hdr_cpy_done = 1; + image = hdr_cpy; + #endif + } else { + image = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS; + } + } else if (part == PART_BOOT) { + if (PARTN_IS_EXT(PART_BOOT)) { + #ifdef EXT_FLASH + ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE); + hdr_cpy_done = 1; + image = hdr_cpy; + #endif + } else { + image = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS; + } + } + /* Don't check image against NULL to allow using address 0x00000000 */ + return wolfBoot_get_blob_version(image); +} + +uint16_t wolfBoot_get_image_type(uint8_t part) +{ + uint16_t *type_field = NULL; + uint8_t *image = NULL; + uint32_t *magic = NULL; + if(part == PART_UPDATE) { + if (PARTN_IS_EXT(PART_UPDATE)) + { + #ifdef EXT_FLASH + ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE); + hdr_cpy_done = 1; + image = hdr_cpy; + #endif + } else { + image = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS; + } + } else if (part == PART_BOOT) { + if (PARTN_IS_EXT(PART_BOOT)) { + #ifdef EXT_FLASH + ext_flash_check_read((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS, hdr_cpy, IMAGE_HEADER_SIZE); + hdr_cpy_done = 1; + image = hdr_cpy; + #endif + } else { + image = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS; + } + } + if (image) { + magic = (uint32_t *)image; + if (*magic != WOLFBOOT_MAGIC) + return 0; + if (wolfBoot_find_header(image + IMAGE_HEADER_OFFSET, HDR_IMG_TYPE, (void *)&type_field) == 0) + return 0; + if (type_field) + return *type_field; + } + return 0; +} + +#if defined(ARCH_AARCH64) || defined(DUALBANK_SWAP) +int wolfBoot_fallback_is_possible(void) +{ + uint32_t boot_v, update_v; + boot_v = wolfBoot_current_firmware_version(); + update_v = wolfBoot_update_firmware_version(); + if ((boot_v == 0) || (update_v == 0)) + return 0; + return 1; +} + +int wolfBoot_dualboot_candidate(void) +{ + int candidate = PART_BOOT; + int fallback_possible = 0; + uint32_t boot_v, update_v; + uint8_t p_state; + /* Find the candidate */ + boot_v = wolfBoot_current_firmware_version(); + update_v = wolfBoot_update_firmware_version(); + /* -1 means no images available */ + if ((boot_v == 0) && (update_v == 0)) + return -1; + + if (boot_v == 0) /* No primary image */ + candidate = PART_UPDATE; + else if ((boot_v > 0) && (update_v > 0)) { + fallback_possible = 1; + if (update_v > boot_v) + candidate = PART_UPDATE; + } + /* Check current status for failure (still in TESTING), and fall-back + * if an alternative is available + */ + if (fallback_possible && + (wolfBoot_get_partition_state(candidate, &p_state) == 0) && + (p_state == IMG_STATE_TESTING)) + { + wolfBoot_erase_partition(candidate); + candidate ^= 1; /* switch to other partition if available */ + } + return candidate; +} +#else +int wolfBoot_dualboot_candidate(void) { return 0; } +int wolfBoot_fallback_is_possible(void) +{ + if (wolfBoot_update_firmware_version() > 0) + return 1; + return 0; +} +#endif /* ARCH_AARCH64 || DUALBANK_SWAP */ + +#ifdef EXT_ENCRYPTED +#include "encrypt.h" +#ifndef EXT_FLASH +#error option EXT_ENCRYPTED requires EXT_FLASH +#endif + + + +#ifdef NVM_FLASH_WRITEONCE +#define ENCRYPT_CACHE NVM_CACHE +#else +static uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] __attribute__((aligned(32))); +#endif + +static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) +{ + uint32_t addr = ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint32_t addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1)); + uint32_t addr_off = addr & (WOLFBOOT_SECTOR_SIZE - 1); + int ret = 0; + hal_flash_unlock(); + XMEMCPY(ENCRYPT_CACHE, (void *)addr_align, WOLFBOOT_SECTOR_SIZE); + ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); + if (ret != 0) + return ret; + XMEMCPY(ENCRYPT_CACHE + addr_off, k, ENCRYPT_KEY_SIZE); + XMEMCPY(ENCRYPT_CACHE + addr_off + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE); + ret = hal_flash_write(addr_align, ENCRYPT_CACHE, WOLFBOOT_SECTOR_SIZE); + hal_flash_lock(); + return ret; +} + +int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce) +{ + hal_set_key(key, nonce); + return 0; +} + +int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce) +{ + uint8_t *mem = (uint8_t *)(ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS); + XMEMCPY(k, mem, ENCRYPT_KEY_SIZE); + XMEMCPY(nonce, mem + ENCRYPT_KEY_SIZE, ENCRYPT_NONCE_SIZE); + return 0; +} + +int RAMFUNCTION wolfBoot_erase_encrypt_key(void) +{ + uint8_t ff[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; + int i; + uint8_t *mem = (uint8_t *)ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS; + XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); + if (XMEMCMP(mem, ff, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE) != 0) + hal_set_key(ff, ff + ENCRYPT_KEY_SIZE); + return 0; +} + +#ifdef __WOLFBOOT + +static ChaCha chacha; +static int chacha_initialized = 0; +static uint8_t chacha_iv_nonce[ENCRYPT_NONCE_SIZE]; + +static int chacha_init(void) +{ + uint8_t *key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + ENCRYPT_TMP_SECRET_OFFSET); + uint8_t ff[ENCRYPT_KEY_SIZE]; + uint8_t *stored_nonce = key + ENCRYPT_KEY_SIZE; + + /* Check against 'all 0xff' or 'all zero' cases */ + XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE); + if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) + return -1; + XMEMSET(ff, 0x00, ENCRYPT_KEY_SIZE); + if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0) + return -1; + + XMEMCPY(chacha_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE); + wc_Chacha_SetKey(&chacha, key, ENCRYPT_KEY_SIZE); + chacha_initialized = 1; + return 0; +} + + +static inline uint8_t part_address(uintptr_t a) +{ + if ( 1 && +#if WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0 + (a >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && +#endif + (a <= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) + return PART_UPDATE; + if ( 1 && +#if WOLFBOOT_PARTITION_SWAP_ADDRESS != 0 + (a >= WOLFBOOT_PARTITION_SWAP_ADDRESS) && +#endif + (a <= WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + return PART_SWAP; + return PART_NONE; +} + +int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len) +{ + uint32_t iv_counter; + uint8_t block[ENCRYPT_BLOCK_SIZE]; + uint8_t part; + int sz = len; + uint32_t row_address = address, row_offset; + int i; + uint8_t enc_block[ENCRYPT_BLOCK_SIZE]; + row_offset = address & (ENCRYPT_BLOCK_SIZE - 1); + if (row_offset != 0) { + row_address = address & ~(ENCRYPT_BLOCK_SIZE - 1); + sz += ENCRYPT_BLOCK_SIZE - row_offset; + } + if (sz < ENCRYPT_BLOCK_SIZE) { + sz = ENCRYPT_BLOCK_SIZE; + } + if (!chacha_initialized) + if (chacha_init() < 0) + return -1; + part = part_address(address); + switch(part) { + case PART_UPDATE: + iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; + /* Do not encrypt last sectors */ + if (iv_counter >= (START_FLAGS_OFFSET - ENCRYPT_BLOCK_SIZE) / ENCRYPT_BLOCK_SIZE) { + return ext_flash_write(address, data, len); + } + break; + case PART_SWAP: + { + uint32_t row_number; + row_number = (address - WOLFBOOT_PARTITION_SWAP_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = row_number; + break; + } + default: + return -1; + } + if (sz > len) { + int step = ENCRYPT_BLOCK_SIZE - row_offset; + if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE) + return -1; + XMEMCPY(block + row_offset, data, step); + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); + wc_Chacha_Process(&chacha, enc_block, block, ENCRYPT_BLOCK_SIZE); + ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE); + address += step; + data += step; + sz -= step; + iv_counter++; + } + for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) { + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); + XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE); + wc_Chacha_Process(&chacha, ENCRYPT_CACHE + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE); + iv_counter++; + } + return ext_flash_write(address, ENCRYPT_CACHE, len); +} + +int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len) +{ + uint32_t iv_counter = 0; + uint8_t block[ENCRYPT_BLOCK_SIZE]; + uint8_t part; + int sz = len; + uint32_t row_address = address, row_offset; + int i; + + row_offset = address & (ENCRYPT_BLOCK_SIZE - 1); + if (row_offset != 0) { + row_address = address & ~(ENCRYPT_BLOCK_SIZE - 1); + sz += ENCRYPT_BLOCK_SIZE - row_offset; + } + if (sz < ENCRYPT_BLOCK_SIZE) { + sz = ENCRYPT_BLOCK_SIZE; + } + if (!chacha_initialized) + if (chacha_init() < 0) + return -1; + part = part_address(row_address); + switch(part) { + case PART_UPDATE: + iv_counter = (address - WOLFBOOT_PARTITION_UPDATE_ADDRESS) / ENCRYPT_BLOCK_SIZE; + /* Do not decrypt last sector */ + if (iv_counter >= (START_FLAGS_OFFSET - ENCRYPT_BLOCK_SIZE) / ENCRYPT_BLOCK_SIZE) { + return ext_flash_read(address, data, len); + } + break; + case PART_SWAP: + { + uint32_t row_number; + row_number = (address - WOLFBOOT_PARTITION_SWAP_ADDRESS) / ENCRYPT_BLOCK_SIZE; + iv_counter = row_number; + break; + } + default: + return -1; + } + if (sz > len) { + uint8_t dec_block[ENCRYPT_BLOCK_SIZE]; + int step = ENCRYPT_BLOCK_SIZE - row_offset; + if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE) + return -1; + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); + wc_Chacha_Process(&chacha, dec_block, block, ENCRYPT_BLOCK_SIZE); + XMEMCPY(data, dec_block + row_offset, step); + address += step; + data += step; + sz -= step; + iv_counter++; + } + if (ext_flash_read(address, data, sz) != sz) + return -1; + for (i = 0; i < sz / ENCRYPT_BLOCK_SIZE; i++) { + wc_Chacha_SetIV(&chacha, chacha_iv_nonce, iv_counter); + XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE); + wc_Chacha_Process(&chacha, data + (ENCRYPT_BLOCK_SIZE * i), block, ENCRYPT_BLOCK_SIZE); + iv_counter++; + } + return len; +} +#endif + +#endif /* EXT_ENCRYPTED */ + diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/nrf52.c b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/nrf52.c new file mode 100644 index 0000000..55d0e4d --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/nrf52.c @@ -0,0 +1,119 @@ +/* nrf52.c + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include "image.h" + +/* Assembly helpers */ +#define DMB() __asm__ volatile ("dmb") + + +/* Instantiation */ +#define CLOCK_CONTROL_BASE (0x40000000) +#define NVMC_BASE (0x4001E000) + + +/* Flash write/erase control */ +#define NVMC_CONFIG *((volatile uint32_t *)(NVMC_BASE + 0x504)) +#define NVMC_ERASEPAGE *((volatile uint32_t *)(NVMC_BASE + 0x508)) +#define NVMC_READY *((volatile uint32_t *)(NVMC_BASE + 0x400)) +#define NVMC_CONFIG_REN 0 +#define NVMC_CONFIG_WEN 1 +#define NVMC_CONFIG_EEN 2 + +#define FLASH_PAGE_SIZE (4096) + +/* Clock control */ +#define TASKS_HFCLKSTART *((volatile uint32_t *)(CLOCK_CONTROL_BASE + 0x000)) +#define TASKS_HFCLKSTOP *((volatile uint32_t *)(CLOCK_CONTROL_BASE + 0x004)) +#define TASKS_HFCLKSTARTED *((volatile uint32_t *)(CLOCK_CONTROL_BASE + 0x100)) + +static void RAMFUNCTION flash_wait_complete(void) +{ + while (NVMC_READY == 0) + ; +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int i = 0; + uint32_t *src, *dst; + + while (i < len) { + if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) { + src = (uint32_t *)data; + dst = (uint32_t *)address; + NVMC_CONFIG = NVMC_CONFIG_WEN; + flash_wait_complete(); + dst[i >> 2] = src[i >> 2]; + flash_wait_complete(); + i+=4; + } else { + uint32_t val; + uint8_t *vbytes = (uint8_t *)(&val); + int off = (address + i) - (((address + i) >> 2) << 2); + dst = (uint32_t *)(address - off); + val = dst[i >> 2]; + vbytes[off] = data[i]; + NVMC_CONFIG = NVMC_CONFIG_WEN; + flash_wait_complete(); + dst[i >> 2] = val; + flash_wait_complete(); + i++; + } + } + return 0; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ +} + +void RAMFUNCTION hal_flash_lock(void) +{ +} + + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + uint32_t end = address + len - 1; + uint32_t p; + for (p = address; p <= end; p += FLASH_PAGE_SIZE) { + NVMC_CONFIG = NVMC_CONFIG_EEN; + flash_wait_complete(); + NVMC_ERASEPAGE = p; + flash_wait_complete(); + } + return 0; +} + +void hal_init(void) +{ + TASKS_HFCLKSTART = 1; + while(TASKS_HFCLKSTARTED == 0) + ; +} + +void hal_prepare_boot(void) +{ + TASKS_HFCLKSTOP = 1; +} + diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.c b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.c new file mode 100644 index 0000000..0227353 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.c @@ -0,0 +1,123 @@ +/* spi_drv_nrf52.c + * + * Driver for the SPI back-end of the SPI_FLASH module. + * + * Example implementation for nrf52F4. + * + * Pinout: see spi_drv_nrf52.h + * + * Copyright (C) 2020 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include "spi_drv.h" +#include "spi_drv_nrf52.h" + +#define SPI0 (0x40003000) +#define SPI1 (0x40004000) +#define SPI2 (0x40023000) + +#define SPI SPI0 +#define SPI_TASKS_START *((volatile uint32_t *)(SPI + 0x10)) +#define SPI_TASKS_STOP *((volatile uint32_t *)(SPI + 0x14)) +#define SPI_EVENTS_ENDRX *((volatile uint32_t *)(SPI + 0x110)) +#define SPI_EVENTS_END *((volatile uint32_t *)(SPI + 0x118)) +#define SPI_EVENTS_ENDTX *((volatile uint32_t *)(SPI + 0x120)) +#define SPI_EV_RDY *((volatile uint32_t *)(SPI + 0x108)) +#define SPI_INTENSET *((volatile uint32_t *)(SPI + 0x304)) +#define SPI_INTENCLR *((volatile uint32_t *)(SPI + 0x308)) +#define SPI_ENABLE *((volatile uint32_t *)(SPI + 0x500)) +#define SPI_PSEL_SCK *((volatile uint32_t *)(SPI + 0x508)) +#define SPI_PSEL_MOSI *((volatile uint32_t *)(SPI + 0x50C)) +#define SPI_PSEL_MISO *((volatile uint32_t *)(SPI + 0x510)) +#define SPI_RXDATA *((volatile uint32_t *)(SPI + 0x518)) +#define SPI_TXDATA *((volatile uint32_t *)(SPI + 0x51C)) +#define SPI_FREQUENCY *((volatile uint32_t *)(SPI + 0x524)) +#define SPI_CONFIG *((volatile uint32_t *)(SPI + 0x554)) + +#define K125 0x02000000 +#define K250 0x04000000 +#define K500 0x08000000 +#define M1 0x10000000 +#define M2 0x20000000 +#define M4 0x40000000 +#define M8 0x80000000 + +void RAMFUNCTION spi_cs_off(int pin) +{ + GPIO_OUTSET = (1 << pin); + while ((GPIO_OUT & (1 << pin)) == 0) + ; +} + +void RAMFUNCTION spi_cs_on(int pin) +{ + GPIO_OUTCLR = (1 << pin); + while ((GPIO_OUT & (1 << pin)) != 0) + ; + +} + +uint8_t RAMFUNCTION spi_read(void) +{ + volatile uint32_t reg = SPI_EV_RDY; + while (!reg) + reg = SPI_EV_RDY; + reg = SPI_RXDATA; + SPI_EV_RDY = 0; + return reg; +} + +void RAMFUNCTION spi_write(const char byte) +{ + uint32_t reg; + SPI_EV_RDY = 0; + SPI_TXDATA = (uint32_t)byte; + reg = SPI_EV_RDY; + while (!reg) + reg = SPI_EV_RDY; +} + + +void spi_init(int polarity, int phase) +{ + static int initialized = 0; + if (!initialized) { + initialized++; + GPIO_PIN_CNF[SPI_CS_PIN] = GPIO_CNF_OUT; + GPIO_PIN_CNF[SPI_SCLK_PIN] = GPIO_CNF_OUT; + GPIO_PIN_CNF[SPI_MOSI_PIN] = GPIO_CNF_OUT; + GPIO_PIN_CNF[SPI_MISO_PIN] = GPIO_CNF_IN; + //GPIO_DIRSET = ((1 << SPI_CS_PIN) | (1 << SPI_SCLK_PIN) | (1 << SPI_MOSI_PIN)); + GPIO_OUTSET = (1 << SPI_CS_PIN); + GPIO_OUTCLR = (1 << SPI_MOSI_PIN) | (1 << SPI_SCLK_PIN); + + SPI_PSEL_MISO = SPI_MISO_PIN; + SPI_PSEL_MOSI = SPI_MOSI_PIN; + SPI_PSEL_SCK = SPI_SCLK_PIN; + + SPI_FREQUENCY = M1; + SPI_CONFIG = 0; /* mode 0,0 default */ + SPI_ENABLE = 1; + } +} + +void spi_release(void) +{ + +} diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.h b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.h new file mode 100644 index 0000000..330585c --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/libwolfboot/spi_drv_nrf52.h @@ -0,0 +1,54 @@ +/* spi_drv_nrf52.h + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef SPI_DRV_NRF52_H_INCLUDED +#define SPI_DRV_NRF52_H_INCLUDED +#include +/** SPI settings **/ + + +#define GPIO_BASE (0x50000000) +#define GPIO_OUT *((volatile uint32_t *)(GPIO_BASE + 0x504)) +#define GPIO_OUTSET *((volatile uint32_t *)(GPIO_BASE + 0x508)) +#define GPIO_OUTCLR *((volatile uint32_t *)(GPIO_BASE + 0x50C)) +#define GPIO_DIRSET *((volatile uint32_t *)(GPIO_BASE + 0x518)) +#define GPIO_PIN_CNF ((volatile uint32_t *)(GPIO_BASE + 0x700)) // Array + +#define GPIO_CNF_IN 0 +#define GPIO_CNF_OUT 3 + + +/* Pinout (P0.x) */ +#if 1 +#define SPI_CS_PIN 13 +#define SPI_MOSI_PIN 4 +#define SPI_MISO_PIN 5 +#define SPI_SCLK_PIN 30 +#endif +#if 0 +#define SPI_SCLK_PIN 5 +#define SPI_MISO_PIN 6 +#define SPI_MOSI_PIN 7 +#define SPI_CS_PIN 8 +#endif + + +#define SPI_CS_FLASH SPI_CS_PIN + + + +#endif diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/main.c b/riotOS-nrf52840dk-ble/nrf52-gatt-service/main.c new file mode 100644 index 0000000..9eacc14 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/main.c @@ -0,0 +1,105 @@ +/* main.c + * + * Copyright (C) 2021 wolfSSL Inc. + * + * wolfBoot fw-update example RIOT application, running on nRF52840. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + */ + +#include +#include "board.h" +#include "net/ipv6/addr.h" +#include "net/gnrc.h" +#include "net/gnrc/netif.h" +#include "shell.h" +#include "periph/flashpage.h" +#include "thread.h" +#include "periph/uart.h" +#include "periph/gpio.h" +#include "xtimer.h" +#include "ringbuffer.h" +#include "wolfboot/wolfboot.h" +#include "mutex.h" +#include "msg.h" + +#ifndef SHELL_BUFSIZE +#define SHELL_BUFSIZE (128U) +#endif + +#define DISPATCHER_PRIO (THREAD_PRIORITY_MAIN - 1) +#define BT_PRIO (THREAD_PRIORITY_MAIN - 1) + +#define MSGQ_SIZE (4) + +#define GPIO_WAKEUP GPIO_PIN(1,11) + + +// Globals +// +static mutex_t fwupdate_mutex = MUTEX_INIT; +static kernel_pid_t gatt_srv_pid; +static char gatt_srv_stack[THREAD_STACKSIZE_MAIN]; +static msg_t gatt_srv_msgq[MSGQ_SIZE]; +static xtimer_t quotes_msg_timer; + +#define STDIO_UART_DEV (UART_UNDEF) + + +static int cmd_info(int argc, char **argv) +{ + (void)argc; + (void)argv; + puts("GATT services for secure firmware updates\n"); + printf("You are running RIOT on %s.\n", RIOT_BOARD); + printf("This board features a %s MCU.\n", RIOT_MCU); + puts("Bootloader info: \n"); + printf(" - Running firmware version %08x\n", (unsigned)wolfBoot_current_firmware_version()); + printf(" - Update firmware version %08x\n", (unsigned)wolfBoot_update_firmware_version()); + puts("\n UART INFO:"); + printf(" - Available devices: %i\n", UART_NUMOF); + if (STDIO_UART_DEV != UART_UNDEF) { + printf(" - UART used for STDIO (the shell): UART_DEV(%i)\n\n", STDIO_UART_DEV); + } + return 0; +} + +void *gatt_srv(void*); +static const shell_command_t shell_commands[] = { + { "info", "device info", cmd_info }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + /* initialize UART */ + char line_buf[SHELL_BUFSIZE]; + + /* initialize GPIO WAKEUP */ + gpio_init(GPIO_WAKEUP, GPIO_OUT); + gpio_set(GPIO_WAKEUP); + + /* Gatt Server */ + gatt_srv_pid = thread_create(gatt_srv_stack, sizeof(gatt_srv_stack), + DISPATCHER_PRIO - 1, 0, gatt_srv, NULL, "BLE_gatt"); + + /* run the shell */ + shell_run(shell_commands, line_buf, SHELL_BUFSIZE); + return 0; +} + + + diff --git a/riotOS-nrf52840dk-ble/nrf52-gatt-service/nimble.inc.mk b/riotOS-nrf52840dk-ble/nrf52-gatt-service/nimble.inc.mk new file mode 100644 index 0000000..d391525 --- /dev/null +++ b/riotOS-nrf52840dk-ble/nrf52-gatt-service/nimble.inc.mk @@ -0,0 +1,21 @@ +# For nRF51-based targets, we need to reduce buffer sizes to make this test fit +# into RAM +# Note: as the CPU variable is not set at this point, we manually 'whitelist' +# all supported nrf51-boards here + +# Set the tests default configuration +APP_MTU ?= 5000 +APP_BUF_CHUNKSIZE ?= 250 # must be full divider of APP_MTU +APP_BUF_NUM ?= 3 +APP_CID ?= 0x0235 + +# Apply configuration values +CFLAGS += -DAPP_MTU=$(APP_MTU) +CFLAGS += -DAPP_BUF_CHUNKSIZE=$(APP_BUF_CHUNKSIZE) +CFLAGS += -DAPP_BUF_NUM=$(APP_BUF_NUM) +CFLAGS += -DAPP_NODENAME=$(APP_NODENAME) +CFLAGS += -DAPP_CID=$(APP_CID) + +# configure NimBLE +MSYS_CNT ?= 40 + diff --git a/riotOS-nrf52840dk-ble/wolfboot-config b/riotOS-nrf52840dk-ble/wolfboot-config new file mode 100644 index 0000000..da96ccc --- /dev/null +++ b/riotOS-nrf52840dk-ble/wolfboot-config @@ -0,0 +1,25 @@ +ARCH?=ARM +TARGET?=nrf52 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +CORTEX_M0?=0 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=0 +DUALBANK_SWAP?=0 +IMAGE_HEADER_SIZE?=256 +PKA?=0 +WOLFBOOT_PARTITION_SIZE?=0x40000 +WOLFBOOT_SECTOR_SIZE?=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x60000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xe0000 diff --git a/riotOS-nrf52840dk-ble/wolfboot.mk b/riotOS-nrf52840dk-ble/wolfboot.mk new file mode 100644 index 0000000..0648254 --- /dev/null +++ b/riotOS-nrf52840dk-ble/wolfboot.mk @@ -0,0 +1,71 @@ +ifdef WOLFBOOT_OFFSET + +WOLFBOOT:=$(abspath $(RIOTBASE)/../../wolfBoot/) +CFLAGS += -I$(WOLFBOOT)/include + +SIGNTOOL ?= python3 $(WOLFBOOT)/tools/keytools/sign.py +KEYGENTOOL ?= python3 $(WOLFBOOT)/tools/keytools/keygen.py + +BINFILE ?= $(BINDIR)/$(APPLICATION).bin +SIGN_BINFILE = $(BINDIR)/$(APPLICATION)_v5_signed.bin +WOLFBOOT_KEYFILE ?= $(WOLFBOOT)/ecc256.der +WOLFBOOT_BIN ?= $(WOLFBOOT)/wolfboot.bin + + + +export IMAGE_HDR_SIZE = 256 + +wolfboot-create-key: $(WOLFBOOT_KEYFILE) + +$(WOLFBOOT_KEYFILE): + make -C $(WOLFBOOT) clean + make -C $(WOLFBOOT) distclean + make -C $(WOLFBOOT) TARGET=nrf52 DEBUG=0 ecc256.der \ + +wolfboot: wolfboot-create-key link + @$(COLOR_ECHO) + @$(COLOR_ECHO) '$(COLOR_PURPLE)Re-linking for wolfBoot at $(WOLFBOOT_OFFSET)...$(COLOR_RESET)' + @$(COLOR_ECHO) + @$(COLOR_ECHO) 'sources:' + @$(COLOR_ECHO) $(SRC) + @$(COLOR_ECHO) + $(_LINK) $(LINKFLAGPREFIX)--defsym=_rom_start_addr="$$(($(WOLFBOOT_OFFSET) + $(IMAGE_HDR_SIZE)))" \ + $(LINKFLAGPREFIX)--defsym=length="$$(($(WOLFBOOT_PARTITION_SIZE) - $(IMAGE_HDR_SIZE)))" \ + $(LINKFLAGPREFIX)--defsym=image_header="$(IMAGE_HDR_SIZE)" -o $(ELFFILE) && \ + $(OBJCOPY) $(OFLAGS) -Obinary $(ELFFILE) $(BINFILE) && \ + $(SIGNTOOL) $(BINFILE) $(WOLFBOOT_KEYFILE) $(IMAGE_VERSION) $(WOLFBOOT_OFFSET) + @$(COLOR_ECHO) + @$(COLOR_ECHO) '$(COLOR_PURPLE)Signed with $(WOLFBOOT_KEYFILE) for version $(IMAGE_VERSION) \ + $(COLOR_RESET)' + @$(COLOR_ECHO) + + +$(WOLFBOOT_BIN): + @$(COLOR_ECHO) 'sources:' + @$(COLOR_ECHO) $(SRC) + @$(COLOR_ECHO) + make -C $(WOLFBOOT) clean + make -C $(WOLFBOOT) TARGET=nrf52 DEBUG=0 BOOT0_OFFSET=$(WOLFBOOT_OFFSET) \ + SIGN=ECC256 \ + WOLFBOOT_SECTOR_SIZE=0x1000 \ + WOLFBOOT_PARTITION_SIZE=0x40000 \ + WOLFBOOT_PARTITION_BOOT_ADDRESS=0x20000 \ + WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x60000 \ + WOLFBOOT_PARTITION_SWAP_ADDRESS=0xE0000 \ + wolfboot.bin + +.PHONY: wolfboot-flash-bootloader wolfboot-flash + +wolfboot-flash-bootloader: HEXFILE = $(WOLFBOOT_BIN) +wolfboot-flash-bootloader: $(WOLFBOOT_BIN) $(FLASHDEPS) + sudo $(FLASHER) $(FFLAGS) -o 0x0 + +wolfboot-flash: HEXFILE = $(SIGN_BINFILE) +wolfboot-flash: wolfboot $(FLASHDEPS) wolfboot-flash-bootloader + sudo $(FLASHER) $(FFLAGS) -o $(WOLFBOOT_OFFSET) + +else +wolfboot: + $(Q)echo "error: wolfboot not supported on board $(BOARD)!" + $(Q)false +endif # WOLFBOOT_OFFSET