Added demo update via BLE (GATT server on nRF52/RiotOS)
parent
b09c5e1291
commit
9b6f5dfa36
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0f3956a14eec28e9a8ca007dd95be8c0f832096f
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Subproject commit bb1a3470d786b5b7e19c4fb59c5094d525862a4e
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
erase
|
||||
r
|
||||
q
|
||||
|
|
@ -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")
|
|
@ -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()
|
|
@ -0,0 +1,4 @@
|
|||
tar rem:3333
|
||||
file bin/nrf52840dk/nrf52-gatt-service.elf
|
||||
foc c
|
||||
|
|
@ -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
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
@ -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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 <inttypes.h>
|
||||
|
||||
#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
|
|
@ -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 <inttypes.h>
|
||||
|
||||
#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
|
|
@ -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
|
|
@ -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 <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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 <stddef.h>
|
||||
#include <string.h>
|
||||
#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 <stddef.h>
|
||||
#include <string.h>
|
||||
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 */
|
||||
|
|
@ -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 <stdint.h>
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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 <stdint.h>
|
||||
#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)
|
||||
{
|
||||
|
||||
}
|
|
@ -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 <stdint.h>
|
||||
/** 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
|
|
@ -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 <stdio.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue