Files
banban/ql-config/soc_platform/preboot_boot2/apps/boot2/main.c
T

699 lines
21 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <byteorder.h>
#include "flasher_guard.h"
#include "syscall-arom.h"
#include "reg.h"
#include "secureboot.h"
#include "pmic.h"
#include "sys-boot-mode.h"
#include "sys_close_all.h"
#include "smux.h"
#include "etimer.h"
#include "clock.h"
#include "arom-crane.h"
#include "pmu.h"
#include "property.h"
#include "transfer-control.h"
#include "loader.h"
#include "ptable.h"
#include "cpu.h"
#include "flash.h"
#include "boot_img.h"
#include "heapmem.h"
#include "fastboot.h"
#include "tbbr_img_def.h"
#include "fip.h"
#include "nvctr_flash.h"
#include "usb_serial.h"
/* Log configuration */
#include "log.h"
#define LOG_MODULE "Boot2"
#define LOG_LEVEL LOG_LEVEL_MAIN
/*---------------------------------------------------------------------------*/
#define QUEC_CUST_FEATURE 1
#if (defined(QUEC_CUST_FEATURE)&& (AROM_VERSION == AROM_VER_2022_11_06_CRANEL_CR5))
#define DOWNLOAD_CMD_DETECT_WAIT_MSECONDS 1200 /* ms */
#else
#define DOWNLOAD_CMD_DETECT_WAIT_MSECONDS 1000 /* ms */
#endif
#define AROM_VERSION_CRANEW_SERIES (AROM_VERSION == AROM_VER_2022_07_28_CRANEW_Z2)
#define AROM_VERSION_CRANE_SERIES (AROM_VERSION == AROM_VER_2020_07_30_CRANEGM_A0 || \
AROM_VERSION == AROM_VER_2021_10_16_CRANEL_A0 || \
AROM_VERSION == AROM_VER_2022_11_06_CRANEL_CR5 || \
AROM_VERSION == AROM_VER_2024_04_30_LAPWING_CR5 || \
AROM_VERSION == AROM_VER_2022_11_11_CRANEG_CR5)
/*---------------------------------------------------------------------------*/
/**
* Preamble message type
*/
enum {
PREAMBLE_DOWNLOAD,
PREAMBLE_PRODUCTION
};
/*---------------------------------------------------------------------------*/
#if AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
static void crane_boot2_init(void);
static void boot2_preamble_callback(const char *preamble, smux_dev_t dev);
static void transfer_arom_do_download(void);
static int do_boot_pz1(void);
#if (defined(BOARD_CRANE) || defined(BOARD_CRANEW_MCU))
static char *boardid_string;
#endif
#endif
/*---------------------------------------------------------------------------*/
extern unsigned char __lzma_decode_start, __lzma_decode_size;
/*---------------------------------------------------------------------------*/
#if (defined(BOARD_CRANE) || defined(BOARD_CRANEW_MCU))
#define PROP_NAME_BOARDID "bl2.boardid"
#endif
/*---------------------------------------------------------------------------*/
__attribute__((constructor)) static void
platform_init(void)
{
#if defined(CPU_MODEL_CRANE) || defined(CPU_MODEL_CRANEW_MCU)
#if AROM_VERSION == AROM_VER_2022_08_08_CRANEW_CR5
sys_boot_mode_set(SYS_BOOT_MODE_NORMAL);
#else
char r_cmd;
pmic_init();
pmic_get_restart_cmd(&r_cmd);
if(r_cmd == RESTART_CMD_USB_DOWNLOAD) {
sys_boot_mode_set(SYS_BOOT_MODE_FORCE_DOWNLOAD);
pmic_set_restart_cmd(RESTART_CMD_NORMAL_POWERON);
} else if(r_cmd == RESTART_CMD_FORCE_NORMAL_POWERON) {
sys_boot_mode_set(SYS_BOOT_MODE_NORMAL);
pmic_set_restart_status(RESTART_STATUS_NORMAL_POWERON);
} else if(r_cmd == RESTART_CMD_PRODUCTION) {
/* The production mode is detected from the pmic restart cmd register */
sys_boot_mode_set(SYS_BOOT_MODE_PRODUCTION);
#if defined(BOARD_CRANE)
/*
* For Crane chips, Watch1 CR5 never reach here.
*
* Keep the pmic restart cmd to "production mode" to ensure compatiblity
* with already mass-producrd products. On those chips, CP(CR5), acting as
* host, checks pmic restart cmd register to process different bootmode and
* then set it to the right value.
*/
#else
/*
* For Watch1 MCU.
*
* Set the pmic restart cmd register to "normal poweron" for the next
* restart. The production mode flag will be passed to the Boot33 by the
* property of bl2.bootmode, and the subsequent software will never use
* the pmic register to determine the boot mode.
*/
pmic_set_restart_cmd(RESTART_CMD_NORMAL_POWERON);
#endif /* BOARD_CRANE */
} else {
/* RESTART_CMD_NORMAL_POWERON and unknown cmd */
sys_boot_mode_set(SYS_BOOT_MODE_TRY_DOWNLOAD);
/* set_restart_status or set_restart_cmd will after cmd detect finished. */
}
#endif
#endif
}
/*---------------------------------------------------------------------------*/
#if 0
static void
display_binary(void *data, size_t len)
{
#define BYTE_COUNT_EACH_LINE 8
static const char hex[] = "0123456789ABCDEF";
static const char seperator = ',';
unsigned char *p = (unsigned char *)data;
char stash[BYTE_COUNT_EACH_LINE * (2 + 2 + 1) + 1];
size_t j = 0;
for(size_t i = 0; i < len; i++) {
stash[j++] = '0';
stash[j++] = 'x';
stash[j++] = hex[(p[i] >> 4) & 0xF];
stash[j++] = hex[p[i] & 0xF];
stash[j++] = seperator;
if(j == sizeof(stash) - 1) {
stash[j] = '\0';
LOG_INFO("%s\n", stash);
j = 0;
}
}
if(j) {
stash[j] = '\0';
LOG_INFO("%s\n", stash);
}
}
#endif
/*---------------------------------------------------------------------------*/
#define USB2_REG0B_DEFAULT_VAL 0x3000
/*---------------------------------------------------------------------------*/
static void
usb_check_reg_before_init(void)
{
uint32_t reg_usb2_0A, reg_usb2_0B;
uint32_t reg_usb2_0A_val, reg_usb2_0B_val;
reg_usb2_0A = UTMI_BASE + UTMI_USB2_REG0A;
reg_usb2_0B = UTMI_BASE + UTMI_USB2_REG0B;
reg_usb2_0A_val = readl(reg_usb2_0A);
reg_usb2_0B_val = readl(reg_usb2_0B);
if(reg_usb2_0A_val != 0 || reg_usb2_0B_val != 0x3000) {
writel(0, reg_usb2_0A);
writel(USB2_REG0B_DEFAULT_VAL, reg_usb2_0B);
}
}
/*---------------------------------------------------------------------------*/
static void
set_preamble_mode(void)
{
usb_check_reg_before_init();
smux_running_mode_set(SMUX_MODE_PREAMBLE);
}
/*---------------------------------------------------------------------------*/
#if AROM_VERSION_CRANE_SERIES
static bool
flash_check_qspi2_nor_device(const void *data, size_t size)
{
size_t flash_info_size = 0;
struct flash_info *info;
do {
info = (struct flash_info *)data;
if(info->magic != FLASH_INFO_MAGIC) {
return false;
}
size_t len = sizeof(struct flash_info);
len += (info->compatible_device_num - 1) * sizeof(struct compatible_device);
flash_info_size += len;
data = (char *)data + len;
if(flash_info_size > size) {
return false;
}
if(!strcmp(info->device_type, FLASH_TYPE_QSPI2_NOR)) {
return true;
}
} while(info->chained);
return false;
}
#endif
/*---------------------------------------------------------------------------*/
static int
do_boot(void)
{
smux_running_mode_set(SMUX_MODE_BYPASS);
#if AROM_VERSION_CRANE_SERIES
/* asic-sv evb case */
if(do_boot_pz1()) {
LOG_ERR("can not boot pz1!\n");
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
extern char __lzma_alloc_base, __lzma_alloc_size;
uint8_t *stash = (uint8_t *)((uintptr_t)&__lzma_alloc_base + SZALLOC_OFFSET);
ssize_t stash_size = (uintptr_t)&__lzma_alloc_size - SZALLOC_OFFSET;
stash_size = sb_volume_read("flashinfo", stash, stash_size);
if(stash_size < 0) {
LOG_ERR("can not read volume 'flashinfo'\n");
set_preamble_mode();
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
#ifdef USE_ARB
if(is_valid_fip(stash)) {
int ret = fip_get_image(stash, BL2_IMAGE_ID, (uint64_t *)&stash, NULL);
if(ret < 0) {
LOG_ERR("Get 'flashinfo' failed!\n");
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
}
#endif
if(flash_check_qspi2_nor_device(stash, stash_size)) {
slt_do_boot_prepare();
LOG_INFO("Refresh volume for booting from qspi2.\n");
sb_volume_exit();
if(sb_volume_init() < 0) {
LOG_ERR("Initialize volume partition failed!\n");
set_preamble_mode();
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
/* Reload new ptable */
if(ptable_loader()) {
LOG_ERR("Load ptable failed!\n");
set_preamble_mode();
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
}
#elif AROM_VERSION_CRANEW_SERIES
/* asic-sv evb case */
if(do_boot_pz1()) {
LOG_ERR("can not boot pz1!\n");
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
#endif
if(!do_boot_cb()) {
set_preamble_mode();
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
}
return 0;
}
#if AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
/*
* Normally it should not exceed 500ms, but it depends on Windows, which is not
* a real time OS, and it has been observed that the enumeration exceeds 3
* second.
*
* Note: If USB is connected but driver is not installed, it will wait for
* tiemout before continuing to execute.
*/
#define USB_ENUMERATION_TIMEOUT_MS 5000
/*---------------------------------------------------------------------------*/
PROCESS(boot2_process, "Crane boot2 process");
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(boot2_process, ev, data)
{
(void)data;
static struct etimer timer;
static bool timer_started;
static clock_time_t t, timeout;
static bool usb_enumeration_stage;
PROCESS_BEGIN();
/* setup a periodic timer that expires after 1 seconds. */
LOG_PRINT("Waiting for %d ms to detect download command.\n", DOWNLOAD_CMD_DETECT_WAIT_MSECONDS);
smux_register_preamble_callback(boot2_preamble_callback);
#ifdef BOARD_CRANE
uart_rx_fixup();
#endif
smux_running_mode_set(SMUX_MODE_PREAMBLE);
/*
* Set smux mode to PREAMBLE will flush logs, taking up approximately 200ms.
* This time should be excluded from the wait download cmd period. So start
* timer after set smux mode.
*/
t = clock_time();
timeout = t + USB_ENUMERATION_TIMEOUT_MS;
usb_enumeration_stage = true;
while(1) {
/* Wait for USB enumeration to complete before waiting for download cmd */
while(usb_enumeration_stage) {
t = clock_time();
if((t > timeout) || usb_enumeration_is_complete()) {
if(t > timeout) {
/* USB enumeration failed, try to continue */
LOG_WARN("Usb enumeration timeout.\n");
}
/*
* USB enumeration complete or timeout, start timer to wait download
* cmd.
*/
usb_enumeration_stage = false;
etimer_set(&timer, CLOCK_SECOND * DOWNLOAD_CMD_DETECT_WAIT_MSECONDS / 1000);
timer_started = true;
break;
} else {
PROCESS_PAUSE();
}
}
PROCESS_YIELD();
switch(ev) {
case PROCESS_EVENT_TIMER:
if(smux_running_mode_get() == SMUX_MODE_PREAMBLE) {
smux_running_mode_set(SMUX_MODE_BYPASS);
char r_cmd;
pmic_get_restart_cmd(&r_cmd);
pmic_set_restart_status(r_cmd);
LOG_INFO("No download command detected, continue booting.\n");
if(timer_started) {
etimer_stop(&timer);
}
if(!do_boot()) {
timer_started = false;
} else {
set_preamble_mode();
}
}
break;
case PREAMBLE_DOWNLOAD:
/* stop timer */
if(timer_started) {
etimer_stop(&timer);
timer_started = false;
}
break;
case PREAMBLE_PRODUCTION:
LOG_INFO("PROD CMD ACCEPTED!\n");
smux_running_mode_set(SMUX_MODE_BYPASS);
smux_use_device(SMUX_DEV_UART);
#if defined(BOARD_CRANE)
/*
* For Crane chips.
*
* Watch1 CR5 should never reach here becasue the production mode flag is
* set by the host through sw_scratch register and detected in Bootrom,
* not by preamble.
*
* For other Crane chips, set the pmic restart cmd to "production mode" to
* ensure compatiblity with already mass-producrd products. On those chips,
* CP(CR5), acting as host, checks the pmic restart cmd register to process
* different bootmode and then set it to the right value.
*
* Since the pmic cmd and status are mapped to the same pmic register, cmd
* is used instead of status.
*/
pmic_set_restart_cmd(RESTART_STATUS_PRODUCTION);
#else
/*
* For Watch1 MCU.
*
* The production mode flag will be passed to the Boot33 by the property of
* bl2.bootmode, and the subsequent software will never use pmic register
* to determine the boot mode.
*/
#endif /* BOARD_CRANE */
sys_boot_mode_set(SYS_BOOT_MODE_PRODUCTION);
if(timer_started) {
etimer_stop(&timer);
}
if(!do_boot()) {
timer_started = false;
} else {
set_preamble_mode();
}
break;
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
static void
crane_boot2_init(void)
{
process_start(&boot2_process, NULL);
}
/*---------------------------------------------------------------------------*/
static void
transfer_arom_do_download(void)
{
/* unregister preamble callback first */
smux_register_preamble_callback(NULL);
process_post_synch(arom_main_process(), PREAMBLE_DOWNLOAD, NULL);
process_exit(&boot2_process);
#if (defined(BOARD_CRANE))
#if AROM_VERSION <= AROM_VER_2021_10_16_CRANEL_A0
size_t len = strlen(boardid_string);
char *p = arom_heapmem_alloc(len + 1); /* must use arom heap */
strcpy(p, boardid_string);
fastboot_publish("boardid", p);
#endif
#endif
}
/*---------------------------------------------------------------------------*/
static void
boot2_preamble_callback(const char *preamble, smux_dev_t dev)
{
static enum { PREAMBLE_TYPE_UNKNOWN,
PREAMBLE_TYPE_UUUU,
PREAMBLE_TYPE_UABT,
PREAMBLE_TYPE_PROD } preamble_type;
if(!strncmp(preamble, SMUX_PREAMBLE_UUUU, SMUX_PREAMBLE_SIZE)) {
if(preamble_type != PREAMBLE_TYPE_UUUU) {
preamble_type = PREAMBLE_TYPE_UUUU;
}
} else if(!strncmp(preamble, SMUX_PREAMBLE_UABT, SMUX_PREAMBLE_SIZE)) {
if(preamble_type != PREAMBLE_TYPE_UABT) {
preamble_type = PREAMBLE_TYPE_UABT;
}
} else if(!strncmp(preamble, SMUX_PREAMBLE_PROD, SMUX_PREAMBLE_SIZE)) {
if(preamble_type != PREAMBLE_TYPE_PROD) {
preamble_type = PREAMBLE_TYPE_PROD;
}
} else {
preamble_type = PREAMBLE_TYPE_UNKNOWN;
return;
}
switch(preamble_type) {
case PREAMBLE_TYPE_UUUU:
smux_use_device(dev);
smux_write_stdout((uint8_t *)SMUX_PREAMBLE_UABT, SMUX_PREAMBLE_SIZE);
process_post(&boot2_process, PREAMBLE_DOWNLOAD, NULL);
break;
case PREAMBLE_TYPE_UABT:
smux_use_device(dev);
/* handle download by bootrom */
transfer_arom_do_download();
smux_write_stdout((uint8_t *)SMUX_PREAMBLE_UABT, SMUX_PREAMBLE_SIZE);
break;
case PREAMBLE_TYPE_PROD:
smux_use_device(dev);
process_post(&boot2_process, PREAMBLE_PRODUCTION, NULL);
break;
default:
break;
}
}
/*---------------------------------------------------------------------------*/
/* if ptable has pz1 img, boot pz1 */
static int
do_boot_pz1(void)
{
struct ptentry *entry = ptable_find_entry("pz1");
/* if found 'pz1' partition boot to 'pz1' partition's xip address */
if(entry) {
LOG_INFO("Found pz1 partition, just load and boot to %p\n",
(void *)(uintptr_t)entry->vstart);
if(entry->vstart == (uintptr_t)&__lzma_decode_start) {
if(sb_flash_read(entry->start, &__lzma_decode_start, entry->size) != (int)entry->size) {
return -1;
}
}
boot_xip_address(entry->vstart);
/* never return */
}
return 0;
}
#endif
/*---------------------------------------------------------------------------*/
#if QSPI_PROBE_IN_BOOT2_ENABLE
static void
flash_probe_in_boot2(void)
{
LOG_PRINT("flash_probe_in_boot2 enter!\n");
extern char __lzma_alloc_base, __lzma_alloc_size;
uint8_t *stash = (uint8_t *)((uintptr_t)&__lzma_alloc_base + SZALLOC_OFFSET);
ssize_t stash_size = (uintptr_t)&__lzma_alloc_size - SZALLOC_OFFSET;
stash_size = sb_volume_read("flashinfo", stash, stash_size);
if(stash_size < 0) {
LOG_ERR("Read volume 'flashinfo' failed!\n");
return;
}
LOG_PRINT("flash_info_size = %u!\n", stash_size);
if(flash_info_init(stash, stash_size) == 0) {
flash_force_all_probe();
}
}
#endif
/*---------------------------------------------------------------------------*/
#if USE_ARB
/*
* NV Counters layout:
* ___________________________________________________________________________
* | init_value | counter | swap counter| init_value | counter | swap counter|
* |____________|_________|_____________|____________|_________|_____________|
* | page 0 | page 1 | page 0 | page 1 |
* |______________________|_____________|______________________|_____________|
* | primary sector 0 | backup sector 1 |
* |____________________________________|____________________________________|
*
* The nv counter partition should be on the same flash devive as the
* Bootloader because Boot2 reads flash through the sb_flash_read()
* syscall which can only read the device where the Bootloader is located.
*
* This function must be called after the pattition table initialization.
*/
static int
display_plat_nvctr(void)
{
struct ptentry *pt_nv = ptable_find_entry(NV_CTR_PART_NAME);
if(!pt_nv) {
/*
* In some layouts, such as eMMC, the nv counter partition is in
* the private partition.
*/
pt_nv = ptable_find_entry(PRIVATE_PART_NAME);
if(!pt_nv) {
LOG_INFO("No nv counter partition\n");
return 0;
}
}
/*
* Read 512 bytes from nv counter partition to memory because:
* 1. sb_flash_read() dose not support to access not page(512 bytes) aligned
* data with eMMC.
* 2. 512 bytes is enough for get nv counter.
*/
extern char __lzma_alloc_base;
void *stash = (void *)((uintptr_t)&__lzma_alloc_base + SZALLOC_OFFSET);
const size_t size = 512;
if(sb_flash_read(pt_nv->start, stash, size) != size) {
LOG_ERR("Read nv counter failed!\n");
return -1;
}
flash_otp_nv_ctr_region_t *nv_ctr = stash;
if(nv_ctr->init_value != OTP_NV_COUNTERS_INITIALIZED) {
LOG_ERR("NV counter is invalid!\n");
return -1;
}
LOG_INFO("Platform nv counter: %lu\n",
nv_ctr->nv_counters[CHECK_NV_CTR_INDEX].nv_ctr);
return 0;
}
#endif
/*---------------------------------------------------------------------------*/
int
main(void)
{
LOG_PRINT("Executing boot2 application...\n");
LOG_PRINT("Boot2 version: %s\n", BOOTLOADER_VERSION);
#ifdef USE_ARB
enable_flasher_guard();
#endif
if(arom_production_mode_get() == 1) {
/* set production mode flag */
#if defined(BOARD_CRANE) && AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
/*
* For Crane chips except for Watch 1 CR5.
*
* Set the pmic restart cmd to "production mode" to ensure compatiblity
* with already mass-producrd products. On those chips, CP(CR5), acting as
* host, checks the pmic restart cmd register to process different
* bootmode and then set it to the right value.
*
* Since the cmd and status are mapped to the same pmic register, cmd is
* used instead of status.
*/
pmic_set_restart_cmd(RESTART_STATUS_PRODUCTION);
#else
/*
* For Watch1 MCU and Watch1 CR5.
*
* The production mode flag will be passed to the Boot33 by the property
* of bl2.bootmode, and the subsequent software will use never pmic
* register to determine the boot mode.
*/
#endif /* defined(BOARD_CRANE) && AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5 */
sys_boot_mode_set(SYS_BOOT_MODE_PRODUCTION);
}
sys_boot_mode_t boot_mode = sys_boot_mode_get();
if(ptable_loader()) {
LOG_ERR("Load ptable failed!\n");
return -1;
}
#if USE_ARB
/* This function must be called after the pattition table initialization. */
display_plat_nvctr();
#endif
#if QSPI_PROBE_IN_BOOT2_ENABLE
flash_probe_in_boot2();
#endif
#if AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
#if (defined(BOARD_CRANE) || defined(BOARD_CRANEW_MCU))
boardid_string = volume_read_boardid();
asr_property_set(PROP_NAME_BOARDID, boardid_string);
LOG_INFO("BOARD_ID: %s\n", boardid_string);
#endif
#endif
/*
* can not know whether usb-vbus is connected or not,
* try download mode only support usb-vbus connected case,
* if usb-vbus not connected, customer use at+qdownload = 1 to enter force download mode
*/
#if AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
if(!usb_connect()) {
LOG_WARN("usb is not connected\n");
if(boot_mode == SYS_BOOT_MODE_TRY_DOWNLOAD) {
#if DISABLE_UART_TRY_DOWNLOAD
boot_mode = SYS_BOOT_MODE_NORMAL;
#endif
}
}
#endif
switch(boot_mode) {
case SYS_BOOT_MODE_NORMAL:
LOG_INFO("Running in normal boot mode.\n");
return do_boot();
case SYS_BOOT_MODE_PRODUCTION:
LOG_INFO("Running in production mode.\n");
return do_boot();
#if AROM_VERSION != AROM_VER_2022_08_08_CRANEW_CR5
case SYS_BOOT_MODE_TRY_DOWNLOAD:
LOG_INFO("Running in try downloading mode.\n");
crane_boot2_init();
return 0;
#endif
case SYS_BOOT_MODE_FORCE_DOWNLOAD:
LOG_INFO("Running in force downloading mode.\n");
#if AROM_VERSION == AROM_VER_2020_07_30_CRANEGM_A0
/* only for AROM_VER_2020_07_30_CRANEGM_A0 */
/* an patch for arom-crane 0730
* fix bug for AT fall back invalid.
* arom-crane miss judge ret from boot2, so use negative value.
*/
return -SYS_BOOT_MODE_FORCE_DOWNLOAD;
#else
return SYS_BOOT_MODE_FORCE_DOWNLOAD;
#endif
default:
break;
}
return -1;
}