blob: 2d3052d255754929771616266eaa8900dd1a8558 [file] [log] [blame]
/*
* Copyright (c) 2018 The Fuchsia Authors
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <aml_i2c.h>
#include <asm/arch/bl31_apis.h>
#include <asm/arch/efuse.h>
#include <asm/arch/secure_apb.h>
#include <asm/reboot.h>
#include <command.h>
#include <common.h>
#include <storage.h>
#include <version.h>
#include <wdt.h>
#include <zbi/zbi.h>
#include <zircon/boot/bootfs.h>
#include <zircon/boot/image.h>
#include <zircon-estelle/fw-testing.h>
#include <zircon-estelle/partition.h>
#include <zircon-estelle/vboot.h>
#include <zircon-estelle/zircon.h>
// for reading factory partition
#include <../../../fs/ubifs/ubifs.h>
// bitmask for determining whether the RNG_USR_DATA register is ready.
// This mask should be applied to the RNG_USR_STS register.
// 0: The RNG_USR_DATA register is not ready to be read from.
// 1: The RNG_USR_DATA register is ready to be read from.
#define USR_RAND32_RDY 0x1
#define PDEV_VID_GOOGLE 3
#define PDEV_PID_ASTRO 3
#define NVRAM_LENGTH (8 * 1024)
// Size of the CMDLINE entropy string.
#define CMDLINE_ENTROPY_SIZE 1024
// Random bits to pass to zircon.
#define CMDLINE_ENTROPY_BITS 256
// Deadline for time waiting for the RNG_USR_STS register to be ready.
// This is a very generous value, given that after reading from
// the hw rng, we should expect it to be available after
// HW_RNG_RESEEDING_INTERVAL_MICROS.
#define ENTROPY_COLLECTION_DEADLINE_MICROS 100000
// HW RNG is reseeded every 40 microseconds.
#define HW_RNG_RESEEDING_INTERVAL_MICROS 40
#define ENTROPY_BITS_PER_CHAR 4
#ifdef DEBUG
#define debugP(fmt...) printf("[Dbg %s]L%d:", __func__, __LINE__), printf(fmt)
#else
#define debugP(fmt...)
#endif
static const char zircon_entropy_arg[] = "kernel.entropy-mixin=";
static char entropy_cmdline[CMDLINE_ENTROPY_SIZE] = { 0 };
_Static_assert(CMDLINE_ENTROPY_BITS % 32 == 0,
"Requested entropy must be a multiple of 32");
_Static_assert((CMDLINE_ENTROPY_BITS / ENTROPY_BITS_PER_CHAR) +
sizeof(zircon_entropy_arg) <
CMDLINE_ENTROPY_SIZE,
"Requested entropy doesn't fit in cmdline.");
static const zbi_cpu_config_t cpu_config = {
.cluster_count = 1,
.clusters = {
{
.cpu_count = 4,
},
},
};
static const zbi_mem_range_t mem_config[] = {
{
.type = ZBI_MEM_RANGE_RAM,
.length = 0x60000000, // 1.5 GB
},
{
.type = ZBI_MEM_RANGE_PERIPHERAL,
.paddr = 0xf5800000,
.length = 0x0a800000,
},
// secmon_reserved:linux,secmon
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x05000000,
.length = 0x2400000,
},
// logo_reserved:linux,meson-fb
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x5f800000,
.length = 0x800000,
},
};
static const dcfg_simple_t uart_driver = {
.mmio_phys = 0xff803000,
.irq = 225,
};
static const dcfg_arm_gicv2_driver_t gicv2_driver = {
.mmio_phys = 0xffc00000,
.gicd_offset = 0x1000,
.gicc_offset = 0x2000,
.gich_offset = 0x4000,
.gicv_offset = 0x6000,
.ipi_base = 5,
};
static const dcfg_arm_psci_driver_t psci_driver = {
.use_hvc = false,
.reboot_args = { 1, 0, 0 },
.reboot_bootloader_args = { 4, 0, 0 },
.reboot_recovery_args = { 2, 0, 0 },
};
static const dcfg_arm_generic_timer_driver_t timer_driver = {
.irq_phys = 30,
};
static const dcfg_amlogic_rng_driver_t rng_driver = {
.rng_data_phys = (uint64_t)P_RNG_USR_DATA,
.rng_status_phys = (uint64_t)P_RNG_USR_STS,
.rng_refresh_interval_usec = HW_RNG_RESEEDING_INTERVAL_MICROS,
};
#define WDT_CTRL 0xffd0f0d0
#define WDT_PET 0xffd0f0dc
#define WATCHDOG_TIMEOUT_SECONDS 5
#define SECONDS_TO_NANOSECONDS 1000000000LL
#ifdef CONFIG_FIRMWARE_TESTING_DEV_BUILD
static dcfg_generic_32bit_watchdog_t watchdog_driver = {
#else
static const dcfg_generic_32bit_watchdog_t watchdog_driver = {
#endif
.pet_action = {
.addr = WDT_PET,
.clr_mask = 0xffffffff,
.set_mask = 0x00000000,
},
.enable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00000000,
.set_mask = 0x00040000,
},
.disable_action = {
.addr = WDT_CTRL,
.clr_mask = 0x00040000,
.set_mask = 0x00000000,
},
.watchdog_period_nsec =
WATCHDOG_TIMEOUT_SECONDS * SECONDS_TO_NANOSECONDS,
.flags = KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED,
};
#ifdef CONFIG_FIRMWARE_TESTING_DEV_BUILD
/**
* disable_watchdog_petting() - disable petting of watchdog
*
* The function disables the petting function of watchdog driver by
* faking a pet_action.addr and setting the set_mask anc clr_mask
* field to be 0.
*
* It is mainly used for testing watchdog after booting into zircon.
* i.e., whether it triggers a reboot if zircon is not able to pet it in time
*/
void disable_watchdog_petting(void)
{
watchdog_driver.pet_action.addr = watchdog_driver.enable_action.addr;
watchdog_driver.pet_action.set_mask = 0; // no set
watchdog_driver.pet_action.clr_mask = 0; // no clear
}
#endif
static const zbi_platform_id_t platform_id = {
.vid = PDEV_VID_GOOGLE,
.pid = PDEV_PID_ASTRO,
.board_name = "astro",
};
// Factory file list. Only files listed here will be supplied to Zircon as metadata.
static const char *const factory_file_list[] = {
"amp_calibration.txt",
"bl_calibration.txt",
"checksum.sha1",
"client.crt",
"client.key",
"clm_bcmdhd.blob",
"corner_BL.bmp",
"corner_BR.bmp",
"corner_TL.bmp",
"corner_TR.bmp",
"hw.txt",
"locale_list.txt",
"logo.bmp",
"mac_addr",
"mlb_sn.txt",
"nf.key",
"partitions.sha1",
"pr3.crt",
"pr3.key",
"rgb_b_led_cal.txt",
"rgb_b_off_cal.txt",
"rgb_b_on_cal.txt",
"rgb_c_led_cal.txt",
"rgb_c_off_cal.txt",
"rgb_c_on_cal.txt",
"rgb_g_led_cal.txt",
"rgb_g_off_cal.txt",
"rgb_g_on_cal.txt",
"rgb_r_led_cal.txt",
"rgb_r_off_cal.txt",
"rgb_r_on_cal.txt",
"serial.txt",
"sounds/checksum.sha1",
"sounds/err_no_lang_pack.opus",
"sounds/error.opus",
"sounds/fdr.opus",
"sounds/meta.txt",
"sounds/mic_muted_warning.opus",
"sounds/mute.opus",
"sounds/unmute.opus",
"sounds/welcome.opus",
"ultrasound.txt",
"weave.crt",
"weave.key",
"weave_device_id",
"weave_pairing_code",
"wip_sn.txt",
"wv.key",
};
static void *mandatory_memset(void *dst, int c, size_t n)
{
volatile unsigned char *out = dst;
size_t i = 0;
for (i = 0; i < n; ++i) {
out[i] = (unsigned char)c;
}
return dst;
}
bool zircon_is_vboot_enabled(void)
{
/* Anything other than our specific UNLOCKED value means vboot is on. */
return bl31_vx_get_bootloader_lock_state() !=
VX_BOOTLOADER_LOCK_STATE_UNLOCKED;
}
int zircon_vboot_unlock(void)
{
/* These log strings are load-bearing in the firmware test suite, make
* sure to keep them in sync if you change them here. */
if (bl31_vx_unlock_bootloader() != 0) {
printf("Error: failed to unlock the bootloader\n");
return -1;
}
printf("Bootloader unlock success\n");
return 0;
}
int zircon_exit_bootloader(void)
{
/* These log strings are load-bearing in the firmware test suite, make
* sure to keep them in sync if you change them here. */
if (bl31_vx_exit_bootloader() != 0) {
printf("Error: failed to exit bootloader\n");
return -1;
}
printf("Bootloader exit success\n");
return 0;
}
/* Helper to add a ZBI item. Returns 0 on success, -1 and logs on failure.*/
int append_zbi_item_or_log(void *zbi, size_t capacity, uint32_t type,
uint32_t extra, const void *payload, size_t size)
{
if (size > 0xFFFFFFFFU) {
printf("Error: ZBI item 0x%08X/0x%X is too large (%zu bytes)\n",
type, extra, size);
return -1;
}
zbi_result_t result = zbi_create_entry_with_payload(
zbi, capacity, type, extra, 0 /*flags*/, payload, size);
if (result != ZBI_RESULT_OK) {
printf("Error: Failed to add ZBI item 0x%08X/0x%X (code %d)\n",
type, extra, result);
return -1;
}
return 0;
}
static int add_partition_map(zbi_header_t *zbi, size_t capacity)
{
const zbi_partition_map_t *partition_map = zircon_get_partition_map();
if (partition_map == NULL) {
printf("WARNING: zircon partition map is not defined.\n");
return -1;
}
return append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_DRV_PARTITION_MAP, 0, partition_map,
sizeof(zbi_partition_map_t) + partition_map->partition_count *
sizeof(zbi_partition_t));
}
// I wish u-boot had sscanf()...
static int hex_to_int(char ch)
{
if (ch >= '0' && ch <= '9') {
return ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
return ch - 'a' + 0xA;
} else if (ch >= 'A' && ch <= 'F') {
return ch - 'A' + 0xA;
} else {
return -1;
}
}
static int add_mac_address(zbi_header_t *zbi, size_t capacity, uint32_t index,
const char *str)
{
uint8_t mac_addr[6];
int i;
for (i = 0; i < 6; i++) {
int hi = hex_to_int(*str++);
int lo = hex_to_int(*str++);
if (hi < 0 || lo < 0) {
return -1;
}
mac_addr[i] = ((hi << 4) | lo);
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DRV_MAC_ADDRESS,
index, mac_addr, sizeof(mac_addr));
}
static int add_mac_addresses(zbi_header_t *zbi, size_t capacity)
{
char buffer[100];
int ret;
memset(buffer, 0, sizeof(buffer));
ret = ubifs_load("mac_addr", (u32)(uintptr_t)buffer, sizeof(buffer));
if (ret < 0) {
printf("ERROR: ubifs_load() failed\n");
return ret;
}
// "buffer" should now contain two hex strings separated by \n, for a total of 25 bytes.
// Unfortunately we have no sscanf() to use here, so we parse the hex strings manually.
ret = add_mac_address(zbi, capacity, 0, buffer);
if (ret < 0) {
printf("ERROR: could not add MAC address from %s\n", buffer);
return ret;
}
ret = add_mac_address(zbi, capacity, 1, buffer + 13);
if (ret < 0) {
printf("ERROR: could not add MAC address from %s\n",
buffer + 13);
return ret;
}
return 0;
}
static int add_serial_number(zbi_header_t *zbi, size_t capacity)
{
const char *s = getenv("serial");
if (!s || (*s == '\0')) {
printf("Failed to retrieve serial number\n");
return -1;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_SERIAL_NUMBER, 0,
s, strlen(s));
}
static int add_board_info(zbi_header_t *zbi, size_t capacity)
{
zbi_board_info_t board_info = {};
char *s;
if (((s = getenv("hw_id")) != NULL) && (*s != '\0')) {
uint32_t hw_id = simple_strtoul(s, NULL, 16);
board_info.revision = hw_id;
} else {
printf("Failed to retrieve Board Revision\n");
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_DRV_BOARD_INFO, 0,
&board_info, sizeof(board_info));
}
// fills an 8 char buffer with the lowercase hex representation of the given
// value.
// WARNING this does not add a '\0' to the end of the buffer.
static inline void uint32_to_hex(uint32_t val, char buf[static 8])
{
static const char hex[] = "0123456789abcdef";
int i = 0;
for (i = 7; i >= 0; i--) {
buf[i] = hex[val & 0xF];
val >>= 4;
}
}
// Reads a value from the userspace hardware random number generator.
//
// This assumes that the drng has been previously seeded. Callers should
// poll P_RNG_USR_STS beforehand to make sure that a reseed occurred.
static inline uint32_t read_hw_rng(void)
{
return readl(P_RNG_USR_DATA);
}
// Gathers CMDLINE_ENTROPY_BITS bits of entropy from the hardware random
// number generator and appends it as a ZBI_TYPE_CMDLINE for zircon to seed
// its cprng.
//
// This function will sleep a maximum of HW_RNG_RESEEDING_INTERVAL_MICROS *
// (CMDLINE_ENTROPY_BITS / 32) + ENTROPY_COLLECTION_DEADLINE_MICROS.
// If the function can't gather enough entropy after after exceeding
// the deadline, it will return -1 and the cmdline zbi will not be added.
static int add_cmdline_entropy(zbi_header_t *zbi, size_t capacity)
{
strcpy(entropy_cmdline, zircon_entropy_arg);
char *entropy = entropy_cmdline + strlen(zircon_entropy_arg);
uint32_t elapsed_time_us = 0;
int i = 0;
for (i = 0; i < CMDLINE_ENTROPY_BITS; i += 32) {
// Reading a 1 in the RNG_USR_STS means that the
// hw rng has been reseeded. Wait until we see a 1,
// without exceeding the global deadline.
while ((readl(P_RNG_USR_STS) & USR_RAND32_RDY) != 1) {
udelay(1);
elapsed_time_us++;
if (elapsed_time_us >
ENTROPY_COLLECTION_DEADLINE_MICROS) {
return -1;
}
}
uint32_to_hex(read_hw_rng(), entropy);
entropy += 8;
// According to the docs, this should guarantee a reseed.
udelay(HW_RNG_RESEEDING_INTERVAL_MICROS);
}
*entropy = '\0';
int ret = append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_CMDLINE, 0,
entropy_cmdline,
sizeof(entropy_cmdline));
mandatory_memset(entropy_cmdline, '\0', sizeof(entropy_cmdline));
return ret;
}
/* Adds a single bootfs factory file to the ZBI payload.
*
* @filename: UBIFS filename.
* @data_off: offset from the payload start to this file's data.
* @max_bootfs_size: maximum payload size.
* @direntry_start: pointer to this file's zbi_bootfs_dirent_t in the payload
* buffer. Will be advanced past this entry on success,
* including padding.
* @payload_start: pointer to this file's data in the payload buffer. Will be
* advanced past this data on success, including padding.
* @data_len: will be filled with the file's data length.
*
* Returns 0 on success, nonzero on failure.
*/
static int add_factory_file(const char *filename, uint32_t data_off,
uint32_t max_bootfs_size, uint8_t **direntry_start,
uint8_t **payload_start, uint32_t *data_len)
{
const uint32_t name_len = strlen(filename) + 1;
const uint32_t max_data_size =
(max_bootfs_size - data_off) & ~(ZBI_BOOTFS_PAGE_SIZE - 1);
int ret = ubifs_load((char *)filename, (u32)(uintptr_t)*payload_start,
max_data_size);
if (ret < 0) {
printf("ERROR: ubifs_load() failed\n");
return ret;
}
// At this point, $filesize contains the actual size of the file. This
// value will not be larger than |max_data_size| due to ubifs_load()
// picking the min of |max_data_size| and the real file size.
*data_len = (uint32_t)getenv_hex("filesize", 0);
*payload_start += *data_len;
uint32_t data_pad_size = ZBI_BOOTFS_PAGE_ALIGN(*data_len) - *data_len;
memset(*payload_start, 0, data_pad_size);
*payload_start += data_pad_size;
// The caller has already reserved space for the dirent structures and
// filenames in the buffer, so we don't need to check for overflow here.
zbi_bootfs_dirent_t *entry_hdr =
(zbi_bootfs_dirent_t *)(*direntry_start);
entry_hdr->name_len = name_len;
entry_hdr->data_len = *data_len;
entry_hdr->data_off = data_off;
*direntry_start += offsetof(zbi_bootfs_dirent_t, name);
memcpy(*direntry_start, filename, name_len);
*direntry_start += name_len;
uint32_t full_dirent_size = ZBI_BOOTFS_DIRENT_SIZE(name_len);
uint32_t after_name_offset =
offsetof(zbi_bootfs_dirent_t, name[name_len]);
uint32_t dirent_pad_size = full_dirent_size - after_name_offset;
memset(*direntry_start, 0, dirent_pad_size);
*direntry_start += dirent_pad_size;
return 0;
}
/* Loads factory files into a BOOTFS ZBI payload.
*
* @bootfs_header: start of the payload buffer to fill.
* @max_bootfs_size: maximum payload size.
* @file_count: number of files in the ZBI item.
* @dirsize: size in bytes of the zbi_bootfs_dirent_t array.
* @bootfs_size: set to the actual payload size.
*
* Returns 0 on success, nonzero on failure.
*/
static int add_bootfs_factory_files(zbi_bootfs_header_t *bootfs_header,
uint32_t max_bootfs_size,
uint32_t file_count, uint32_t dirsize,
uint32_t *bootfs_size)
{
if (sizeof(*bootfs_header) > max_bootfs_size) {
printf("ERROR: can't fit bootfs header in ZBI payload (%zu > %u)\n",
sizeof(*bootfs_header), max_bootfs_size);
return 1;
}
bootfs_header->magic = ZBI_BOOTFS_MAGIC;
bootfs_header->dirsize = dirsize;
bootfs_header->reserved0 = 0;
bootfs_header->reserved1 = 0;
uint32_t dir_entries_end_offset = dirsize + sizeof(zbi_bootfs_header_t);
uint32_t data_off = ZBI_BOOTFS_PAGE_ALIGN(dir_entries_end_offset);
if (data_off < dirsize) {
printf("ERROR: bootfs dirsize overflow (dirsize = %u, data_off = %u)\n",
dirsize, data_off);
return 1;
}
if (data_off > max_bootfs_size) {
printf("ERROR: can't fit bootfs dir entries in ZBI payload (%u > %u)\n",
data_off, max_bootfs_size);
return 1;
}
uint8_t *header_start = (uint8_t *)bootfs_header;
uint8_t *entry_ptr = header_start + sizeof(zbi_bootfs_header_t);
uint8_t *payload_padding_start = header_start + dir_entries_end_offset;
uint8_t *payload_start = header_start + data_off;
memset(payload_padding_start, 0,
(size_t)(payload_start - payload_padding_start));
uint32_t i;
for (i = 0; i < file_count; i++) {
const char *const filename = factory_file_list[i];
uint32_t data_len = 0;
payload_padding_start = payload_start;
int ret =
add_factory_file(filename, data_off, max_bootfs_size,
&entry_ptr, &payload_start, &data_len);
if (ret != 0) {
// Log the error, but try to keep going and add the rest of the
// factory files.
printf("ERROR: failed to add factory file %s\n",
filename);
}
payload_padding_start += data_len;
data_off += ZBI_BOOTFS_PAGE_ALIGN(data_len);
memset(payload_padding_start, 0,
(size_t)(payload_start - payload_padding_start));
}
*bootfs_size = data_off;
return 0;
}
/* Adds a ZBI item containing the factory files to the given container.
*
* On Estelle, factory files are stored in a UBIFS partition. Fuchsia doesn't
* have a UBIFS driver, so we load the files from the bootloader and pass them
* as a ZBI item that Fuchsia understands.
*
* Note also that ZBI_TYPE_STORAGE_BOOTFS_FACTORY wasn't really meant to be
* ABI-stable, so the definitions aren't part of the Firmware SDK. In the future
* we may want to formalize this as a stable ABI e.g. ZBI_TYPE_ASTRO_FACTORY_FS.
*
* @zbi: ZBI container to add the factory files to.
* @capacity: ZBI container max capacity.
*
* Returns 0 on success, nonzero on failure.
*/
static int add_factory_data(zbi_header_t *zbi, size_t capacity)
{
int ret = ubifs_init();
if (ret < 0) {
printf("ERROR: ubifs_init() failed\n");
return ret;
}
ret = uboot_ubifs_mount("ubi:factory");
if (ret < 0) {
printf("ERROR: uboot_ubifs_mount() failed\n");
return ret;
}
// MAC addresses are a special case of factory files which are put in their
// own ZBI items.
ret = add_mac_addresses(zbi, capacity);
if (ret < 0) {
printf("ERROR: add_mac_addresses() failed\n");
return ret;
}
// Add an empty ZBI header for factory data. Factory data is formated as BOOTFS.
// BOOTFS is a trivial "filesystem" format.
//
// It consists of a zbi_bootfs_header_t followed by a series of zbi_bootfs_dirent_t structs.
// After the zbi_bootfs_dirent_t structs, file data is placed.
// File data offsets are page aligned (multiple of 4096).
// zbi_bootfs_dirent_t structs start on uint32 boundaries.
void *payload = NULL;
uint32_t max_payload_size = 0;
zbi_result_t result = zbi_get_next_entry_payload(
zbi, capacity, &payload, &max_payload_size);
if (result != ZBI_RESULT_OK) {
printf("ERROR: zbi_get_next_entry_payload() failed: %d\n",
result);
return 1;
}
// Determine how much space is needed to store all of the directory entries.
uint32_t dirsize = 0;
uint32_t file_count = (uint32_t)(sizeof(factory_file_list) /
sizeof(*factory_file_list));
uint32_t i;
for (i = 0; i < file_count; i++) {
const char *const filename = factory_file_list[i];
dirsize += ZBI_BOOTFS_DIRENT_SIZE(strlen(filename) + 1);
}
// Mark the start of the zbi_bootfs_header_t. This header is bootfs-specific and stores
// data related to the number of directory entries.
uint32_t bootfs_size = 0;
ret = add_bootfs_factory_files((zbi_bootfs_header_t *)payload,
max_payload_size, file_count, dirsize,
&bootfs_size);
if (ret != 0) {
printf("ERROR: add_bootfs_factory_files() failed\n");
return ret;
}
// Finally, add the ZBI item using the newly created payload.
result =
zbi_create_entry(zbi, capacity, ZBI_TYPE_STORAGE_BOOTFS_FACTORY,
0, 0, bootfs_size, NULL);
if (result != ZBI_RESULT_OK) {
printf("ERROR: zbi_create_entry() failed: %d\n", result);
return 1;
}
return 0;
}
static int add_reboot_reason(zbi_header_t *zbi, size_t capacity)
{
// See cmd/amlogic/cmd_reboot.c
const uint32_t reboot_mode_val = ((readl(AO_SEC_SD_CFG15) >> 12) & 0xf);
zbi_hw_reboot_reason_t reboot_reason;
switch (reboot_mode_val) {
case AMLOGIC_COLD_BOOT:
reboot_reason = ZBI_HW_REBOOT_COLD;
break;
case AMLOGIC_NORMAL_BOOT:
case AMLOGIC_FACTORY_RESET_REBOOT:
case AMLOGIC_UPDATE_REBOOT:
case AMLOGIC_FASTBOOT_REBOOT:
case AMLOGIC_SUSPEND_REBOOT:
case AMLOGIC_HIBERNATE_REBOOT:
case AMLOGIC_BOOTLOADER_REBOOT:
case AMLOGIC_SHUTDOWN_REBOOT:
case AMLOGIC_RPMBP_REBOOT:
case AMLOGIC_FACTORY_BOOT:
case AMLOGIC_CRASH_REBOOT:
case AMLOGIC_KERNEL_PANIC:
reboot_reason = ZBI_HW_REBOOT_WARM;
break;
case AMLOGIC_WATCHDOG_REBOOT:
reboot_reason = ZBI_HW_REBOOT_WATCHDOG;
break;
default:
reboot_reason = ZBI_HW_REBOOT_UNDEFINED;
break;
}
return append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_HW_REBOOT_REASON,
0, &reboot_reason, sizeof(reboot_reason));
}
// Buffer to keep staged ZBI files.
// We store these in their own ZBI container, which takes up a little extra
// space due to ZBI headers, but makes copying them over to the actual ZBI
// trivial.
//
// The test team needs to be able to put up to 3 SSH keys on a single device
// so make sure it's at least big enough for that.
static uint8_t zbi_files[4096] __attribute__((aligned(ZBI_ALIGNMENT)));
static bool zbi_files_initialized = false;
int zircon_stage_zbi_file(const char *name, const uint8_t *data,
size_t data_len)
{
size_t name_len = strlen(name);
if (name_len > U8_MAX) {
printf("ZBI filename too long");
return -1;
}
// Payload = (name_length_byte + name + data), size must fit in a uint32_t.
size_t payload_length = 1 + name_len + data_len;
if (payload_length > U32_MAX || payload_length < data_len) {
printf("ZBI file data too large");
return -1;
}
if (!zbi_files_initialized) {
zbi_result_t result = zbi_init(zbi_files, sizeof(zbi_files));
if (result != ZBI_RESULT_OK) {
printf("Failed to initialize zbi_files: %d\n", result);
return -1;
}
zbi_files_initialized = true;
}
uint8_t *payload = NULL;
zbi_result_t result =
zbi_create_entry(zbi_files, sizeof(zbi_files),
ZBI_TYPE_BOOTLOADER_FILE, 0, 0, payload_length,
(void **)&payload);
if (result != ZBI_RESULT_OK) {
printf("Failed to create ZBI file entry: %d\n", result);
return -1;
}
payload[0] = name_len;
memcpy(&payload[1], name, name_len);
memcpy(&payload[1 + name_len], data, data_len);
return 0;
}
static int add_staged_zbi_files(zbi_header_t *zbi, size_t capacity)
{
if (!zbi_files_initialized) {
return 0;
}
zbi_result_t result = zbi_extend(zbi, capacity, zbi_files);
if (result != ZBI_RESULT_OK) {
printf("Failed to add staged ZBI files: %d\n", result);
return -1;
}
return 0;
}
#ifdef CONFIG_WDT
static void start_watchdog(void)
{
printf("starting watchdog\n");
wdt_init();
wdt_start(WATCHDOG_TIMEOUT_SECONDS);
wdt_reset();
}
#endif
/* We do this a lot in zircon_preboot(), a macro helps with boilerplate.
* Generally it's best to fail loudly if we can't add a ZBI item; we want to
* know immediately if ZBI items are missing, not later when random things
* stop working in the OS. */
#define RETURN_IF_NONZERO(val) \
do { \
int ret = (val); \
if (ret != 0) { \
return ret; \
} \
} while (0)
int zircon_fixup_zbi(zbi_header_t *zbi, size_t capacity)
{
// add CPU configuration
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_CPU_CONFIG, 0, &cpu_config,
sizeof(zbi_cpu_config_t) +
sizeof(zbi_cpu_cluster_t) * cpu_config.cluster_count));
// allocate crashlog save area before 0x5f800000-0x60000000 reserved area
zbi_nvram_t nvram;
nvram.base = 0x5f800000 - NVRAM_LENGTH;
nvram.length = NVRAM_LENGTH;
RETURN_IF_NONZERO(append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_NVRAM,
0, &nvram, sizeof(nvram)));
// add memory configuration
RETURN_IF_NONZERO(
append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_MEM_CONFIG, 0,
&mem_config, sizeof(mem_config)));
// add kernel drivers
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_UART,
&uart_driver, sizeof(uart_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2,
&gicv2_driver, sizeof(gicv2_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_PSCI,
&psci_driver, sizeof(psci_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GENERIC_TIMER,
&timer_driver, sizeof(timer_driver)));
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_RNG,
&rng_driver, sizeof(rng_driver)));
char uboot_ver[] = "bootloader.name=" U_BOOT_VERSION_STRING;
// Zircon's cmdline parameters cannot contain spaces so
// convert spaces in autogenerated U-boot version string
// to underscores.
// See zircon/docs/kernel_cmdline.md
int i;
int len = strlen(uboot_ver);
for (i = 0; i < len; i++) {
if (uboot_ver[i] == ' ') {
uboot_ver[i] = '_';
}
}
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_CMDLINE, 0, uboot_ver, len + 1));
// Non-fatal if for some reason this fails, just continue.
add_serial_number(zbi, capacity);
RETURN_IF_NONZERO(add_board_info(zbi, capacity));
// add platform ID
RETURN_IF_NONZERO(
append_zbi_item_or_log(zbi, capacity, ZBI_TYPE_PLATFORM_ID, 0,
&platform_id, sizeof(platform_id)));
RETURN_IF_NONZERO(add_partition_map(zbi, capacity));
RETURN_IF_NONZERO(add_cmdline_entropy(zbi, capacity));
RETURN_IF_NONZERO(add_factory_data(zbi, capacity));
#ifdef CONFIG_ABR_WEAR_LEVELING
// The following option enables abr wear-leveling technique.
const char abr_wear_leveling_opt[] =
"astro.sysconfig.abr-wear-leveling=1";
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_CMDLINE, 0, abr_wear_leveling_opt,
sizeof(abr_wear_leveling_opt)));
#endif
RETURN_IF_NONZERO(add_reboot_reason(zbi, capacity));
RETURN_IF_NONZERO(add_staged_zbi_files(zbi, capacity));
#if defined(CONFIG_ZIRCON_VBOOT)
RETURN_IF_NONZERO(zircon_vboot_add_extra_zbi_items(zbi, capacity));
#endif
#ifdef CONFIG_FIRMWARE_TESTING_DEV_BUILD
RETURN_IF_NONZERO(zircon_fw_testing_add_extra_zbi_items(zbi, capacity));
#endif
#ifdef CONFIG_WDT
RETURN_IF_NONZERO(append_zbi_item_or_log(
zbi, capacity, ZBI_TYPE_KERNEL_DRIVER,
KDRV_GENERIC_32BIT_WATCHDOG, &watchdog_driver,
sizeof(watchdog_driver)));
if (watchdog_driver.flags & KDRV_GENERIC_32BIT_WATCHDOG_FLAG_ENABLED) {
start_watchdog();
}
#endif
return 0;
}