esp-idf/examples/system/ota/main/ota.c
Angus Gratton 67336672bd OTA: Improve verification of OTA image before writing, incl. secure boot
Verify 0xE9 magic byte on first write, verify entire image before
switching.

Enable verification for secure boot signature (was using invalid ifdef
guard)
2017-01-26 16:20:06 +11:00

314 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* OTA example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "nvs_flash.h"
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#define EXAMPLE_SERVER_IP CONFIG_SERVER_IP
#define EXAMPLE_SERVER_PORT CONFIG_SERVER_PORT
#define EXAMPLE_FILENAME CONFIG_EXAMPLE_FILENAME
#define BUFFSIZE 1024
#define TEXT_BUFFSIZE 1024
static const char *TAG = "ota";
/*an ota data write buffer ready to write to the flash*/
char ota_write_data[BUFFSIZE + 1] = { 0 };
/*an packet receive buffer*/
char text[BUFFSIZE + 1] = { 0 };
/* an image total length*/
int binary_file_length = 0;
/*socket id*/
int socket_id = -1;
char http_request[64] = {0};
/* operate handle : uninitialized value is zero ,every ota begin would exponential growth*/
esp_ota_handle_t out_handle = 0;
esp_partition_t operate_partition;
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
/*read buffer by byte still delim ,return read bytes counts*/
static int read_until(char *buffer, char delim, int len)
{
// /*TODO: delim check,buffer check,further: do an buffer length limited*/
int i = 0;
while (buffer[i] != delim && i < len) {
++i;
}
return i + 1;
}
/* resolve a packet from http socket
* return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body
* otherwise return false
* */
static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t out_handle)
{
/* i means current position */
int i = 0, i_read_len = 0;
while (text[i] != 0 && i < total_len) {
i_read_len = read_until(&text[i], '\n', total_len);
// if we resolve \r\n line,we think packet header is finished
if (i_read_len == 2) {
int i_write_len = total_len - (i + 2);
memset(ota_write_data, 0, BUFFSIZE);
/*copy first http packet body to write buffer*/
memcpy(ota_write_data, &(text[i + 2]), i_write_len);
esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err);
return false;
} else {
ESP_LOGI(TAG, "esp_ota_write header OK");
binary_file_length += i_write_len;
}
return true;
}
i += i_read_len;
}
return false;
}
bool connect_to_http_server()
{
ESP_LOGI(TAG, "Server IP: %s Server Port:%s", EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT);
sprintf(http_request, "GET %s HTTP/1.1\r\nHost: %s:%s \r\n\r\n", EXAMPLE_FILENAME, EXAMPLE_SERVER_IP, EXAMPLE_SERVER_PORT);
int http_connect_flag = -1;
struct sockaddr_in sock_info;
socket_id = socket(AF_INET, SOCK_STREAM, 0);
if (socket_id == -1) {
ESP_LOGE(TAG, "Create socket failed!");
return false;
}
// set connect info
memset(&sock_info, 0, sizeof(struct sockaddr_in));
sock_info.sin_family = AF_INET;
sock_info.sin_addr.s_addr = inet_addr(EXAMPLE_SERVER_IP);
sock_info.sin_port = htons(atoi(EXAMPLE_SERVER_PORT));
// connect to http server
http_connect_flag = connect(socket_id, (struct sockaddr *)&sock_info, sizeof(sock_info));
if (http_connect_flag == -1) {
ESP_LOGE(TAG, "Connect to server failed! errno=%d", errno);
close(socket_id);
return false;
} else {
ESP_LOGI(TAG, "Connected to server");
return true;
}
return false;
}
bool ota_init()
{
esp_err_t err;
const esp_partition_t *esp_current_partition = esp_ota_get_boot_partition();
if (esp_current_partition->type != ESP_PARTITION_TYPE_APP) {
ESP_LOGE(TAG, "Error esp_current_partition->type != ESP_PARTITION_TYPE_APP");
return false;
}
esp_partition_t find_partition;
memset(&operate_partition, 0, sizeof(esp_partition_t));
/*choose which OTA image should we write to*/
switch (esp_current_partition->subtype) {
case ESP_PARTITION_SUBTYPE_APP_FACTORY:
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
break;
case ESP_PARTITION_SUBTYPE_APP_OTA_0:
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1;
break;
case ESP_PARTITION_SUBTYPE_APP_OTA_1:
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
break;
default:
break;
}
find_partition.type = ESP_PARTITION_TYPE_APP;
const esp_partition_t *partition = esp_partition_find_first(find_partition.type, find_partition.subtype, NULL);
assert(partition != NULL);
memset(&operate_partition, 0, sizeof(esp_partition_t));
err = esp_ota_begin( partition, OTA_SIZE_UNKNOWN, &out_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed err=0x%x!", err);
return false;
} else {
memcpy(&operate_partition, partition, sizeof(esp_partition_t));
ESP_LOGI(TAG, "esp_ota_begin init OK");
return true;
}
return false;
}
void __attribute__((noreturn)) task_fatal_error()
{
ESP_LOGE(TAG, "Exiting task due to fatal error...");
close(socket_id);
(void)vTaskDelete(NULL);
while (1) {
;
}
}
void main_task(void *pvParameter)
{
esp_err_t err;
ESP_LOGI(TAG, "Starting OTA example...");
/* Wait for the callback to set the CONNECTED_BIT in the
event group.
*/
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");
/*connect to http server*/
if (connect_to_http_server()) {
ESP_LOGI(TAG, "Connected to http server");
} else {
ESP_LOGE(TAG, "Connect to http server failed!");
task_fatal_error();
}
int res = -1;
/*send GET request to http server*/
res = send(socket_id, http_request, strlen(http_request), 0);
if (res == -1) {
ESP_LOGE(TAG, "Send GET request to server failed");
task_fatal_error();
} else {
ESP_LOGI(TAG, "Send GET request to server succeeded");
}
if ( ota_init() ) {
ESP_LOGI(TAG, "OTA Init succeeded");
} else {
ESP_LOGE(TAG, "OTA Init failed");
task_fatal_error();
}
bool resp_body_start = false, flag = true;
/*deal with all receive packet*/
while (flag) {
memset(text, 0, TEXT_BUFFSIZE);
memset(ota_write_data, 0, BUFFSIZE);
int buff_len = recv(socket_id, text, TEXT_BUFFSIZE, 0);
if (buff_len < 0) { /*receive error*/
ESP_LOGE(TAG, "Error: receive data error! errno=%d", errno);
task_fatal_error();
} else if (buff_len > 0 && !resp_body_start) { /*deal with response header*/
memcpy(ota_write_data, text, buff_len);
resp_body_start = read_past_http_header(text, buff_len, out_handle);
} else if (buff_len > 0 && resp_body_start) { /*deal with response body*/
memcpy(ota_write_data, text, buff_len);
err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err);
task_fatal_error();
}
binary_file_length += buff_len;
ESP_LOGI(TAG, "Have written image length %d", binary_file_length);
} else if (buff_len == 0) { /*packet over*/
flag = false;
ESP_LOGI(TAG, "Connection closed, all packets received");
close(socket_id);
} else {
ESP_LOGE(TAG, "Unexpected recv result");
}
}
ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length);
if (esp_ota_end(out_handle) != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed!");
task_fatal_error();
}
err = esp_ota_set_boot_partition(&operate_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%x", err);
task_fatal_error();
}
ESP_LOGI(TAG, "Prepare to restart system!");
esp_restart();
return ;
}
void app_main()
{
nvs_flash_init();
initialise_wifi();
xTaskCreate(&main_task, "main_task", 8192, NULL, 5, NULL);
}