Merge pull request #136 from SuperHouse/feature/ota_improvements

OTA improvements, always enable OTA mode
This commit is contained in:
Angus Gratton 2016-05-28 12:46:58 +10:00
commit 4b39a0e6ca
27 changed files with 730 additions and 514 deletions

4
.gitmodules vendored
View file

@ -7,3 +7,7 @@
[submodule "extras/jsmn/jsmn"] [submodule "extras/jsmn/jsmn"]
path = extras/jsmn/jsmn path = extras/jsmn/jsmn
url = https://github.com/zserge/jsmn.git url = https://github.com/zserge/jsmn.git
[submodule "bootloader/rboot"]
path = bootloader/rboot
url = https://github.com/raburton/rboot.git

View file

@ -5,15 +5,12 @@ env:
OPENSDK_COMMIT=a48b12f OPENSDK_COMMIT=a48b12f
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}" CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
CROSS_BINDIR="${CROSS_ROOT}/bin" CROSS_BINDIR="${CROSS_ROOT}/bin"
ESPTOOL2_COMMIT=ec0e2c7
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
CROSS="ccache xtensa-lx106-elf-" CROSS="ccache xtensa-lx106-elf-"
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples" MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples"
PATH=${PATH}:${CROSS_BINDIR}
cache: cache:
directories: directories:
- ${CROSS_ROOT} - ${CROSS_ROOT}
- ${ESPTOOL2_DIR}
addons: addons:
apt: apt:
packages: packages:
@ -35,9 +32,9 @@ addons:
- python-serial - python-serial
- sed - sed
- git - git
- vim-common
before_install: before_install:
- utils/travis_build/install_esptool2.sh
- travis_wait 30 utils/travis_build/install_toolchain.sh - travis_wait 30 utils/travis_build/install_toolchain.sh
script: script:
@ -46,3 +43,5 @@ script:
- sed -i "s%#error%//#error%" include/ssid_config.h - sed -i "s%#error%//#error%" include/ssid_config.h
# Don't verbose-build all examples (too much output), only verbose-build errors # Don't verbose-build all examples (too much output), only verbose-build errors
- ( ${MAKE_CMD} ) || ( ${MAKE_CMD} V=1 ) - ( ${MAKE_CMD} ) || ( ${MAKE_CMD} V=1 )
# build bootloader
- make -C bootloader/

59
bootloader/Makefile Normal file
View file

@ -0,0 +1,59 @@
# This is a wrapper around the rboot makefile, which gives us the parameters
# we need to use rboot with esp-open-rtos.
#
# Use 'make bootloader' to build a custom bootloader.
#
# 'make flash' for any esp-open-rtos program will use the compiled
# bootloader if it exists, or a prebuilt bootloader if no custom
# bootloader was compiled.
#
# The wrapper means we don't require esptool2 in the build process, so we can just use
# esptool.py (still need xxd, grep, sed to generate the header - see below.)
BOOTLOADER_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../parameters.mk
all: $(FIRMWARE_DIR)/rboot.bin
rboot/Makefile:
$(error rboot git submodule is not checkedo out. Try running 'git submodule update --init --recursive')
$(FIRMWARE_DIR)/rboot.bin: $(BUILD_DIR)/rboot.elf $(FIRMWARE_DIR)
@echo "FW rboot.bin"
$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(BUILD_DIR)/
$(Q) mv $(BUILD_DIR)/0x00000.bin $@
# rboot generates this header using the 'esptool2 -header' option. To try and avoid
# esptool2 as a dependency, we try it here using grep, sed, xxd (all fairly common Unix tools)
$(BUILD_DIR)/rboot-hex2a.h: $(BUILD_DIR)/rboot-stage2a.elf $(BUILD_DIR)
@echo "Extracting stub image header..."
$(Q) xtensa-lx106-elf-objcopy $< --only-section .text -Obinary $(BUILD_DIR)/rboot-hex2a.bin
$(Q) xxd -i $(BUILD_DIR)/rboot-hex2a.bin > $@.in
$(Q) sed -i "s/unsigned char .\+\[\]/const uint8 _text_data[]/" $@.in
$(Q) sed -i "s/unsigned int .\+_len/const uint32 _text_len/" $@.in
$(Q) echo "const uint32 entry_addr = $$(xtensa-lx106-elf-objdump -f $< | grep 'start address' | grep -o '0x.\+');" >> $@.in
$(Q) echo "const uint32 _text_addr = 0x$$(xtensa-lx106-elf-objdump -h -j .text $< | grep ".text" | grep -o '401.....' | head -n1);" >> $@.in
$(Q) mv $@.in $@
RBOOT_BUILD_BASE="$(abspath $(BUILD_DIR))"
RBOOT_FW_BASE="$(abspath $(FIRMWARE_DIR))"
MAKE_VARS=RBOOT_EXTRA_INCDIR=$(BOOTLOADER_DIR) RBOOT_BUILD_BASE=$(RBOOT_BUILD_BASE) RBOOT_FW_BASE=$(RBOOT_FW_BASE)
$(BUILD_DIR)/rboot-stage2a.elf: $(BUILD_DIR)
$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf $(MAKE_VARS)
$(BUILD_DIR)/rboot.elf: $(BUILD_DIR)/rboot-hex2a.h
$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf $(MAKE_VARS)
$(BUILD_DIR) $(FIRMWARE_DIR):
mkdir -p $@
flash: $(FIRMWARE_DIR)/rboot.bin
$(Q) $(ESPTOOL) -p $(ESPPORT) -b $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $<
clean:
$(Q) rm -rf $(BUILD_DIR)
$(Q) rm -rf $(FIRMWARE_DIR)
.PHONY: all

12
bootloader/README.md Normal file
View file

@ -0,0 +1,12 @@
OTA Bootloader (rboot) source module and support files.
rboot is an open source bootloader by Richard Burton:
https://github.com/raburton/rboot
Can be used to build an esp-open-rtos compatible rboot bootloader. Run 'make bootloader' in this directory to compile a new bootloader.
Compiling a new bootloader is optional, there's a prebuilt one in the "firmware_prebuilt" directory that will be used if no new bootloader was compiled.
It is also possible to use rboot from upstream verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
See the contents of the 'rboot' directory for more information on rboot.

Binary file not shown.

Binary file not shown.

1
bootloader/rboot Submodule

@ -0,0 +1 @@
Subproject commit 30afbaa777e00abf9d7d469fb3345f118c4975c1

23
bootloader/rboot.h Normal file
View file

@ -0,0 +1,23 @@
/* rboot header overrides
This "wrapper" header contains default values for building rboot
on/for esp-open-rtos. It gets included both when building the
bootloader and when building extras/rboot-ota support. It includes
the default bootloader/rboot/rboot.h header via the gcc
include_next mechanism.
*/
#ifndef __RBOOT_H__
// Big flash support is required for esp-open-rtos (we use 8Mbit
// "slots" only.)
#define BOOT_BIG_FLASH
// enable 2 way communication between
// rBoot and the user app via the esp rtc data area
#define BOOT_RTC_ENABLED
// Call 'main' rboot.h to pick up defaults for other parameters
#include_next "rboot.h"
#endif

187
common.mk
View file

@ -20,156 +20,10 @@
# assume the 'root' directory (ie top of the tree) is the directory common.mk is in # assume the 'root' directory (ie top of the tree) is the directory common.mk is in
ROOT := $(dir $(lastword $(MAKEFILE_LIST))) ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
# include optional local overrides at the root level, then in program directory include $(ROOT)parameters.mk
#
# Create either of these files for local system overrides if possible,
# instead of editing this makefile directly.
-include $(ROOT)local.mk
-include local.mk
# Flash size in megabits
# Valid values are same as for esptool.py - 2,4,8,16,32
FLASH_SIZE ?= 16
# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
FLASH_MODE ?= qio
# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
FLASH_SPEED ?= 40
# Output directories to store intermediate compiled files
# relative to the program directory
BUILD_DIR ?= $(PROGRAM_DIR)build/
FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
# esptool.py from https://github.com/themadinventor/esptool
ESPTOOL ?= esptool.py
# serial port settings for esptool.py
ESPPORT ?= /dev/ttyUSB0
ESPBAUD ?= 115200
# set this to 0 if you don't need floating point support in printf/scanf
# this will save approx 14.5KB flash space and 448 bytes of statically allocated
# data RAM
#
# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
PRINTF_SCANF_FLOAT_SUPPORT ?= 1
# Set OTA to 1 to build an image that supports rBoot OTA bootloader
#
# Currently only works with 16mbit or more flash sizes, with 8mbit
# images for each "slot"
OTA ?= 0
ifeq ($(OTA),1)
# for OTA, we build a "SDK v1.2 bootloader" compatible image where everything is in
# one file (should work with the v1.2 binary bootloader, and the FOSS rBoot bootloader).
IMGTOOL ?= esptool2
# Tell C preprocessor that we're building for OTA
CPPFLAGS = -DOTA
endif
FLAVOR ?= release # or debug
# Compiler names, etc. assume gdb
CROSS ?= xtensa-lx106-elf-
# Path to the filteroutput.py tool
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
AR = $(CROSS)ar
CC = $(CROSS)gcc
CPP = $(CROSS)cpp
LD = $(CROSS)gcc
NM = $(CROSS)nm
C++ = $(CROSS)g++
SIZE = $(CROSS)size
OBJCOPY = $(CROSS)objcopy
OBJDUMP = $(CROSS)objdump
# Source components to compile and link. Each of these are subdirectories
# of the root, with a 'component.mk' file.
COMPONENTS ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core
# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
SDK_LIBS ?= main net80211 phy pp wpa
# open source libraries linked in
LIBS ?= hal gcc c
# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
OWN_LIBC ?= 1
# Note: you will need a recent esp
ENTRY_SYMBOL ?= call_user_start
# Set this to zero if you don't want individual function & data sections
# (some code may be slightly slower, linking will be slighty slower,
# but compiled code size will come down a small amount.)
SPLIT_SECTIONS ?= 1
# Set this to 1 to have all compiler warnings treated as errors (and stop the
# build). This is recommended whenever you are working on code which will be
# submitted back to the main project, as all submitted code will be expected to
# compile without warnings to be accepted.
WARNINGS_AS_ERRORS ?= 0
# Common flags for both C & C++_
C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
# Flags for C only
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
# Flags for C++ only
CXXFLAGS ?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
# these aren't technically preprocesor args, but used by all 3 of C, C++, assembler
CPPFLAGS += -mlongcalls -mtext-section-literals
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
ifeq ($(WARNINGS_AS_ERRORS),1)
C_CXX_FLAGS += -Werror
endif
ifeq ($(SPLIT_SECTIONS),1)
C_CXX_FLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,-gc-sections
endif
ifeq ($(FLAVOR),debug)
C_CXX_FLAGS += -g -O0
LDFLAGS += -g -O0
else ifeq ($(FLAVOR),sdklike)
# These are flags intended to produce object code as similar as possible to
# the output of the compiler used to build the SDK libs (for comparison of
# disassemblies when coding replacement routines). It is not normally
# intended to be used otherwise.
CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder
LDFLAGS += -O2
else
C_CXX_FLAGS += -g -O2
LDFLAGS += -g -O2
endif
GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
ifeq ($(GITSHORTREV),\"\")
GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
endif
CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
ifeq ($(OTA),0)
LINKER_SCRIPTS = $(ROOT)ld/nonota.ld
else
LINKER_SCRIPTS = $(ROOT)ld/ota.ld
endif
LINKER_SCRIPTS += $(ROOT)ld/common.ld $(ROOT)ld/rom.ld
####
#### no user configurable options below here
####
ifndef PROGRAM ifndef PROGRAM
$(error "Set the PROGRAM environment variable in your Makefile before including common.mk" $(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
endif endif
# hacky way to get a single space value # hacky way to get a single space value
@ -188,28 +42,7 @@ LIB_ARGS = $(addprefix -l,$(LIBS))
PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out
LDFLAGS += $(addprefix -T,$(LINKER_SCRIPTS)) LDFLAGS += $(addprefix -T,$(LINKER_SCRIPTS))
ifeq ($(OTA),0)
# for non-OTA, we create two different files for uploading into the flash
# these are the names and options to generate them
FW_ADDR_1 = 0x00000
FW_ADDR_2 = 0x20000
FW_FILE_1 = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_1).bin)
FW_FILE_2 = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_2).bin)
else
# for OTA, it's a single monolithic image
FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin) FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin)
endif
# firmware tool arguments
ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
IMGTOOL_FLASH_SIZE_2=256
IMGTOOL_FLASH_SIZE_4=512
IMGTOOL_FLASH_SIZE_8=1024
IMGTOOL_FLASH_SIZE_16=2048
IMGTOOL_FLASH_SIZE_32=4096
IMGTOOL_FLASH_SIZE=$(value IMGTOOL_FLASH_SIZE_$(FLASH_SIZE))
IMGTOOL_ARGS=-$(IMGTOOL_FLASH_SIZE) -$(FLASH_MODE) -$(FLASH_SPEED)
# Common include directories, shared across all "components" # Common include directories, shared across all "components"
# components will add their include directories to this argument # components will add their include directories to this argument
@ -235,7 +68,7 @@ Q := @
vecho := @echo vecho := @echo
endif endif
.PHONY: all clean debug_print .PHONY: all clean flash erase_flash
all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE) all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
@ -373,16 +206,14 @@ $(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR) $(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
$(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR) $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
$(Q) $(IMGTOOL) $(IMGTOOL_ARGS) -bin -boot2 $(PROGRAM_OUT) $(FW_FILE) .text .data .rodata $(vecho) "FW $@"
$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
ifeq ($(OTA),0)
flash: $(FW_FILE_1) $(FW_FILE_2)
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) $(FW_ADDR_2) $(FW_FILE_2) $(FW_ADDR_1) $(FW_FILE_1)
else
flash: $(FW_FILE) flash: $(FW_FILE)
$(vecho) "Flashing OTA image slot 0 (bootloader not updated)" $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE)
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
endif erase_flash:
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
size: $(PROGRAM_OUT) size: $(PROGRAM_OUT)
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT) $(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)

View file

@ -12,8 +12,6 @@
* Copyright (C) 2015 Superhouse Automation Pty Ltd * Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE * BSD Licensed as described in the file LICENSE
*/ */
#ifdef OTA
#define RBOOT_CONFIG_BASE (0x40200000 + 0x1000) #define RBOOT_CONFIG_BASE (0x40200000 + 0x1000)
#define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */ #define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */
@ -74,5 +72,3 @@ Cache_Read_Enable:
movi a0, cache_return_save /* restore a0 return address */ movi a0, cache_return_save /* restore a0 return address */
l32i a0, a0, 0 l32i a0, a0, 0
ret.n ret.n
#endif

View file

@ -1,5 +1,4 @@
PROGRAM=ota_basic PROGRAM=ota_basic
OTA=1 EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls
EXTRA_COMPONENTS=extras/rboot-ota
include ../../common.mk include ../../common.mk

View file

@ -1,23 +1,121 @@
/* A very simple OTA example /* A very simple OTA example
* *
* Binds a TCP socket, reads an image from it over TFTP and then flashes live. * Tries to run both a TFTP client and a TFTP server simultaneously, either will accept a TTP firmware and update it.
*
* Not a realistic OTA setup, this needs adapting (choose either client or server) before you'd want to use it.
* *
* For more information about esp-open-rtos OTA see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration * For more information about esp-open-rtos OTA see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
* *
* NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!! * NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!!
*/ */
#include <string.h>
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "esp/uart.h" #include "esp/uart.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "esp8266.h" #include "esp8266.h"
#include "ssid_config.h" #include "ssid_config.h"
#include "mbedtls/sha256.h"
#include "ota-tftp.h" #include "ota-tftp.h"
#include "rboot-integration.h"
#include "rboot.h"
#include "rboot-api.h" #include "rboot-api.h"
/* TFTP client will request this image filenames from this server */
#define TFTP_IMAGE_SERVER "192.168.1.23"
#define TFTP_IMAGE_FILENAME1 "firmware1.bin"
#define TFTP_IMAGE_FILENAME2 "firmware2.bin"
/* Output of the command 'sha256sum firmware1.bin' */
static const char *FIRMWARE1_SHA256 = "88199daff8b9e76975f685ec7f95bc1df3c61bd942a33a54a40707d2a41e5488";
/* Example function to TFTP download a firmware file and verify its SHA256 before
booting into it.
*/
static void tftpclient_download_and_verify_file1(int slot, rboot_config *conf)
{
printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME1, 1000, slot, NULL);
printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
if (res != 0) {
return;
}
printf("Looks valid, calculating SHA256...\n");
uint32_t length;
bool valid = rboot_verify_image(conf->roms[slot], &length, NULL);
static mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0);
valid = valid && rboot_digest_image(conf->roms[slot], length, (rboot_digest_update_fn)mbedtls_sha256_update, &ctx);
static uint8_t hash_result[32];
mbedtls_sha256_finish(&ctx, hash_result);
mbedtls_sha256_free(&ctx);
if(!valid)
{
printf("Not valid after all :(\n");
return;
}
printf("Image SHA256 = ");
valid = true;
for(int i = 0; i < sizeof(hash_result); i++) {
char hexbuf[3];
snprintf(hexbuf, 3, "%02x", hash_result[i]);
printf(hexbuf);
if(strncmp(hexbuf, FIRMWARE1_SHA256+i*2, 2))
valid = false;
}
printf("\n");
if(!valid) {
printf("Downloaded image SHA256 didn't match expected '%s'\n", FIRMWARE1_SHA256);
return;
}
printf("SHA256 Matches. Rebooting into slot %d...\n", slot);
rboot_set_current_rom(slot);
sdk_system_restart();
}
/* Much simpler function that just downloads a file via TFTP into an rboot slot.
(
*/
static void tftpclient_download_file2(int slot)
{
printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME2, 1000, slot, NULL);
printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
}
void tftp_client_task(void *pvParameters)
{
printf("TFTP client task starting...\n");
rboot_config conf;
conf = rboot_get_config();
int slot = (conf.current_rom + 1) % conf.count;
printf("Image will be saved in OTA slot %d.\n", slot);
if(slot == conf.current_rom) {
printf("FATAL ERROR: Only one OTA slot is configured!\n");
while(1) {}
}
/* Alternate between trying two different filenames. Probalby want to change this if making a practical
example!
Note: example will reboot into FILENAME1 if it is successfully downloaded, but FILENAME2 is ignored.
*/
while(1) {
tftpclient_download_and_verify_file1(slot, &conf);
vTaskDelay(5000 / portTICK_RATE_MS);
tftpclient_download_file2(slot);
vTaskDelay(5000 / portTICK_RATE_MS);
}
}
void user_init(void) void user_init(void)
{ {
uart_set_baud(0, 115200); uart_set_baud(0, 115200);
@ -38,5 +136,7 @@ void user_init(void)
sdk_wifi_set_opmode(STATION_MODE); sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&config); sdk_wifi_station_set_config(&config);
printf("Starting TFTP server...");
ota_tftp_init_server(TFTP_PORT); ota_tftp_init_server(TFTP_PORT);
xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 2048, NULL, 2, NULL);
} }

View file

@ -1,8 +1,9 @@
# Component makefile for extras/rboot-ota # Component makefile for extras/rboot-ota
INC_DIRS += $(rboot-ota_ROOT) # global include directories need to find rboot.h for integration, even
# when just including rboot-api.h :(
INC_DIRS += $(rboot-ota_ROOT) $(ROOT)bootloader $(ROOT)bootloader/rboot
# args for passing into compile rule generation
rboot-ota_SRC_DIR = $(rboot-ota_ROOT) rboot-ota_SRC_DIR = $(rboot-ota_ROOT)
$(eval $(call component_compile_rules,rboot-ota)) $(eval $(call component_compile_rules,rboot-ota))

View file

@ -22,12 +22,12 @@
#include <espressif/esp_system.h> #include <espressif/esp_system.h>
#include "ota-tftp.h" #include "ota-tftp.h"
#include "rboot.h"
#include "rboot-api.h" #include "rboot-api.h"
#define TFTP_FIRMWARE_FILE "firmware.bin" #define TFTP_FIRMWARE_FILE "firmware.bin"
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */ #define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
#define TFTP_OP_RRQ 1
#define TFTP_OP_WRQ 2 #define TFTP_OP_WRQ 2
#define TFTP_OP_DATA 3 #define TFTP_OP_DATA 3
#define TFTP_OP_ACK 4 #define TFTP_OP_ACK 4
@ -43,8 +43,9 @@
static void tftp_task(void *port_p); static void tftp_task(void *port_p);
static char *tftp_get_field(int field, struct netbuf *netbuf); static char *tftp_get_field(int field, struct netbuf *netbuf);
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len); static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb);
static err_t tftp_send_ack(struct netconn *nc, int block); static err_t tftp_send_ack(struct netconn *nc, int block);
static err_t tftp_send_rrq(struct netconn *nc, const char *filename);
static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg); static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
void ota_tftp_init_server(int listen_port) void ota_tftp_init_server(int listen_port)
@ -52,6 +53,62 @@ void ota_tftp_init_server(int listen_port)
xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL); xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL);
} }
err_t ota_tftp_download(const char *server, int port, const char *filename,
int timeout, int ota_slot, tftp_receive_cb receive_cb)
{
rboot_config rboot_config = rboot_get_config();
/* Validate the OTA slot parameter */
if(rboot_config.current_rom == ota_slot || rboot_config.count <= ota_slot)
{
return ERR_VAL;
}
/* This is all we need to know from the rboot config - where we need
to write data to.
*/
uint32_t flash_offset = rboot_config.roms[ota_slot];
struct netconn *nc = netconn_new (NETCONN_UDP);
err_t err;
if(!nc) {
return ERR_IF;
}
netconn_set_recvtimeout(nc, timeout);
/* try to bind our client port as our local port,
or keep trying the next 10 ports after it */
int local_port = port-1;
do {
err = netconn_bind(nc, IP_ADDR_ANY, ++local_port);
} while(err == ERR_USE && local_port < port + 10);
if(err) {
netconn_delete(nc);
return err;
}
ip_addr_t addr;
err = netconn_gethostbyname(server, &addr);
if(err) {
netconn_delete(nc);
return err;
}
netconn_connect(nc, &addr, port);
err = tftp_send_rrq(nc, filename);
if(err) {
netconn_delete(nc);
return err;
}
size_t received_len;
err = tftp_receive_data(nc, flash_offset, flash_offset+MAX_IMAGE_SIZE,
&received_len, &addr, port, receive_cb);
netconn_delete(nc);
return err;
}
static void tftp_task(void *listen_port) static void tftp_task(void *listen_port)
{ {
struct netconn *nc = netconn_new (NETCONN_UDP); struct netconn *nc = netconn_new (NETCONN_UDP);
@ -100,7 +157,7 @@ static void tftp_task(void *listen_port)
/* check mode */ /* check mode */
char *mode = tftp_get_field(1, netbuf); char *mode = tftp_get_field(1, netbuf);
if(!mode || strcmp("octet", mode)) { if(!mode || strcmp(TFTP_OCTET_MODE, mode)) {
tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary"); tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary");
free(mode); free(mode);
netbuf_delete(netbuf); netbuf_delete(netbuf);
@ -133,7 +190,8 @@ static void tftp_task(void *listen_port)
/* Finished WRQ phase, start TFTP data transfer */ /* Finished WRQ phase, start TFTP data transfer */
size_t received_len; size_t received_len;
int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len); netconn_set_recvtimeout(nc, 10000);
int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len, NULL, 0, NULL);
netconn_disconnect(nc); netconn_disconnect(nc);
printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len); printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len);
@ -182,20 +240,48 @@ static char *tftp_get_field(int field, struct netbuf *netbuf)
return result; return result;
} }
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len) #define TFTP_TIMEOUT_RETRANSMITS 10
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb)
{ {
*received_len = 0; *received_len = 0;
const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */ const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
uint32_t start_offs = write_offs; uint32_t start_offs = write_offs;
int block = 1; int block = 1;
struct netbuf *netbuf; struct netbuf *netbuf = 0;
int retries = TFTP_TIMEOUT_RETRANSMITS;
while(1) while(1)
{ {
netconn_set_recvtimeout(nc, 10000); if(peer_addr) {
netconn_disconnect(nc);
}
err_t err = netconn_recv(nc, &netbuf); err_t err = netconn_recv(nc, &netbuf);
if(peer_addr) {
if(netbuf) {
/* For TFTP server, the UDP connection is already established. But for client,
we don't know what port the server is using until we see the first data
packet - so we connect here.
*/
netconn_connect(nc, netbuf_fromaddr(netbuf), netbuf_fromport(netbuf));
peer_addr = 0;
} else {
/* Otherwise, temporarily re-connect so we can send errors */
netconn_connect(nc, peer_addr, peer_port);
}
}
if(err == ERR_TIMEOUT) { if(err == ERR_TIMEOUT) {
if(retries-- > 0 && block > 1) {
/* Retransmit the last ACK, wait for repeat data block.
This doesn't work for the first block, have to time out and start again. */
tftp_send_ack(nc, block-1);
continue;
}
tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout"); tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout");
return ERR_TIMEOUT; return ERR_TIMEOUT;
} }
@ -225,6 +311,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
} }
} }
/* Reset retry count if we got valid data */
retries = TFTP_TIMEOUT_RETRANSMITS;
if(write_offs % SECTOR_SIZE == 0) { if(write_offs % SECTOR_SIZE == 0) {
sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE); sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE);
} }
@ -281,7 +370,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
it so the client gets an indication if things were successful. it so the client gets an indication if things were successful.
*/ */
const char *err = "Unknown validation error"; const char *err = "Unknown validation error";
if(!rboot_verify_image(start_offs, *received_len, &err)) { uint32_t image_length;
if(!rboot_verify_image(start_offs, &image_length, &err)
|| image_length != *received_len) {
tftp_send_error(nc, TFTP_ERR_ILLEGAL, err); tftp_send_error(nc, TFTP_ERR_ILLEGAL, err);
return ERR_VAL; return ERR_VAL;
} }
@ -293,6 +384,11 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
return ack_err; return ack_err;
} }
// Make sure ack was successful before calling callback.
if(receive_cb) {
receive_cb(*received_len);
}
if(len < DATA_PACKET_SZ) { if(len < DATA_PACKET_SZ) {
return ERR_OK; return ERR_OK;
} }
@ -325,3 +421,17 @@ static void tftp_send_error(struct netconn *nc, int err_code, const char *err_ms
netconn_send(nc, err); netconn_send(nc, err);
netbuf_delete(err); netbuf_delete(err);
} }
static err_t tftp_send_rrq(struct netconn *nc, const char *filename)
{
struct netbuf *rrqbuf = netbuf_new();
uint16_t *rrqdata = (uint16_t *)netbuf_alloc(rrqbuf, 4 + strlen(filename) + strlen(TFTP_OCTET_MODE));
rrqdata[0] = htons(TFTP_OP_RRQ);
char *rrq_filename = (char *)&rrqdata[1];
strcpy(rrq_filename, filename);
strcpy(rrq_filename + strlen(filename) + 1, TFTP_OCTET_MODE);
err_t err = netconn_send(nc, rrqbuf);
netbuf_delete(rrqbuf);
return err;
}

View file

@ -1,5 +1,10 @@
#ifndef _OTA_TFTP_H #ifndef _OTA_TFTP_H
#define _OTA_TFTP_H #define _OTA_TFTP_H
#include "lwip/err.h"
typedef void (*tftp_receive_cb)(size_t bytes_received);
/* TFTP Server OTA Support /* TFTP Server OTA Support
* *
* To use, call ota_tftp_init_server() which will start the TFTP server task * To use, call ota_tftp_init_server() which will start the TFTP server task
@ -30,6 +35,21 @@
*/ */
void ota_tftp_init_server(int listen_port); void ota_tftp_init_server(int listen_port);
/* Attempt to make a TFTP client connection and download the specified filename.
'timeout' is in milliseconds, and is timeout for any UDP exchange
_not_ the entire download.
Returns 0 on success, LWIP err.h values for errors.
Does not change the current firmware slot, or reboot.
receive_cb: called repeatedly after each successful packet that
has been written to flash and ACKed. Can pass NULL to omit.
*/
err_t ota_tftp_download(const char *server, int port, const char *filename,
int timeout, int ota_slot, tftp_receive_cb receive_cb);
#define TFTP_PORT 69 #define TFTP_PORT 69
#endif #endif

View file

@ -4,9 +4,11 @@
// richardaburton@gmail.com // richardaburton@gmail.com
// See license.txt for license terms. // See license.txt for license terms.
// OTA code based on SDK sample from Espressif. // OTA code based on SDK sample from Espressif.
//
// esp-open-rtos additions Copyright 2016 Angus Gratton
////////////////////////////////////////////////// //////////////////////////////////////////////////
#include <rboot.h> #include <rboot-api.h>
#include <string.h> #include <string.h>
//#include <c_types.h> //#include <c_types.h>
//#include <spi_flash.h> //#include <spi_flash.h>
@ -209,6 +211,164 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
} }
#endif #endif
/* NOTE: Functions below here were added for esp-open-rtos only */
uint32_t rboot_get_slot_offset(uint8_t slot) {
rboot_config conf;
conf = rboot_get_config();
if (slot >= conf.count) return (uint32_t)-1;
return conf.roms[slot];
}
/* Structures for parsing the rboot OTA image format */
typedef struct __attribute__((packed)) {
uint8_t magic;
uint8_t section_count;
uint8_t val[2]; /* flash size & speed when placed @ offset 0, I thik ignored otherwise */
uint32_t entrypoint;
} image_header_t;
typedef struct __attribute__((packed)) {
uint32_t load_addr;
uint32_t length;
} section_header_t;
#define ROM_MAGIC_OLD 0xe9
#define ROM_MAGIC_NEW 0xea
bool rboot_verify_image(uint32_t initial_offset, uint32_t *image_length, const char **error_message)
{
uint32_t offset = initial_offset;
char *error = NULL;
RBOOT_DEBUG("rboot_verify_image: verifying image at 0x%08x\n", initial_offset);
if(offset % 4) {
error = "Unaligned flash offset";
goto fail;
}
/* sanity limit on how far we can read */
uint32_t end_limit = offset + 0x100000;
image_header_t image_header __attribute__((aligned(4)));
if(sdk_spi_flash_read(offset, &image_header, sizeof(image_header_t))) {
error = "Flash fail";
goto fail;
}
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
error = "Missing initial magic";
goto fail;
}
bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
int remaining_sections = image_header.section_count;
uint8_t checksum = CHKSUM_INIT;
while(remaining_sections > 0 && offset < end_limit)
{
/* read section header */
section_header_t header __attribute__((aligned(4)));
if(sdk_spi_flash_read(offset, &header, sizeof(section_header_t))) {
error = "Flash fail";
goto fail;
}
RBOOT_DEBUG("Found section @ 0x%08x (abs 0x%08x) length %d load 0x%08x\n", offset-initial_offset, offset, header.length, header.load_addr);
offset += sizeof(section_header_t);
if(header.length+offset > end_limit) {
break; /* sanity check: will reading section take us off end of expected flashregion? */
}
if(header.length % 4) {
error = "Header length not modulo 4";
goto fail;
}
if(!is_new_header) {
/* Add individual data of the section to the checksum. */
char chunk[16] __attribute__((aligned(4)));
for(int i = 0; i < header.length; i++) {
if(i % sizeof(chunk) == 0)
sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
checksum ^= chunk[i % sizeof(chunk)];
}
}
offset += header.length;
/* pad section to 4 byte align */
offset = (offset+3) & ~3;
remaining_sections--;
if(is_new_header) {
/* pad to a 16 byte offset */
offset = (offset+15) & ~15;
/* expect a v1.1 header here at start of "real" sections */
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD) {
error = "Bad second magic";
goto fail;
}
remaining_sections = image_header.section_count;
is_new_header = false;
}
}
if(remaining_sections > 0) {
error = "Image truncated";
goto fail;
}
/* add a byte for the image checksum (actually comes after the padding) */
offset++;
/* pad the image length to a 16 byte boundary */
offset = (offset+15) & ~15;
uint32_t read_checksum;
sdk_spi_flash_read(offset-1, &read_checksum, 1);
if((uint8_t)read_checksum != checksum) {
error = "Invalid checksum";
goto fail;
}
RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08x bytes.\n", offset - initial_offset);
if(image_length)
*image_length = offset - initial_offset;
return true;
fail:
if(error_message)
*error_message = error;
if(error) {
printf("%s: %s\n", __func__, error);
}
if(image_length)
*image_length = offset - initial_offset;
return false;
}
bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx)
{
uint8_t buf[32] __attribute__((aligned(4)));
for(int i = 0; i < image_length; i += sizeof(buf)) {
if(sdk_spi_flash_read(offset+i, buf, sizeof(buf)))
return false;
uint32_t digest_len = sizeof(buf);
if(i + digest_len > image_length)
digest_len = image_length - i;
update_fn(update_ctx, buf, digest_len);
}
return true;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -12,6 +12,7 @@
* @{ * @{
*/ */
#include <rboot-integration.h>
#include <rboot.h> #include <rboot.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -128,6 +129,49 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom);
bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode); bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode);
#endif #endif
/* ADDITIONS TO RBOOT-API FOR ESP-OPEN-RTOS FOLLOW */
/* Returns offset of given rboot slot, or (uint32_t)-1 if slot is invalid.
*/
uint32_t rboot_get_slot_offset(uint8_t slot);
/** @description Verify basic image parameters - headers, CRC8 checksum.
@param Offset of image to verify. Can use rboot_get_slot_offset() to find.
@param Optional pointer will return the total valid length of the image.
@param Optional pointer to a static human-readable error message if fails.
@return True for valid, False for invalid.
**/
bool rboot_verify_image(uint32_t offset, uint32_t *image_length, const char **error_message);
/* @description Digest callback prototype, designed to be compatible with
mbedtls digest functions (SHA, MD5, etc.)
See the ota_basic example to see an example of calculating the
SHA256 digest of an OTA image.
*/
typedef void (*rboot_digest_update_fn)(void * ctx, void *data, size_t data_len);
/** @description Calculate a digest over the image at the offset specified
@note This function is actually a generic function that hashes SPI
flash contents, doesn't know anything about rboot image format. Use
rboot_verify_image to ensure a well-formed OTA image.
@param offset - Starting offset of image to hash (should be 4 byte aligned.)
@param image_length - Length of image to hash (should be 4 byte aligned.)
@param update_fn - Function to update digest (see rboot_digest_update_fn for details)
@param update_ctx - Context argument for digest update function.
@return True if digest completes successfully, false if digest function failed part way through
**/
bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,14 +0,0 @@
#ifndef _RBOOT_CONFIG_H
/* rboot configuration parameters.
Must match the config in the compiler bootloader rboot.h. Values below are the rboot.h defaults.
Override rboot parameters by editing this file, or (much better
alternative) copy rboot-config.h to your program directory or
program/include/ directory, then edit it to override these values.
*/
#define RBOOT_MAX_ROMS 4
//#define RBOOT_CONFIG_CHECKSUM
#endif

View file

@ -1,132 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <espressif/spi_flash.h>
#include <espressif/esp_system.h>
#include <FreeRTOS.h>
#include <task.h>
#include <rboot.h>
#define ROM_MAGIC_OLD 0xe9
#define ROM_MAGIC_NEW 0xea
typedef struct __attribute__((packed)) {
uint8_t magic;
uint8_t section_count;
uint8_t val[2]; /* flash size & speed when placed @ offset 0, I think ignored otherwise */
uint32_t entrypoint;
} image_header_t;
typedef struct __attribute__((packed)) {
uint32_t load_addr;
uint32_t length;
} section_header_t;
// Check that a valid-looking rboot image is found at this offset on the flash, and
// takes up 'expected_length' bytes.
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
{
char *error = NULL;
if(offset % 4) {
error = "Unaligned flash offset";
goto fail;
}
uint32_t end_offset = offset + expected_length;
image_header_t image_header;
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
error = "Missing initial magic";
goto fail;
}
bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
int remaining_sections = image_header.section_count;
uint8_t checksum = CHKSUM_INIT;
while(remaining_sections > 0 && offset < end_offset)
{
/* read section header */
section_header_t header;
sdk_spi_flash_read(offset, (uint32_t *)&header, sizeof(section_header_t));
RBOOT_DEBUG("Found section @ 0x%08lx length 0x%08lx load 0x%08lx padded 0x%08lx\r\n", offset, header.length, header.load_addr, header.length);
offset += sizeof(section_header_t);
if(header.length+offset > end_offset) {
break; /* sanity check: will reading section take us off end of expected flashregion? */
}
if(!is_new_header) {
/* Add individual data of the section to the checksum. */
char chunk[16];
for(int i = 0; i < header.length; i++) {
if(i % sizeof(chunk) == 0)
sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
checksum ^= chunk[i % sizeof(chunk)];
}
}
offset += header.length;
/* pad section to 4 byte align */
offset = (offset+3) & ~3;
remaining_sections--;
if(is_new_header) {
/* pad to a 16 byte offset */
offset = (offset+15) & ~15;
/* expect a v1.1 header here at start of "real" sections */
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD) {
error = "Bad second magic";
goto fail;
}
remaining_sections = image_header.section_count;
is_new_header = false;
}
}
if(remaining_sections > 0) {
error = "Image truncated";
goto fail;
}
/* add a byte for the image checksum (actually comes after the padding) */
offset++;
/* pad the image length to a 16 byte boundary */
offset = (offset+15) & ~15;
uint8_t read_checksum;
sdk_spi_flash_read(offset-1, (uint32_t *)&read_checksum, 1); /* not sure if this will work */
if(read_checksum != checksum) {
error = "Invalid checksum";
goto fail;
}
if(offset != end_offset) {
error = "Wrong length";
goto fail;
}
RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08lx bytes.\r\n", expected_length);
return true;
fail:
if(error_message)
*error_message = error;
printf("%s: %s\r\n", __func__, error);
return false;
}

View file

@ -9,6 +9,10 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <task.h> #include <task.h>
/***************************************************
* Platform configuration definitions *
***************************************************/
#define uint8 uint8_t #define uint8 uint8_t
#define uint16 uint16_t #define uint16 uint16_t
#define uint32 uint32_t #define uint32 uint32_t
@ -28,7 +32,11 @@
#define RBOOT_DEBUG(f_, ...) #define RBOOT_DEBUG(f_, ...)
#endif #endif
// Check that a valid-looking rboot image is found at this offset on the flash, and /* Enable checksumming when writing out the config,
// takes up 'expected_length' bytes. so if the bootloader is built with checksumming then
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message); it will still work.
*/
#define BOOT_CONFIG_CHKSUM
#endif // __RBOOT_INTEGRATION_H__ #endif // __RBOOT_INTEGRATION_H__

View file

@ -1,111 +0,0 @@
#ifndef __RBOOT_H__
#define __RBOOT_H__
//////////////////////////////////////////////////
// rBoot open source boot loader for ESP8266.
// Copyright 2015 Richard A Burton
// richardaburton@gmail.com
// See license.txt for license terms.
//////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#define RBOOT_INTEGRATION
// uncomment to use only c code
// if you aren't using gcc you may need to do this
//#define BOOT_NO_ASM
// uncomment to have a checksum on the boot config
//#define BOOT_CONFIG_CHKSUM
// uncomment to enable big flash support (>1MB)
#define BOOT_BIG_FLASH
// uncomment to enable 2 way communication between
// rBoot and the user app via the esp rtc data area
#define BOOT_RTC_ENABLED
// uncomment to enable GPIO booting
//#define BOOT_GPIO_ENABLED
// uncomment to include .irom0.text section in the checksum
// roms must be built with esptool2 using -iromchksum option
//#define BOOT_IROM_CHKSUM
// uncomment to add a boot delay, allows you time to connect
// a terminal before rBoot starts to run and output messages
// value is in microseconds
//#define BOOT_DELAY_MICROS 2000000
// increase if required
#define MAX_ROMS 4
#define CHKSUM_INIT 0xef
#define SECTOR_SIZE 0x1000
#define BOOT_CONFIG_SECTOR 1
#define BOOT_CONFIG_MAGIC 0xe1
#define BOOT_CONFIG_VERSION 0x01
#define MODE_STANDARD 0x00
#define MODE_GPIO_ROM 0x01
#define MODE_TEMP_ROM 0x02
#define RBOOT_RTC_MAGIC 0x2334ae68
#define RBOOT_RTC_READ 1
#define RBOOT_RTC_WRITE 0
#define RBOOT_RTC_ADDR 64
#ifdef RBOOT_INTEGRATION
#include <rboot-integration.h>
#endif
/** @brief Structure containing rBoot configuration
* @note ROM addresses must be multiples of 0x1000 (flash sector aligned).
* Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will
* be memory mapped so ROM slots containing .irom0.text sections must
* remain below 0x100000. Slots beyond this will only be accessible via
* spi read calls, so use these for stored resources, not code. With
* BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so
* ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.
* @ingroup rboot
*/
typedef struct {
uint8 magic; ///< Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC
uint8 version; ///< Version of configuration structure - should be BOOT_CONFIG_VERSION
uint8 mode; ///< Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM)
uint8 current_rom; ///< Currently selected ROM (will be used for next standard boot)
uint8 gpio_rom; ///< ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM
uint8 count; ///< Quantity of ROMs available to boot
uint8 unused[2]; ///< Padding (not used)
uint32 roms[MAX_ROMS]; ///< Flash addresses of each ROM
#ifdef BOOT_CONFIG_CHKSUM
uint8 chksum; ///< Checksum of this configuration structure (if BOOT_CONFIG_CHKSUM defined)
#endif
} rboot_config;
#ifdef BOOT_RTC_ENABLED
/** @brief Structure containing rBoot status/control data
* @note This structure is used to, optionally, communicate between rBoot and
* the user app. It is stored in the ESP RTC data area.
* @ingroup rboot
*/
typedef struct {
uint32 magic; ///< Magic, identifies rBoot RTC data - should be RBOOT_RTC_MAGIC
uint8 next_mode; ///< The next boot mode, defaults to MODE_STANDARD - can be set to MODE_TEMP_ROM
uint8 last_mode; ///< The last (this) boot mode - can be MODE_STANDARD, MODE_GPIO_ROM or MODE_TEMP_ROM
uint8 last_rom; ///< The last (this) boot rom number
uint8 temp_rom; ///< The next boot rom number when next_mode set to MODE_TEMP_ROM
uint8 chksum; ///< Checksum of this structure this will be updated for you passed to the API
} rboot_rtc_data;
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -31,7 +31,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_erase_sector(uint16_t sec);
/* Write data to flash. /* Write data to flash.
des_addr is byte offset to write to. Should be 4-byte aligned. des_addr is byte offset to write to. Should be 4-byte aligned.
src is pointer to a buffer to read bytes from. src is pointer to a buffer to read bytes from. Should be 4-byte aligned.
size is length of buffer in bytes. Should be a multiple of 4. size is length of buffer in bytes. Should be a multiple of 4.
*/ */
sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, const void *src, uint32_t size); sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, const void *src, uint32_t size);
@ -39,7 +39,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, const void *src, uin
/* Read data from flash. /* Read data from flash.
src_addr is byte offset to read from. Should be 4-byte aligned. src_addr is byte offset to read from. Should be 4-byte aligned.
des is pointer to a buffer to read bytes into. des is pointer to a buffer to read bytes into. Should be 4-byte aligned.
size is number of bytes to read. Should be a multiple of 4. size is number of bytes to read. Should be a multiple of 4.
*/ */
sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, void *des, uint32_t size); sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, void *des, uint32_t size);

View file

@ -1,15 +0,0 @@
/* Memory layout for esp-open-rtos when not using OTA
(ie ROM bootloader only, no second stage rboot)
*/
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x20000 to leave space for bootloader RAM sections.
- Length is max 8Mbit of mappable flash, minus start offset
*/
irom0_0_seg : org = 0x40220000, len = (1M - 0x20000)
}

View file

@ -1,15 +0,0 @@
/* Memory layout for esp-open-rtos when using OTA second stage bootloader */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x2010 to create spacer for second stage bootloader image,
header.
- Length is max 8Mbit of mappable flash, minus start offset
*/
irom0_0_seg : org = 0x40202010, len = (1M - 0x2010)
}

View file

@ -1,7 +1,17 @@
/* /* Memory layout for esp-open-rtos when using OTA second stage bootloader */
* Common (OTA and non-OTA) parts for the esp-open-rtos Linker Script MEMORY
* {
*/ dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x2010 to create spacer for second stage bootloader image,
header.
- Length is max 8Mbit of mappable flash, minus start offset
*/
irom0_0_seg : org = 0x40202010, len = (1M - 0x2010)
}
/* FreeRTOS memory management functions /* FreeRTOS memory management functions

141
parameters.mk Normal file
View file

@ -0,0 +1,141 @@
# Parameters for the esp-open-rtos make process
#
# You can edit this file to change parameters, but a better option is
# to create a local.mk file and add overrides there. The local.mk file
# can be in the root directory, or the program directory alongside the
# Makefile, or both.
#
-include $(ROOT)local.mk
-include local.mk
# Flash size in megabits
# Valid values are same as for esptool.py - 2,4,8,16,32
FLASH_SIZE ?= 16
# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
FLASH_MODE ?= qio
# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
FLASH_SPEED ?= 40
# Output directories to store intermediate compiled files
# relative to the program directory
BUILD_DIR ?= $(PROGRAM_DIR)build/
FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
# esptool.py from https://github.com/themadinventor/esptool
ESPTOOL ?= esptool.py
# serial port settings for esptool.py
ESPPORT ?= /dev/ttyUSB0
ESPBAUD ?= 115200
# firmware tool arguments
ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
# set this to 0 if you don't need floating point support in printf/scanf
# this will save approx 14.5KB flash space and 448 bytes of statically allocated
# data RAM
#
# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
PRINTF_SCANF_FLOAT_SUPPORT ?= 1
FLAVOR ?= release # or debug
# Compiler names, etc. assume gdb
CROSS ?= xtensa-lx106-elf-
# Path to the filteroutput.py tool
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
AR = $(CROSS)ar
CC = $(CROSS)gcc
CPP = $(CROSS)cpp
LD = $(CROSS)gcc
NM = $(CROSS)nm
C++ = $(CROSS)g++
SIZE = $(CROSS)size
OBJCOPY = $(CROSS)objcopy
OBJDUMP = $(CROSS)objdump
# Source components to compile and link. Each of these are subdirectories
# of the root, with a 'component.mk' file.
COMPONENTS ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core
# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
SDK_LIBS ?= main net80211 phy pp wpa
# open source libraries linked in
LIBS ?= hal gcc c
# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
OWN_LIBC ?= 1
# Note: you will need a recent esp
ENTRY_SYMBOL ?= call_user_start
# Set this to zero if you don't want individual function & data sections
# (some code may be slightly slower, linking will be slighty slower,
# but compiled code size will come down a small amount.)
SPLIT_SECTIONS ?= 1
# Set this to 1 to have all compiler warnings treated as errors (and stop the
# build). This is recommended whenever you are working on code which will be
# submitted back to the main project, as all submitted code will be expected to
# compile without warnings to be accepted.
WARNINGS_AS_ERRORS ?= 0
# Common flags for both C & C++_
C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
# Flags for C only
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
# Flags for C++ only
CXXFLAGS ?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
# these aren't all technically preprocesor args, but used by all 3 of C, C++, assembler
CPPFLAGS += -mlongcalls -mtext-section-literals
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
ifeq ($(WARNINGS_AS_ERRORS),1)
C_CXX_FLAGS += -Werror
endif
ifeq ($(SPLIT_SECTIONS),1)
C_CXX_FLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,-gc-sections
endif
ifeq ($(FLAVOR),debug)
C_CXX_FLAGS += -g -O0
LDFLAGS += -g -O0
else ifeq ($(FLAVOR),sdklike)
# These are flags intended to produce object code as similar as possible to
# the output of the compiler used to build the SDK libs (for comparison of
# disassemblies when coding replacement routines). It is not normally
# intended to be used otherwise.
CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder
LDFLAGS += -O2
else
C_CXX_FLAGS += -g -O2
LDFLAGS += -g -O2
endif
GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
ifeq ($(GITSHORTREV),\"\")
GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
endif
CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
LINKER_SCRIPTS += $(ROOT)ld/program.ld $(ROOT)ld/rom.ld
# rboot firmware binary paths for flashing
RBOOT_BIN = $(ROOT)bootloader/firmware/rboot.bin
RBOOT_PREBUILT_BIN = $(ROOT)bootloader/firmware_prebuilt/rboot.bin
RBOOT_CONF = $(ROOT)bootloader/firmware_prebuilt/blank_config.bin
# if a custom bootloader hasn't been compiled, use the
# prebuilt binary from the source tree
ifeq (,$(wildcard $(RBOOT_BIN)))
RBOOT_BIN=$(RBOOT_PREBUILT_BIN)
endif

View file

@ -1,15 +0,0 @@
#!/bin/bash
set -uv
# Called by Travis to install esptool2 to generate OTA-compatible build
# images
if test -f ${ESPTOOL2_DIR}/esptool2; then
echo "Using cached esptool2"
exit 0
fi
git clone https://github.com/raburton/esptool2 ${ESPTOOL2_DIR}
cd ${ESPTOOL2_DIR}
git reset --hard ${ESPTOOL2_COMMIT}
make