blob: c0326260f8047fce21d749e21a1f279324d255fd [file] [log] [blame]
/*
* 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.
*/
#include "mcuboot_config/mcuboot_config.h"
#include <assert.h>
#include <stddef.h>
#include <inttypes.h>
#include <stdio.h>
#include <syscfg/syscfg.h>
#include <flash_map_backend/flash_map_backend.h>
#include <os/os.h>
#include <bsp/bsp.h>
#include <hal/hal_bsp.h>
#include <hal/hal_system.h>
#include <hal/hal_flash.h>
#include <hal/hal_watchdog.h>
#include <sysinit/sysinit.h>
#ifdef MCUBOOT_SERIAL
#include <hal/hal_gpio.h>
#include <hal/hal_nvreg.h>
#include <boot_serial/boot_serial.h>
#endif
#if defined(MCUBOOT_SERIAL)
#include <boot_uart/boot_uart.h>
#endif
#include <console/console.h>
#include "bootutil/image.h"
#include "bootutil/bootutil.h"
#include "bootutil/bootutil_log.h"
#include "bootutil/fault_injection_hardening.h"
#if MYNEWT_VAL(BOOT_CUSTOM_START)
void boot_custom_start(uintptr_t flash_base, struct boot_rsp *rsp);
#endif
#if defined(MCUBOOT_SERIAL)
#define BOOT_SERIAL_REPORT_DUR \
(MYNEWT_VAL(OS_CPUTIME_FREQ) / MYNEWT_VAL(BOOT_SERIAL_REPORT_FREQ))
#define BOOT_SERIAL_INPUT_MAX (512)
static int boot_read(char *str, int cnt, int *newline);
static const struct boot_uart_funcs boot_uart_funcs = {
.read = boot_read,
.write = boot_uart_write
};
static int
boot_read(char *str, int cnt, int *newline)
{
#if MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN) != -1
static uint32_t tick = 0;
if (tick == 0) {
/*
* Configure GPIO line as output. This is a pin we toggle at the
* given frequency.
*/
hal_gpio_init_out(MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN), 0);
tick = os_cputime_get32();
} else {
if (os_cputime_get32() - tick > BOOT_SERIAL_REPORT_DUR) {
hal_gpio_toggle(MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN));
tick = os_cputime_get32();
}
}
#endif
hal_watchdog_tickle();
return boot_uart_read(str, cnt, newline);
}
#if MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) != 0
/** Don't include null-terminator in comparison. */
#define BOOT_SERIAL_DETECT_STRING_LEN \
(sizeof MYNEWT_VAL(BOOT_SERIAL_DETECT_STRING) - 1)
/**
* Listens on the UART for the management string. Blocks for up to
* BOOT_SERIAL_DETECT_TIMEOUT milliseconds.
*
* @return true if the management string was received;
* false if the management string was not received
* before the UART listen timeout expired.
*/
static bool
serial_detect_uart_string(void)
{
uint32_t start_tick;
char buf[BOOT_SERIAL_DETECT_STRING_LEN] = { 0 };
char ch;
int newline;
int rc;
/* Calculate the timeout duration in OS cputime ticks. */
static const uint32_t timeout_dur =
MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) /
(1000.0 / MYNEWT_VAL(OS_CPUTIME_FREQ));
rc = boot_uart_open();
assert(rc == 0);
start_tick = os_cputime_get32();
while (1) {
/* Read a single character from the UART. */
rc = boot_uart_read(&ch, 1, &newline);
if (rc > 0) {
/* Eliminate the oldest character in the buffer to make room for
* the new one.
*/
memmove(buf, buf + 1, BOOT_SERIAL_DETECT_STRING_LEN - 1);
buf[BOOT_SERIAL_DETECT_STRING_LEN - 1] = ch;
/* If the full management string has been received, indicate that
* the serial boot loader should start.
*/
rc = memcmp(buf,
MYNEWT_VAL(BOOT_SERIAL_DETECT_STRING),
BOOT_SERIAL_DETECT_STRING_LEN);
if (rc == 0) {
boot_uart_close();
return true;
}
}
/* Abort the listen on timeout. */
if (os_cputime_get32() >= start_tick + timeout_dur) {
boot_uart_close();
return false;
}
}
}
#endif
static void
serial_boot_detect(void)
{
/*
* Read retained register and compare with expected magic value.
* If it matches, await for download commands from serial.
*/
#if MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX) != -1
if (hal_nvreg_read(MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX)) ==
MYNEWT_VAL(BOOT_SERIAL_NVREG_MAGIC)) {
hal_nvreg_write(MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX), 0);
goto serial_boot;
}
#endif
/*
* Configure a GPIO as input, and compare it against expected value.
* If it matches, await for download commands from serial.
*/
#if MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN) != -1
hal_gpio_init_in(MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN),
MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN_CFG));
if (hal_gpio_read(MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN)) ==
MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN_VAL)) {
goto serial_boot;
}
#endif
/*
* Listen for management pattern in UART input. If detected, await for
* download commands from serial.
*/
#if MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) != 0
if (serial_detect_uart_string()) {
goto serial_boot;
}
#endif
return;
serial_boot:
boot_uart_open();
boot_serial_start(&boot_uart_funcs);
assert(0);
}
#endif
/*
* Temporary flash_device_base() implementation.
*
* TODO: remove this when mynewt needs to support flash_device_base()
* for devices with nonzero base addresses.
*/
int flash_device_base(uint8_t fd_id, uintptr_t *ret)
{
*ret = 0;
return 0;
}
int
main(void)
{
struct boot_rsp rsp;
uintptr_t flash_base;
int rc;
fih_int fih_rc = FIH_FAILURE;
hal_bsp_init();
#if !MYNEWT_VAL(OS_SCHEDULING) && MYNEWT_VAL(WATCHDOG_INTERVAL)
rc = hal_watchdog_init(MYNEWT_VAL(WATCHDOG_INTERVAL));
assert(rc == 0);
#endif
#if defined(MCUBOOT_SERIAL) || defined(MCUBOOT_HAVE_LOGGING) || \
MYNEWT_VAL(CRYPTO) || MYNEWT_VAL(HASH)
/* initialize uart/crypto without os */
os_dev_initialize_all(OS_DEV_INIT_PRIMARY);
os_dev_initialize_all(OS_DEV_INIT_SECONDARY);
sysinit();
console_blocking_mode();
#if defined(MCUBOOT_SERIAL)
serial_boot_detect();
hal_timer_deinit(MYNEWT_VAL(OS_CPUTIME_TIMER_NUM));
#endif
#else
flash_map_init();
#endif
FIH_CALL(boot_go, fih_rc, &rsp);
if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
assert(fih_int_decode(fih_rc) == FIH_POSITIVE_VALUE);
FIH_PANIC;
}
rc = flash_device_base(rsp.br_flash_dev_id, &flash_base);
assert(rc == 0);
#if MYNEWT_VAL(BOOT_CUSTOM_START)
boot_custom_start(flash_base, &rsp);
#else
hal_bsp_deinit();
hal_system_start((void *)(flash_base + rsp.br_image_off +
rsp.br_hdr->ih_hdr_size));
#endif
return 0;
}