292 lines
7.9 KiB
C
292 lines
7.9 KiB
C
|
/****************************************************************************
|
||
|
* apps/boot/mcuboot/mcuboot_agent_main.c
|
||
|
*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||
|
* contributor license agreements. See the NOTICE file distributed with
|
||
|
* this work for additional information regarding copyright ownership. The
|
||
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||
|
* "License"); you may not use this file except in compliance with the
|
||
|
* License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
* License for the specific language governing permissions and limitations
|
||
|
* under the License.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Included Files
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include <nuttx/config.h>
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include <arpa/inet.h>
|
||
|
#include <net/if.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <sys/boardctl.h>
|
||
|
|
||
|
#include <bootutil/bootutil_public.h>
|
||
|
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
#include "netutils/md5.h"
|
||
|
#endif
|
||
|
#include "netutils/netlib.h"
|
||
|
#include "netutils/webclient.h"
|
||
|
|
||
|
#include "flash_map_backend/flash_map_backend.h"
|
||
|
#include "sysflash/sysflash.h"
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Preprocessor Definitions
|
||
|
****************************************************************************/
|
||
|
|
||
|
#define DL_BUFFER_SIZE CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_BUFFER_SIZE
|
||
|
|
||
|
#define DL_UPDATE_URL CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_UPDATE_URL
|
||
|
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
# define MD5_HASH_LENGTH 32
|
||
|
# define MD5_DIGEST_LENGTH 16
|
||
|
# define MD5_EXPECTED_HASH CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_MD5_HASH
|
||
|
#endif
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Types
|
||
|
****************************************************************************/
|
||
|
|
||
|
struct download_context_s
|
||
|
{
|
||
|
FAR const struct flash_area *fa;
|
||
|
uint32_t fa_offset;
|
||
|
ssize_t image_size;
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
MD5_CTX md5_ctx;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Data
|
||
|
****************************************************************************/
|
||
|
|
||
|
static char g_iobuffer[DL_BUFFER_SIZE];
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Functions
|
||
|
****************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: header_callback
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int header_callback(FAR const char *line, bool truncated,
|
||
|
FAR void *arg)
|
||
|
{
|
||
|
FAR struct download_context_s *ctx = (FAR struct download_context_s *)arg;
|
||
|
|
||
|
if (ctx->image_size == -1)
|
||
|
{
|
||
|
char *pos = strstr(line, "Content-Length:");
|
||
|
if (pos != NULL)
|
||
|
{
|
||
|
long i;
|
||
|
int errcode;
|
||
|
|
||
|
i = strtol(pos + sizeof("Content-Length:"), NULL, 10);
|
||
|
errcode = errno;
|
||
|
if (errcode == ERANGE)
|
||
|
{
|
||
|
fprintf(stderr, "Error reading \"Content-Length\": %s\n",
|
||
|
strerror(errcode));
|
||
|
}
|
||
|
|
||
|
printf("Firmware Update size: %ld bytes\n", i);
|
||
|
|
||
|
ctx->image_size = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: sink_callback
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int sink_callback(FAR char **buffer, int offset, int datend,
|
||
|
FAR int *buflen, FAR void *arg)
|
||
|
{
|
||
|
FAR struct download_context_s *ctx = (FAR struct download_context_s *)arg;
|
||
|
uint32_t length = datend - offset;
|
||
|
|
||
|
if (length > 0)
|
||
|
{
|
||
|
uint32_t progress;
|
||
|
|
||
|
flash_area_write(ctx->fa, ctx->fa_offset, &((*buffer)[offset]),
|
||
|
length);
|
||
|
|
||
|
ctx->fa_offset += length;
|
||
|
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
md5_update(&ctx->md5_ctx,
|
||
|
(FAR const unsigned char *)&((*buffer)[offset]),
|
||
|
length);
|
||
|
#endif
|
||
|
|
||
|
progress = (ctx->fa_offset * 100) / ctx->image_size;
|
||
|
|
||
|
printf("Received: %-8" PRIu32 " of %zd bytes [%" PRIu32 "%%]\n",
|
||
|
ctx->fa_offset, ctx->image_size, progress);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: download_firmware_image
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int download_firmware_image(FAR const char *url)
|
||
|
{
|
||
|
int ret;
|
||
|
struct webclient_context client_ctx;
|
||
|
struct download_context_s dl_ctx;
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
uint8_t digest[MD5_DIGEST_LENGTH];
|
||
|
char hash[MD5_HASH_LENGTH + 1];
|
||
|
int i;
|
||
|
#endif
|
||
|
|
||
|
dl_ctx.fa = NULL;
|
||
|
dl_ctx.fa_offset = 0;
|
||
|
dl_ctx.image_size = -1;
|
||
|
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
md5_init(&dl_ctx.md5_ctx);
|
||
|
#endif
|
||
|
|
||
|
webclient_set_defaults(&client_ctx);
|
||
|
client_ctx.method = "GET";
|
||
|
client_ctx.buffer = g_iobuffer;
|
||
|
client_ctx.buflen = sizeof(g_iobuffer);
|
||
|
client_ctx.header_callback = header_callback;
|
||
|
client_ctx.header_callback_arg = &dl_ctx;
|
||
|
client_ctx.sink_callback = sink_callback;
|
||
|
client_ctx.sink_callback_arg = &dl_ctx;
|
||
|
client_ctx.url = url;
|
||
|
|
||
|
ret = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(0), &dl_ctx.fa);
|
||
|
if (ret != OK)
|
||
|
{
|
||
|
fprintf(stderr, "Failed to open flash area\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = webclient_perform(&client_ctx);
|
||
|
if (ret != OK)
|
||
|
{
|
||
|
fprintf(stderr, "webclient_perform failed with %d\n", ret);
|
||
|
|
||
|
goto exit_close;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE_DL_VERIFY_MD5
|
||
|
md5_final(digest, &dl_ctx.md5_ctx);
|
||
|
|
||
|
for (i = 0; i < MD5_DIGEST_LENGTH; i++)
|
||
|
{
|
||
|
sprintf(&hash[i * 2], "%02x", digest[i]);
|
||
|
}
|
||
|
|
||
|
hash[MD5_HASH_LENGTH] = '\0';
|
||
|
|
||
|
if (strncmp(hash, MD5_EXPECTED_HASH, sizeof(hash)) != 0)
|
||
|
{
|
||
|
fprintf(stderr, "Download checksum verification failure:\n");
|
||
|
fprintf(stderr, "%s Expected\n", MD5_EXPECTED_HASH);
|
||
|
fprintf(stderr, "%s Got\n", hash);
|
||
|
|
||
|
ret = ERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Download checksum verification successful!\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
exit_close:
|
||
|
flash_area_close(dl_ctx.fa);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Public Functions
|
||
|
****************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
* mcuboot_agent_main
|
||
|
****************************************************************************/
|
||
|
|
||
|
int main(int argc, FAR char *argv[])
|
||
|
{
|
||
|
int ret;
|
||
|
FAR const char *url;
|
||
|
|
||
|
printf("MCUboot Update Agent example\n");
|
||
|
|
||
|
if (argc > 1)
|
||
|
{
|
||
|
url = argv[1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
url = DL_UPDATE_URL;
|
||
|
}
|
||
|
|
||
|
if (url[0] == '\0')
|
||
|
{
|
||
|
fprintf(stderr, "Usage: mcuboot_agent <URL>\n");
|
||
|
|
||
|
return ERROR;
|
||
|
}
|
||
|
|
||
|
printf("Downloading from %s\n", url);
|
||
|
|
||
|
ret = download_firmware_image(url);
|
||
|
if (ret != OK)
|
||
|
{
|
||
|
fprintf(stderr, "Application Image download failure.\n");
|
||
|
fprintf(stderr, "Update aborted.\n");
|
||
|
|
||
|
return ERROR;
|
||
|
}
|
||
|
|
||
|
printf("Application Image successfully downloaded!\n");
|
||
|
|
||
|
boot_set_pending_multi(0, 0);
|
||
|
|
||
|
printf("Requested update for next boot. Restarting...\n");
|
||
|
|
||
|
fflush(stdout);
|
||
|
fflush(stderr);
|
||
|
|
||
|
usleep(1000);
|
||
|
|
||
|
boardctl(BOARDIOC_RESET, EXIT_SUCCESS);
|
||
|
|
||
|
return OK;
|
||
|
}
|